lexgui 0.6.12 → 0.7.1

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 +500 -226
  11. package/build/lexgui.js +1562 -772
  12. package/build/lexgui.min.css +8 -2
  13. package/build/lexgui.min.js +1 -1
  14. package/build/lexgui.module.js +1563 -773
  15. package/build/lexgui.module.min.js +1 -1
  16. package/changelog.md +81 -21
  17. package/demo.js +45 -31
  18. package/examples/{all_widgets.html → all-components.html} +22 -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.1",
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,19 @@ 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 refElement = trigger ?? this.reference;
766
+ const nestedDialog = refElement.closest( "dialog" );
767
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
768
+ {
769
+ this._parent = nestedDialog;
770
+ }
771
+ else
772
+ {
773
+ this._parent = LX.root;
774
+ }
775
+
776
+ this._parent.appendChild( this.root );
748
777
 
749
778
  this.root.addEventListener( "keydown", (e) => {
750
779
  if( e.key == "Escape" )
@@ -866,6 +895,13 @@ class Popover {
866
895
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
867
896
  }
868
897
 
898
+ if( this._parent instanceof HTMLDialogElement )
899
+ {
900
+ let parentRect = this._parent.getBoundingClientRect();
901
+ position[ 0 ] -= parentRect.x;
902
+ position[ 1 ] -= parentRect.y;
903
+ }
904
+
869
905
  this.root.style.left = `${ position[ 0 ] }px`;
870
906
  this.root.style.top = `${ position[ 1 ] }px`;
871
907
  }
@@ -939,7 +975,7 @@ class Sheet {
939
975
  this.root.tabIndex = "1";
940
976
  this.root.role = "dialog";
941
977
  this.root.className = "lexsheet fixed z-1000 bg-primary";
942
- LX.root.appendChild( this.root );
978
+ document.body.appendChild( this.root );
943
979
 
944
980
  this.root.addEventListener( "keydown", (e) => {
945
981
  if( e.key == "Escape" )
@@ -980,17 +1016,21 @@ class Sheet {
980
1016
  this.root.style.height = "100%";
981
1017
  break;
982
1018
  case "top":
1019
+ this.root.style.left = 0;
983
1020
  this.root.style.top = 0;
984
1021
  this.root.style.width = "100%";
985
1022
  this.root.style.height = size;
986
1023
  break;
987
1024
  case "bottom":
1025
+ this.root.style.left = 0;
988
1026
  this.root.style.bottom = 0;
989
1027
  this.root.style.width = "100%";
990
1028
  this.root.style.height = size;
991
1029
  break;
992
1030
  }
993
1031
 
1032
+ document.documentElement.setAttribute( "data-scale", `sheet-${ this.side }` );
1033
+
994
1034
  this.root.focus();
995
1035
 
996
1036
  this._onClick = e => {
@@ -1008,6 +1048,8 @@ class Sheet {
1008
1048
 
1009
1049
  destroy() {
1010
1050
 
1051
+ document.documentElement.setAttribute( "data-scale", "" );
1052
+
1011
1053
  document.body.removeEventListener( "mousedown", this._onClick, true );
1012
1054
  document.body.removeEventListener( "focusin", this._onClick, true );
1013
1055
 
@@ -1057,7 +1099,18 @@ class DropdownMenu {
1057
1099
  this.root.dataset["side"] = this.side;
1058
1100
  this.root.tabIndex = "1";
1059
1101
  this.root.className = "lexdropdownmenu";
1060
- LX.root.appendChild( this.root );
1102
+
1103
+ const nestedDialog = trigger.closest( "dialog" );
1104
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
1105
+ {
1106
+ this._parent = nestedDialog;
1107
+ }
1108
+ else
1109
+ {
1110
+ this._parent = LX.root;
1111
+ }
1112
+
1113
+ this._parent.appendChild( this.root );
1061
1114
 
1062
1115
  this._create( this._items );
1063
1116
 
@@ -1093,7 +1146,7 @@ class DropdownMenu {
1093
1146
  document.body.removeEventListener( "mousedown", this._onClick, true );
1094
1147
  document.body.removeEventListener( "focusin", this._onClick, true );
1095
1148
 
1096
- LX.root.querySelectorAll( ".lexdropdownmenu" ).forEach( m => { m.remove(); } );
1149
+ this._parent.querySelectorAll( ".lexdropdownmenu" ).forEach( m => { m.remove(); } );
1097
1150
 
1098
1151
  DropdownMenu.currentMenu = null;
1099
1152
 
@@ -1118,14 +1171,22 @@ class DropdownMenu {
1118
1171
  newParent.className = "lexdropdownmenu";
1119
1172
  newParent.dataset["id"] = parentDom.dataset["id"];
1120
1173
  newParent.dataset["side"] = "right"; // submenus always come from the right
1121
- LX.root.appendChild( newParent );
1174
+ this._parent.appendChild( newParent );
1122
1175
 
1123
1176
  newParent.currentParent = parentDom;
1124
1177
  parentDom = newParent;
1125
1178
 
1126
1179
  LX.doAsync( () => {
1180
+
1127
1181
  const position = [ parentRect.x + parentRect.width, parentRect.y ];
1128
1182
 
1183
+ if( this._parent instanceof HTMLDialogElement )
1184
+ {
1185
+ let rootParentRect = this._parent.getBoundingClientRect();
1186
+ position[ 0 ] -= rootParentRect.x;
1187
+ position[ 1 ] -= rootParentRect.y;
1188
+ }
1189
+
1129
1190
  if( this.avoidCollisions )
1130
1191
  {
1131
1192
  position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - newParent.offsetWidth - this._windowPadding );
@@ -1141,139 +1202,180 @@ class DropdownMenu {
1141
1202
 
1142
1203
  for( let item of items )
1143
1204
  {
1144
- if( !item )
1145
- {
1146
- this._addSeparator( parentDom );
1147
- continue;
1148
- }
1205
+ this._createItem( item, parentDom, applyIconPadding );
1206
+ }
1207
+ }
1149
1208
 
1150
- const key = item.name ?? item;
1151
- const pKey = LX.getSupportedDOMName( key );
1209
+ _createItem( item, parentDom, applyIconPadding ) {
1152
1210
 
1153
- // Item already created
1154
- if( parentDom.querySelector( "#" + pKey ) )
1155
- {
1156
- continue;
1157
- }
1211
+ if( !item )
1212
+ {
1213
+ this._addSeparator( parentDom );
1214
+ return;
1215
+ }
1158
1216
 
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 );
1217
+ const key = item.name ?? item;
1218
+ const pKey = LX.getSupportedDOMName( key );
1165
1219
 
1166
- if( item.constructor === String ) // Label case
1167
- {
1168
- continue;
1169
- }
1220
+ // Item already created
1221
+ if( parentDom.querySelector( "#" + pKey ) )
1222
+ {
1223
+ return;
1224
+ }
1170
1225
 
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 );
1226
+ const menuItem = document.createElement('div');
1227
+ menuItem.className = "lexdropdownmenuitem" + ( item.name ? "" : " label" ) + ( item.disabled ?? false ? " disabled" : "" ) + ( ` ${ item.className ?? "" }` );
1228
+ menuItem.dataset["id"] = pKey;
1229
+ menuItem.innerHTML = `<span>${ key }</span>`;
1230
+ menuItem.tabIndex = "1";
1231
+ parentDom.appendChild( menuItem );
1179
1232
 
1180
- const kbd = LX.makeKbd( item.kbd );
1181
- menuItem.appendChild( kbd );
1233
+ if( item.constructor === String ) // Label case
1234
+ {
1235
+ return;
1236
+ }
1182
1237
 
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
- }
1238
+ if( item.submenu )
1239
+ {
1240
+ const submenuIcon = LX.makeIcon( "Right", { svgClass: "sm" } );
1241
+ menuItem.appendChild( submenuIcon );
1242
+ }
1243
+ else if( item.kbd )
1244
+ {
1245
+ item.kbd = [].concat( item.kbd );
1195
1246
 
1196
- const disabled = item.disabled ?? false;
1247
+ const kbd = LX.makeKbd( item.kbd );
1248
+ menuItem.appendChild( kbd );
1197
1249
 
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
- }
1250
+ document.addEventListener( "keydown", e => {
1251
+ if( !this._trigger.ddm ) return;
1252
+ e.preventDefault();
1253
+ // Check if it's a letter or other key
1254
+ let kdbKey = item.kbd.join("");
1255
+ kdbKey = kdbKey.length == 1 ? kdbKey.toLowerCase() : kdbKey;
1256
+ if( kdbKey == e.key )
1257
+ {
1258
+ menuItem.click();
1259
+ }
1260
+ } );
1261
+ }
1207
1262
 
1208
- if( disabled )
1209
- {
1210
- continue;
1211
- }
1263
+ const disabled = item.disabled ?? false;
1212
1264
 
1213
- if( item.checked != undefined )
1265
+ if( this._radioGroup !== undefined )
1266
+ {
1267
+ if( item.name === this._radioGroup.selected )
1214
1268
  {
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
- } );
1269
+ const icon = LX.makeIcon( "Circle", { svgClass: "xxs fill-current" } );
1270
+ menuItem.prepend( icon );
1232
1271
  }
1233
- else
1234
- {
1235
- menuItem.addEventListener( "click", () => {
1236
- const f = item[ 'callback' ];
1237
- if( f )
1238
- {
1239
- f.call( this, key, menuItem );
1240
- }
1241
1272
 
1242
- this.destroy( true );
1243
- } );
1244
- }
1273
+ menuItem.setAttribute( "data-radioname", this._radioGroup.name );
1274
+ }
1275
+ else if( item.icon )
1276
+ {
1277
+ const icon = LX.makeIcon( item.icon, { svgClass: disabled ? "fg-tertiary" : item.className } );
1278
+ menuItem.prepend( icon );
1279
+ }
1280
+ else if( item.checked == undefined && applyIconPadding ) // no checkbox, no icon, apply padding if there's checkbox or icon in other items
1281
+ {
1282
+ menuItem.classList.add( "pl-8" );
1283
+ }
1245
1284
 
1246
- menuItem.addEventListener("mouseover", e => {
1285
+ if( disabled )
1286
+ {
1287
+ return;
1288
+ }
1247
1289
 
1248
- let path = menuItem.dataset["id"];
1249
- let p = parentDom;
1290
+ if( item.checked != undefined )
1291
+ {
1292
+ const checkbox = new LX.Checkbox( pKey + "_entryChecked", item.checked, (v) => {
1293
+ const f = item[ 'callback' ];
1294
+ item.checked = v;
1295
+ if( f )
1296
+ {
1297
+ f.call( this, key, v, menuItem );
1298
+ }
1299
+ }, { className: "accent" });
1300
+ const input = checkbox.root.querySelector( "input" );
1301
+ input.classList.add( "ml-auto" );
1302
+ menuItem.appendChild( input );
1303
+
1304
+ menuItem.addEventListener( "click", (e) => {
1305
+ if( e.target.type == "checkbox" ) return;
1306
+ input.checked = !input.checked;
1307
+ checkbox.set( input.checked );
1308
+ } );
1309
+ }
1310
+ else
1311
+ {
1312
+ menuItem.addEventListener( "click", () => {
1313
+ const f = item[ 'callback' ];
1314
+ if( f )
1315
+ {
1316
+ f.call( this, key, menuItem );
1317
+ }
1250
1318
 
1251
- while( p )
1319
+ const radioName = menuItem.getAttribute( "data-radioname" );
1320
+ if( radioName )
1252
1321
  {
1253
- path += "/" + p.dataset["id"];
1254
- p = p.currentParent?.parentElement;
1322
+ this._trigger[ radioName ] = key;
1255
1323
  }
1256
1324
 
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
- } );
1325
+ this.destroy( true );
1326
+ } );
1327
+ }
1328
+
1329
+ menuItem.addEventListener("mouseover", e => {
1330
+
1331
+ let path = menuItem.dataset["id"];
1332
+ let p = parentDom;
1333
+
1334
+ while( p )
1335
+ {
1336
+ path += "/" + p.dataset["id"];
1337
+ p = p.currentParent?.parentElement;
1338
+ }
1264
1339
 
1265
- if( item.submenu && this.inPlace )
1340
+ this._parent.querySelectorAll( ".lexdropdownmenu" ).forEach( m => {
1341
+ if( !path.includes( m.dataset["id"] ) )
1266
1342
  {
1267
- if( menuItem.built )
1268
- {
1269
- return;
1270
- }
1271
- menuItem.built = true;
1272
- this._create( item.submenu, menuItem );
1343
+ m.currentParent.built = false;
1344
+ m.remove();
1273
1345
  }
1346
+ } );
1274
1347
 
1275
- e.stopPropagation();
1276
- });
1348
+ if( item.submenu && this.inPlace )
1349
+ {
1350
+ if( menuItem.built )
1351
+ {
1352
+ return;
1353
+ }
1354
+ menuItem.built = true;
1355
+ this._create( item.submenu, menuItem );
1356
+ }
1357
+
1358
+ e.stopPropagation();
1359
+ });
1360
+
1361
+ if( item.options )
1362
+ {
1363
+ this._addSeparator();
1364
+
1365
+ console.assert( this._trigger[ item.name ] && "An item of the radio group must be selected!" );
1366
+ this._radioGroup = {
1367
+ name: item.name,
1368
+ selected: this._trigger[ item.name ]
1369
+ };
1370
+
1371
+ for( let o of item.options )
1372
+ {
1373
+ this._createItem( o, parentDom, applyIconPadding );
1374
+ }
1375
+
1376
+ delete this._radioGroup;
1377
+
1378
+ this._addSeparator();
1277
1379
  }
1278
1380
  }
1279
1381
 
@@ -1333,6 +1435,13 @@ class DropdownMenu {
1333
1435
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
1334
1436
  }
1335
1437
 
1438
+ if( this._parent instanceof HTMLDialogElement )
1439
+ {
1440
+ let parentRect = this._parent.getBoundingClientRect();
1441
+ position[ 0 ] -= parentRect.x;
1442
+ position[ 1 ] -= parentRect.y;
1443
+ }
1444
+
1336
1445
  this.root.style.left = `${ position[ 0 ] }px`;
1337
1446
  this.root.style.top = `${ position[ 1 ] }px`;
1338
1447
  this.inPlace = true;
@@ -1593,25 +1702,25 @@ class ColorPicker {
1593
1702
  this._updateColorValue( null, true );
1594
1703
  } ).root );
1595
1704
 
1596
- this.labelWidget = new LX.TextInput( null, "", null, { inputClass: "bg-none", fit: true, disabled: true } );
1597
- colorLabel.appendChild( this.labelWidget.root );
1705
+ this.labelComponent = new LX.TextInput( null, "", null, { inputClass: "bg-none", fit: true, disabled: true } );
1706
+ colorLabel.appendChild( this.labelComponent.root );
1598
1707
 
1599
1708
  // Copy button
1600
1709
  {
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";
1710
+ const copyButtonComponent = new LX.Button(null, "copy", async () => {
1711
+ navigator.clipboard.writeText( this.labelComponent.value() );
1712
+ copyButtonComponent.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "none";
1604
1713
 
1605
1714
  LX.doAsync( () => {
1606
- copyButtonWidget.swap( true );
1607
- copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "auto";
1715
+ copyButtonComponent.swap( true );
1716
+ copyButtonComponent.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "auto";
1608
1717
  }, 3000 );
1609
1718
 
1610
1719
  }, { swap: "Check", icon: "Copy", buttonClass: "bg-none", className: "ml-auto", title: "Copy" } );
1611
1720
 
1612
- copyButtonWidget.root.querySelector( ".swap-on svg" ).addClass( "fg-success" );
1721
+ copyButtonComponent.root.querySelector( ".swap-on svg" ).addClass( "fg-success" );
1613
1722
 
1614
- colorLabel.appendChild( copyButtonWidget.root );
1723
+ colorLabel.appendChild( copyButtonComponent.root );
1615
1724
  }
1616
1725
 
1617
1726
  this._updateColorValue( hexValue, true );
@@ -1666,25 +1775,25 @@ class ColorPicker {
1666
1775
  if( this.colorModel == "CSS" )
1667
1776
  {
1668
1777
  const { r, g, b, a } = this.currentColor.css;
1669
- this.labelWidget.set( `rgb${ this.useAlpha ? 'a' : '' }(${ r },${ g },${ b }${ this.useAlpha ? ',' + toFixed( a ) : '' })` );
1778
+ this.labelComponent.set( `rgb${ this.useAlpha ? 'a' : '' }(${ r },${ g },${ b }${ this.useAlpha ? ',' + toFixed( a ) : '' })` );
1670
1779
  }
1671
1780
  else if( this.colorModel == "Hex" )
1672
1781
  {
1673
- this.labelWidget.set( ( this.useAlpha ? this.currentColor.hex : this.currentColor.hex.substr( 0, 7 ) ).toUpperCase() );
1782
+ this.labelComponent.set( ( this.useAlpha ? this.currentColor.hex : this.currentColor.hex.substr( 0, 7 ) ).toUpperCase() );
1674
1783
  }
1675
1784
  else if( this.colorModel == "HSV" )
1676
1785
  {
1677
1786
  const { h, s, v, a } = this.currentColor.hsv;
1678
1787
  const components = [ Math.floor( h ) + 'º', Math.floor( s * 100 ) + '%', Math.floor( v * 100 ) + '%' ];
1679
1788
  if( this.useAlpha ) components.push( toFixed( a ) );
1680
- this.labelWidget.set( components.join( ' ' ) );
1789
+ this.labelComponent.set( components.join( ' ' ) );
1681
1790
  }
1682
1791
  else // RGB
1683
1792
  {
1684
1793
  const { r, g, b, a } = this.currentColor.rgb;
1685
1794
  const components = [ toFixed( r ), toFixed( g ), toFixed( b ) ];
1686
1795
  if( this.useAlpha ) components.push( toFixed( a ) );
1687
- this.labelWidget.set( components.join( ' ' ) );
1796
+ this.labelComponent.set( components.join( ' ' ) );
1688
1797
  }
1689
1798
  }
1690
1799
 
@@ -1848,8 +1957,6 @@ class Calendar {
1848
1957
  let fromRangeDate = this.range ? LX.dateFromDateString( this.range[ 0 ] ) : null;
1849
1958
  let toRangeDate = this.range ? LX.dateFromDateString( this.range[ 1 ] ) : null;
1850
1959
 
1851
- this.currentDate ? new Date( `${ this.currentDate.month }/${ this.currentDate.day }/${ this.currentDate.year }` ) : null;
1852
-
1853
1960
  for( let week = 0; week < 6; week++ )
1854
1961
  {
1855
1962
  const hrow = document.createElement( 'tr' );
@@ -2000,6 +2107,13 @@ class Calendar {
2000
2107
  this.refresh();
2001
2108
  }
2002
2109
 
2110
+ setMonth( month ) {
2111
+
2112
+ this.month = month;
2113
+
2114
+ this.fromMonthYear( this.month, this.year );
2115
+ }
2116
+
2003
2117
  _getOrdinalSuffix( day ) {
2004
2118
  if ( day > 3 && day < 21 ) return "th";
2005
2119
  switch ( day % 10 )
@@ -2028,7 +2142,8 @@ class CalendarRange {
2028
2142
 
2029
2143
  console.assert( range && range.constructor === Array, "Range cannot be empty and has to be an Array!" );
2030
2144
 
2031
- let mustAdvanceMonth = false;
2145
+ let mustSetMonth = null;
2146
+ let dateReversed = false;
2032
2147
 
2033
2148
  // Fix any issues with date range picking
2034
2149
  {
@@ -2040,9 +2155,10 @@ class CalendarRange {
2040
2155
  const tmp = range[ 0 ];
2041
2156
  range[ 0 ] = range[ 1 ];
2042
2157
  range[ 1 ] = tmp;
2158
+ dateReversed = true;
2043
2159
  }
2044
2160
 
2045
- mustAdvanceMonth = ( t0.getMonth() == t1.getMonth() ) && ( t0.getFullYear() == t1.getFullYear() );
2161
+ mustSetMonth = (dateReversed ? t1.getMonth() : t0.getMonth() ) + 2; // +1 to convert range, +1 to use next month
2046
2162
  }
2047
2163
 
2048
2164
  this.from = range[ 0 ];
@@ -2095,10 +2211,8 @@ class CalendarRange {
2095
2211
  range
2096
2212
  });
2097
2213
 
2098
- if( mustAdvanceMonth )
2099
- {
2100
- this.toCalendar._nextMonth( true );
2101
- }
2214
+ console.assert( mustSetMonth && "New Month must be valid" );
2215
+ this.toCalendar.setMonth( mustSetMonth );
2102
2216
 
2103
2217
  this.root.appendChild( this.fromCalendar.root );
2104
2218
  this.root.appendChild( this.toCalendar.root );
@@ -2322,6 +2436,19 @@ class Tabs {
2322
2436
  tabEl.instance = this;
2323
2437
  contentEl.id = tabEl.id + "_content";
2324
2438
 
2439
+ if( options.badge )
2440
+ {
2441
+ const asChild = options.badge.asChild ?? false;
2442
+ const badgeOptions = { };
2443
+
2444
+ if( asChild )
2445
+ {
2446
+ badgeOptions.parent = tabEl;
2447
+ }
2448
+
2449
+ tabEl.innerHTML += LX.badge( options.badge.content ?? "", options.badge.className, badgeOptions );
2450
+ }
2451
+
2325
2452
  if( tabEl.selected )
2326
2453
  {
2327
2454
  this.selected = name;
@@ -2594,7 +2721,7 @@ class Dialog {
2594
2721
 
2595
2722
  if( !callback )
2596
2723
  {
2597
- console.warn("Content is empty, add some widgets using 'callback' parameter!");
2724
+ console.warn("Content is empty, add some components using 'callback' parameter!");
2598
2725
  }
2599
2726
 
2600
2727
  this._oncreate = callback;
@@ -2609,6 +2736,7 @@ class Dialog {
2609
2736
  let root = document.createElement('dialog');
2610
2737
  root.className = "lexdialog " + (options.className ?? "");
2611
2738
  root.id = options.id ?? "dialog" + Dialog._last_id++;
2739
+ root.dataset["modal"] = modal;
2612
2740
  LX.root.appendChild( root );
2613
2741
 
2614
2742
  LX.doAsync( () => {
@@ -2662,9 +2790,9 @@ class Dialog {
2662
2790
  const panelChildCount = panel.root.childElementCount;
2663
2791
 
2664
2792
  const branch = panel.branch( data.name, { closed: data.closed } );
2665
- branch.widgets = data.widgets;
2793
+ branch.components = data.components;
2666
2794
 
2667
- for( let w of branch.widgets )
2795
+ for( let w of branch.components )
2668
2796
  {
2669
2797
  branch.content.appendChild( w.root );
2670
2798
  }
@@ -2908,9 +3036,6 @@ class ContextMenu {
2908
3036
 
2909
3037
  this.root = document.createElement( "div" );
2910
3038
  this.root.className = "lexcontextmenu";
2911
- this.root.style.left = ( event.x - 48 ) + "px";
2912
- this.root.style.top = ( event.y - 8 ) + "px";
2913
-
2914
3039
  this.root.addEventListener("mouseleave", function() {
2915
3040
  this.remove();
2916
3041
  });
@@ -2926,31 +3051,57 @@ class ContextMenu {
2926
3051
  item[ "icon" ] = options.icon;
2927
3052
  this.items.push( item );
2928
3053
  }
3054
+
3055
+ const nestedDialog = event.target.closest( "dialog" );
3056
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
3057
+ {
3058
+ this._parent = nestedDialog;
3059
+ }
3060
+ else
3061
+ {
3062
+ this._parent = LX.root;
3063
+ }
3064
+
3065
+ this._parent.appendChild( this.root );
3066
+
3067
+ // Set position based on parent
3068
+ const position = [ event.x - 48, event.y - 8 ];
3069
+ if( this._parent instanceof HTMLDialogElement )
3070
+ {
3071
+ let parentRect = this._parent.getBoundingClientRect();
3072
+ position[ 0 ] -= parentRect.x;
3073
+ position[ 1 ] -= parentRect.y;
3074
+ }
3075
+
3076
+ this.root.style.left = `${ position[ 0 ] }px`;
3077
+ this.root.style.top = `${ position[ 1 ] }px`;
2929
3078
  }
2930
3079
 
2931
3080
  _adjustPosition( div, margin, useAbsolute = false ) {
2932
3081
 
2933
3082
  let rect = div.getBoundingClientRect();
3083
+ let left = parseInt( div.style.left );
3084
+ let top = parseInt( div.style.top );
2934
3085
 
2935
3086
  if( !useAbsolute )
2936
3087
  {
2937
3088
  let width = rect.width;
2938
3089
  if( rect.left < 0 )
2939
3090
  {
2940
- div.style.left = margin + "px";
3091
+ left = margin;
2941
3092
  }
2942
3093
  else if( window.innerWidth - rect.right < 0 )
2943
3094
  {
2944
- div.style.left = (window.innerWidth - width - margin) + "px";
3095
+ left = (window.innerWidth - width - margin);
2945
3096
  }
2946
3097
 
2947
3098
  if( rect.top < 0 )
2948
3099
  {
2949
- div.style.top = margin + "px";
3100
+ top = margin;
2950
3101
  }
2951
3102
  else if( (rect.top + rect.height) > window.innerHeight )
2952
3103
  {
2953
- div.style.top = (window.innerHeight - rect.height - margin) + "px";
3104
+ top = (window.innerHeight - rect.height - margin);
2954
3105
  }
2955
3106
  }
2956
3107
  else
@@ -2958,15 +3109,18 @@ class ContextMenu {
2958
3109
  let dt = window.innerWidth - rect.right;
2959
3110
  if( dt < 0 )
2960
3111
  {
2961
- div.style.left = div.offsetLeft + (dt - margin) + "px";
3112
+ left = div.offsetLeft + (dt - margin);
2962
3113
  }
2963
3114
 
2964
3115
  dt = window.innerHeight - (rect.top + rect.height);
2965
3116
  if( dt < 0 )
2966
3117
  {
2967
- div.style.top = div.offsetTop + (dt - margin + 20 ) + "px";
3118
+ top = div.offsetTop + (dt - margin + 20 );
2968
3119
  }
2969
3120
  }
3121
+
3122
+ div.style.left = `${ left }px`;
3123
+ div.style.top = `${ top }px`;
2970
3124
  }
2971
3125
 
2972
3126
  _createSubmenu( o, k, c, d ) {
@@ -3188,7 +3342,6 @@ LX.ContextMenu = ContextMenu;
3188
3342
  function addContextMenu( title, event, callback, options )
3189
3343
  {
3190
3344
  const menu = new ContextMenu( event, title, options );
3191
- LX.root.appendChild( menu.root );
3192
3345
 
3193
3346
  if( callback )
3194
3347
  {
@@ -4304,6 +4457,43 @@ class CanvasMap2D {
4304
4457
 
4305
4458
  LX.CanvasMap2D = CanvasMap2D;
4306
4459
 
4460
+ class Skeleton {
4461
+
4462
+ constructor( elements ) {
4463
+
4464
+ this.root = LX.makeContainer( [ "auto", "auto" ], "flex flex-row lexskeleton" );
4465
+
4466
+ if( elements.constructor === String )
4467
+ {
4468
+ this.root.innerHTML = elements;
4469
+ }
4470
+ else
4471
+ {
4472
+ // Force array
4473
+ elements = [].concat( elements );
4474
+
4475
+ for( let e of elements )
4476
+ {
4477
+ this.root.appendChild( e );
4478
+ }
4479
+ }
4480
+ }
4481
+
4482
+ destroy() {
4483
+
4484
+ this.root.dataset[ "closed" ] = true;
4485
+
4486
+ LX.doAsync( () => {
4487
+ this.root.remove();
4488
+ this.root = null;
4489
+ }, 200 );
4490
+ }
4491
+ }
4492
+
4493
+ LX.Skeleton = Skeleton;
4494
+
4495
+ // Js native overrides
4496
+
4307
4497
  Object.defineProperty(String.prototype, 'lastChar', {
4308
4498
  get: function() { return this[ this.length - 1 ]; },
4309
4499
  enumerable: true,
@@ -4604,6 +4794,28 @@ function deleteElement( element )
4604
4794
 
4605
4795
  LX.deleteElement = deleteElement;
4606
4796
 
4797
+ /**
4798
+ * @method toCamelCase
4799
+ * @param {String} str
4800
+ */
4801
+ function toCamelCase( str )
4802
+ {
4803
+ return str.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase());
4804
+ }
4805
+
4806
+ LX.toCamelCase = toCamelCase;
4807
+
4808
+ /**
4809
+ * @method toTitleCase
4810
+ * @param {String} str
4811
+ */
4812
+ function toTitleCase( str )
4813
+ {
4814
+ return str.replace(/-/g, " ").toLowerCase().replace(/\b\w/g, char => char.toUpperCase());
4815
+ }
4816
+
4817
+ LX.toTitleCase = toTitleCase;
4818
+
4607
4819
  /**
4608
4820
  * @method getSupportedDOMName
4609
4821
  * @description Convert a text string to a valid DOM name
@@ -4632,12 +4844,12 @@ LX.getSupportedDOMName = getSupportedDOMName;
4632
4844
 
4633
4845
  /**
4634
4846
  * @method has
4635
- * @description Ask if LexGUI is using a specific component
4636
- * @param {String} componentName Name of the LexGUI component
4847
+ * @description Ask if LexGUI is using a specific extension
4848
+ * @param {String} extensionName Name of the LexGUI extension
4637
4849
  */
4638
- function has( componentName )
4850
+ function has( extensionName )
4639
4851
  {
4640
- return ( LX.components.indexOf( componentName ) > -1 );
4852
+ return ( LX.extensions.indexOf( extensionName ) > -1 );
4641
4853
  }
4642
4854
 
4643
4855
  LX.has = has;
@@ -4814,6 +5026,19 @@ function getThemeColor( colorName )
4814
5026
 
4815
5027
  LX.getThemeColor = getThemeColor;
4816
5028
 
5029
+ /**
5030
+ * @method switchSpacing
5031
+ * @description Toggles between "default" and "compact" spacing layouts
5032
+ */
5033
+ function switchSpacing()
5034
+ {
5035
+ const currentSpacing = document.documentElement.getAttribute( "data-spacing" ) ?? "default";
5036
+ document.documentElement.setAttribute( "data-spacing", ( currentSpacing == "default" ) ? "compact" : "default" );
5037
+ LX.emit( "@on_new_spacing_layout", currentSpacing );
5038
+ }
5039
+
5040
+ LX.switchSpacing = switchSpacing;
5041
+
4817
5042
  /**
4818
5043
  * @method getBase64Image
4819
5044
  * @description Convert an image to a base64 string
@@ -5355,6 +5580,80 @@ function makeKbd( keys, useSpecialKeys = true, extraClass = "" )
5355
5580
 
5356
5581
  LX.makeKbd = makeKbd;
5357
5582
 
5583
+ /**
5584
+ * @method makeBreadcrumb
5585
+ * @description Displays the path to the current resource using a hierarchy
5586
+ * @param {Array} items
5587
+ * @param {Object} options
5588
+ * maxItems: Max items until ellipsis is used to overflow
5589
+ * separatorIcon: Customize separator icon
5590
+ */
5591
+ function makeBreadcrumb( items, options = {} )
5592
+ {
5593
+ const breadcrumb = LX.makeContainer( ["auto", "auto"], "flex flex-row gap-1" );
5594
+
5595
+ const separatorIcon = options.separatorIcon ?? "ChevronRight";
5596
+ const maxItems = options.maxItems ?? 4;
5597
+ const eraseNum = items.length - maxItems;
5598
+ if( eraseNum > 0 )
5599
+ {
5600
+ const erased = items.splice( 1, eraseNum + 1 );
5601
+ const ellipsisItem = { title: "...", ellipsis: erased.map( v => v.title ).join( "/" ) };
5602
+ items.splice( 1, 0, ellipsisItem );
5603
+ }
5604
+
5605
+ for( let i = 0; i < items.length; ++i )
5606
+ {
5607
+ const item = items[ i ];
5608
+ console.assert( item.title, "Breadcrumb item must have a title!" );
5609
+
5610
+ if( i != 0 )
5611
+ {
5612
+ const icon = LX.makeIcon( separatorIcon, { svgClass: "sm fg-secondary separator" } );
5613
+ breadcrumb.appendChild( icon );
5614
+ }
5615
+
5616
+ const lastElement = ( i == items.length - 1 );
5617
+ const breadcrumbItem = LX.makeContainer( ["auto", "auto"], `p-1 flex flex-row gap-1 items-center ${ lastElement ? "" : "fg-secondary" }` );
5618
+ breadcrumb.appendChild( breadcrumbItem );
5619
+
5620
+ let itemTitle = LX.makeElement( "p", "", item.title );
5621
+ if( item.icon )
5622
+ {
5623
+ breadcrumbItem.appendChild( LX.makeIcon( item.icon, { svgClass: "sm" } ) );
5624
+ }
5625
+
5626
+ if( item.items !== undefined )
5627
+ {
5628
+ const bDropdownTrigger = LX.makeContainer( ["auto", "auto"], `${ lastElement ? "" : "fg-secondary" }` );
5629
+ bDropdownTrigger.listen( "click", (e) => {
5630
+ new LX.DropdownMenu( e.target, item.items, { side: "bottom", align: "start" });
5631
+ } );
5632
+ bDropdownTrigger.append( itemTitle );
5633
+ breadcrumbItem.appendChild( bDropdownTrigger );
5634
+ }
5635
+ else if( item.url !== undefined )
5636
+ {
5637
+ let itemUrl = LX.makeElement( "a", `decoration-none fg-${ lastElement ? "primary" : "secondary" }`, "", breadcrumbItem );
5638
+ itemUrl.href = item.url;
5639
+ itemUrl.appendChild( itemTitle );
5640
+ }
5641
+ else
5642
+ {
5643
+ breadcrumbItem.appendChild( itemTitle );
5644
+ }
5645
+
5646
+ if( item.ellipsis )
5647
+ {
5648
+ LX.asTooltip( breadcrumbItem, item.ellipsis, { side: "bottom", offset: 4 } );
5649
+ }
5650
+ }
5651
+
5652
+ return breadcrumb;
5653
+ }
5654
+
5655
+ LX.makeBreadcrumb = makeBreadcrumb;
5656
+
5358
5657
  /**
5359
5658
  * @method makeIcon
5360
5659
  * @description Gets an SVG element using one of LX.ICONS
@@ -5671,6 +5970,7 @@ LX.prompt = prompt;
5671
5970
  * @param {String} title
5672
5971
  * @param {String} description (Optional)
5673
5972
  * @param {Object} options
5973
+ * position: Set new position for the toasts ("top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right")
5674
5974
  * action: Data of the custom action { name, callback }
5675
5975
  * closable: Allow closing the toast
5676
5976
  * timeout: Time in which the toast closed automatically, in ms. -1 means persistent. [3000]
@@ -5687,9 +5987,48 @@ function toast( title, description, options = {} )
5687
5987
 
5688
5988
  const toast = document.createElement( "li" );
5689
5989
  toast.className = "lextoast";
5690
- toast.style.translate = "0 calc(100% + 30px)";
5691
5990
  this.notifications.prepend( toast );
5692
5991
 
5992
+ const [ positionVertical, positionHorizontal ] = options.position ? options.position.split( "-" ) : [ "bottom", "right" ];
5993
+
5994
+ // Reset style
5995
+ this.notifications.style.right = "unset";
5996
+ this.notifications.style.left = "unset";
5997
+ this.notifications.style.top = "unset";
5998
+ this.notifications.style.bottom = "unset";
5999
+ this.notifications.style.placeSelf = "unset";
6000
+
6001
+ switch( positionVertical )
6002
+ {
6003
+ case "top":
6004
+ toast.style.translate = "0 -30px";
6005
+ this.notifications.style.top = "1rem";
6006
+ this.notifications.style.flexDirection = "column";
6007
+ break;
6008
+ case "bottom":
6009
+ toast.style.translate = "0 calc(100% + 30px)";
6010
+ this.notifications.style.top = "auto";
6011
+ this.notifications.style.bottom = "1rem";
6012
+ this.notifications.style.flexDirection = "column-reverse";
6013
+ break;
6014
+ }
6015
+
6016
+ switch( positionHorizontal )
6017
+ {
6018
+ case "left":
6019
+ this.notifications.style.left = "1rem";
6020
+ break;
6021
+ case "center":
6022
+ this.notifications.style.placeSelf = "center";
6023
+ break;
6024
+ case "right":
6025
+ this.notifications.style.right = "1rem";
6026
+ break;
6027
+ }
6028
+
6029
+ toast.classList.add( positionVertical );
6030
+ toast.classList.add( positionHorizontal );
6031
+
5693
6032
  LX.doAsync( () => {
5694
6033
 
5695
6034
  if( this.notifications.offsetWidth > this.notifications.iWidth )
@@ -5728,7 +6067,7 @@ function toast( title, description, options = {} )
5728
6067
  const that = this;
5729
6068
 
5730
6069
  toast.close = function() {
5731
- this.dataset[ "closed" ] = true;
6070
+ this.dataset[ "open" ] = false;
5732
6071
  LX.doAsync( () => {
5733
6072
  this.remove();
5734
6073
  if( !that.notifications.childElementCount )
@@ -5774,7 +6113,32 @@ function badge( text, className, options = {} )
5774
6113
  const container = document.createElement( "div" );
5775
6114
  container.innerHTML = text;
5776
6115
  container.className = "lexbadge " + ( className ?? "" );
6116
+
6117
+ if( options.chip )
6118
+ {
6119
+ container.classList.add( "chip" );
6120
+ }
6121
+
5777
6122
  Object.assign( container.style, options.style ?? {} );
6123
+
6124
+ if( options.callback )
6125
+ {
6126
+ const arrowIcon = LX.makeIcon( "ArrowUpRight", { svgClass: "xs fg-contrast" } );
6127
+ arrowIcon.querySelector("svg").style.marginLeft = "-0.25rem";
6128
+ container.innerHTML += arrowIcon.innerHTML;
6129
+ container.addEventListener( "click", e => {
6130
+ e.preventDefault();
6131
+ e.stopPropagation();
6132
+ options.callback();
6133
+ } );
6134
+ }
6135
+
6136
+ if( options.parent )
6137
+ {
6138
+ options.parent.classList.add( "lexbadge-parent" );
6139
+ options.parent.appendChild( container );
6140
+ }
6141
+
5778
6142
  return ( options.asElement ?? false ) ? container : container.outerHTML;
5779
6143
  }
5780
6144
 
@@ -5839,7 +6203,10 @@ LX.makeContainer = makeContainer;
5839
6203
  * @param {Object} options
5840
6204
  * side: Side of the tooltip
5841
6205
  * offset: Tooltip margin offset
6206
+ * offsetX: Tooltip margin horizontal offset
6207
+ * offsetY: Tooltip margin vertical offset
5842
6208
  * active: Tooltip active by default [true]
6209
+ * callback: Callback function to execute when the tooltip is shown
5843
6210
  */
5844
6211
 
5845
6212
  function asTooltip( trigger, content, options = {} )
@@ -5849,6 +6216,11 @@ function asTooltip( trigger, content, options = {} )
5849
6216
  trigger.dataset[ "disableTooltip" ] = !( options.active ?? true );
5850
6217
 
5851
6218
  let tooltipDom = null;
6219
+ let tooltipParent = LX.root;
6220
+
6221
+ const _offset = options.offset ?? 6;
6222
+ const _offsetX = options.offsetX ?? _offset;
6223
+ const _offsetY = options.offsetY ?? _offset;
5852
6224
 
5853
6225
  trigger.addEventListener( "mouseenter", function(e) {
5854
6226
 
@@ -5857,51 +6229,72 @@ function asTooltip( trigger, content, options = {} )
5857
6229
  return;
5858
6230
  }
5859
6231
 
5860
- LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
5861
-
5862
6232
  tooltipDom = document.createElement( "div" );
5863
6233
  tooltipDom.className = "lextooltip";
5864
- tooltipDom.innerHTML = content;
6234
+ tooltipDom.innerHTML = trigger.dataset[ "tooltipContent" ] ?? content;
6235
+
6236
+ const nestedDialog = trigger.closest( "dialog" );
6237
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
6238
+ {
6239
+ tooltipParent = nestedDialog;
6240
+ }
6241
+
6242
+ // Remove other first
6243
+ LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
6244
+
6245
+ // Append new tooltip
6246
+ tooltipParent.appendChild( tooltipDom );
5865
6247
 
5866
6248
  LX.doAsync( () => {
5867
6249
 
5868
6250
  const position = [ 0, 0 ];
6251
+ const offsetX = parseFloat( trigger.dataset[ "tooltipOffsetX" ] ?? _offsetX );
6252
+ const offsetY = parseFloat( trigger.dataset[ "tooltipOffsetY" ] ?? _offsetY );
5869
6253
  const rect = this.getBoundingClientRect();
5870
- const offset = options.offset ?? 6;
5871
6254
  let alignWidth = true;
5872
6255
 
5873
6256
  switch( options.side ?? "top" )
5874
6257
  {
5875
6258
  case "left":
5876
- position[ 0 ] += ( rect.x - tooltipDom.offsetWidth - offset );
6259
+ position[ 0 ] += ( rect.x - tooltipDom.offsetWidth - offsetX );
5877
6260
  alignWidth = false;
5878
6261
  break;
5879
6262
  case "right":
5880
- position[ 0 ] += ( rect.x + rect.width + offset );
6263
+ position[ 0 ] += ( rect.x + rect.width + offsetX );
5881
6264
  alignWidth = false;
5882
6265
  break;
5883
6266
  case "top":
5884
- position[ 1 ] += ( rect.y - tooltipDom.offsetHeight - offset );
6267
+ position[ 1 ] += ( rect.y - tooltipDom.offsetHeight - offsetY );
5885
6268
  alignWidth = true;
5886
6269
  break;
5887
6270
  case "bottom":
5888
- position[ 1 ] += ( rect.y + rect.height + offset );
6271
+ position[ 1 ] += ( rect.y + rect.height + offsetY );
5889
6272
  alignWidth = true;
5890
6273
  break;
5891
6274
  }
5892
6275
 
5893
- if( alignWidth ) { position[ 0 ] += ( rect.x + rect.width * 0.5 ) - tooltipDom.offsetWidth * 0.5; }
5894
- else { position[ 1 ] += ( rect.y + rect.height * 0.5 ) - tooltipDom.offsetHeight * 0.5; }
6276
+ if( alignWidth ) { position[ 0 ] += ( rect.x + rect.width * 0.5 ) - tooltipDom.offsetWidth * 0.5 + offsetX; }
6277
+ else { position[ 1 ] += ( rect.y + rect.height * 0.5 ) - tooltipDom.offsetHeight * 0.5 + offsetY; }
5895
6278
 
5896
6279
  // Avoid collisions
5897
6280
  position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - tooltipDom.offsetWidth - 4 );
5898
6281
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - tooltipDom.offsetHeight - 4 );
5899
6282
 
6283
+ if( tooltipParent instanceof HTMLDialogElement )
6284
+ {
6285
+ let parentRect = tooltipParent.getBoundingClientRect();
6286
+ position[ 0 ] -= parentRect.x;
6287
+ position[ 1 ] -= parentRect.y;
6288
+ }
6289
+
5900
6290
  tooltipDom.style.left = `${ position[ 0 ] }px`;
5901
6291
  tooltipDom.style.top = `${ position[ 1 ] }px`;
5902
- } );
5903
6292
 
5904
- LX.root.appendChild( tooltipDom );
6293
+ if( options.callback )
6294
+ {
6295
+ options.callback( tooltipDom, trigger );
6296
+ }
6297
+ } );
5905
6298
  } );
5906
6299
 
5907
6300
  trigger.addEventListener( "mouseleave", function(e) {
@@ -6347,15 +6740,15 @@ class AreaOverlayButtons {
6347
6740
  }
6348
6741
 
6349
6742
  let callback = b.callback;
6350
- let widget = null;
6743
+ let component = null;
6351
6744
 
6352
6745
  if( b.options )
6353
6746
  {
6354
- widget = overlayPanel.addSelect( null, b.options, b.value ?? b.name, callback, _options );
6747
+ component = overlayPanel.addSelect( null, b.options, b.value ?? b.name, callback, _options );
6355
6748
  }
6356
6749
  else
6357
6750
  {
6358
- widget = overlayPanel.addButton( null, b.name, function( value, event ) {
6751
+ component = overlayPanel.addButton( null, b.name, function( value, event ) {
6359
6752
  if( b.selectable )
6360
6753
  {
6361
6754
  if( b.group )
@@ -6372,13 +6765,13 @@ class AreaOverlayButtons {
6372
6765
 
6373
6766
  if( callback )
6374
6767
  {
6375
- callback( value, event, widget.root );
6768
+ callback( value, event, component.root );
6376
6769
  }
6377
6770
 
6378
6771
  }, _options );
6379
6772
  }
6380
6773
 
6381
- this.buttons[ b.name ] = widget;
6774
+ this.buttons[ b.name ] = component;
6382
6775
 
6383
6776
  // ends the group
6384
6777
  if( overlayGroup && last )
@@ -6452,6 +6845,7 @@ class Area {
6452
6845
  * minHeight: Minimum height to be applied when resizing
6453
6846
  * maxWidth: Maximum width to be applied when resizing
6454
6847
  * maxHeight: Maximum height to be applied when resizing
6848
+ * layout: Layout to automatically split the area
6455
6849
  */
6456
6850
 
6457
6851
  constructor( options = {} ) {
@@ -6497,6 +6891,11 @@ class Area {
6497
6891
  lexroot.appendChild( this.root );
6498
6892
  }
6499
6893
 
6894
+ if( options.layout )
6895
+ {
6896
+ this.setLayout( options.layout );
6897
+ }
6898
+
6500
6899
  let overlay = options.overlay;
6501
6900
 
6502
6901
  if( overlay )
@@ -6657,6 +7056,47 @@ class Area {
6657
7056
  this.root.appendChild( element );
6658
7057
  }
6659
7058
 
7059
+ /**
7060
+ * @method setLayout
7061
+ * @description Automatically split the area to generate the desired layout
7062
+ * @param {Array} layout
7063
+ */
7064
+
7065
+ setLayout( layout ) {
7066
+
7067
+ this.layout = LX.deepCopy( layout );
7068
+
7069
+ if( !layout.splits )
7070
+ {
7071
+ console.warn( "Area layout has no splits!" );
7072
+ return;
7073
+ }
7074
+
7075
+ const _splitArea = ( area, layout ) => {
7076
+
7077
+ if( layout.className )
7078
+ {
7079
+ area.root.className += ` ${ layout.className }`;
7080
+ }
7081
+
7082
+ if( !layout.splits )
7083
+ {
7084
+ return;
7085
+ }
7086
+
7087
+ const type = layout.type ?? "horizontal";
7088
+ const resize = layout.resize ?? true;
7089
+ const minimizable = layout.minimizable ?? false;
7090
+ const [ splitA, splitB ] = area.split( { type, resize, minimizable, sizes: [ layout.splits[ 0 ].size, layout.splits[ 1 ].size ] } );
7091
+
7092
+ _splitArea( splitA, layout.splits[ 0 ] );
7093
+ _splitArea( splitB, layout.splits[ 1 ] );
7094
+
7095
+ };
7096
+
7097
+ _splitArea( this, layout );
7098
+ }
7099
+
6660
7100
  /**
6661
7101
  * @method split
6662
7102
  * @param {Object} options
@@ -6915,12 +7355,17 @@ class Area {
6915
7355
  doc.addEventListener( 'mouseup', innerMouseUp );
6916
7356
  e.stopPropagation();
6917
7357
  e.preventDefault();
6918
- document.body.classList.add( 'nocursor' );
6919
- that.splitBar.classList.add( 'nocursor' );
6920
7358
  }
6921
7359
 
6922
7360
  function innerMouseMove( e )
6923
7361
  {
7362
+ const rect = that.root.getBoundingClientRect();
7363
+ if( ( ( e.x ) < rect.x || ( e.x ) > ( rect.x + rect.width ) ) ||
7364
+ ( ( e.y ) < rect.y || ( e.y ) > ( rect.y + rect.height ) ) )
7365
+ {
7366
+ return;
7367
+ }
7368
+
6924
7369
  if( that.type == "horizontal" )
6925
7370
  {
6926
7371
  that._moveSplit( -e.movementX );
@@ -6939,8 +7384,6 @@ class Area {
6939
7384
  const doc = that.root.ownerDocument;
6940
7385
  doc.removeEventListener( 'mousemove', innerMouseMove );
6941
7386
  doc.removeEventListener( 'mouseup', innerMouseUp );
6942
- document.body.classList.remove( 'nocursor' );
6943
- that.splitBar.classList.remove( 'nocursor' );
6944
7387
  }
6945
7388
 
6946
7389
  return this.sections;
@@ -7299,10 +7742,11 @@ class Area {
7299
7742
  }
7300
7743
  else
7301
7744
  {
7745
+ const parentHeight = this.size[ 1 ];
7302
7746
  var size = Math.max( ( a2Root.offsetHeight + dt ) + a2.offset, parseInt( a2.minHeight ) );
7747
+ size = Math.min( parentHeight - LX.DEFAULT_SPLITBAR_SIZE, size );
7303
7748
  if( forceWidth ) size = forceWidth;
7304
7749
 
7305
- const parentHeight = this.size[ 1 ];
7306
7750
  const bottomPercent = ( size / parentHeight ) * 100;
7307
7751
  const topPercent = Math.max( 0, 100 - bottomPercent );
7308
7752
 
@@ -7361,13 +7805,13 @@ class Area {
7361
7805
  }
7362
7806
  LX.Area = Area;
7363
7807
 
7364
- // widget.js @jxarco
7808
+ // base_component.js @jxarco
7365
7809
 
7366
7810
  /**
7367
- * @class Widget
7811
+ * @class BaseComponent
7368
7812
  */
7369
7813
 
7370
- class Widget {
7814
+ class BaseComponent {
7371
7815
 
7372
7816
  static NONE = 0;
7373
7817
  static TEXT = 1;
@@ -7407,14 +7851,15 @@ class Widget {
7407
7851
  static TABS = 35;
7408
7852
  static DATE = 36;
7409
7853
  static MAP2D = 37;
7410
- static LABEL = 38;
7411
- static BLANK = 39;
7854
+ static LABEL = 39;
7855
+ static BLANK = 40;
7856
+ static RATE = 41;
7412
7857
 
7413
7858
  static NO_CONTEXT_TYPES = [
7414
- Widget.BUTTON,
7415
- Widget.LIST,
7416
- Widget.FILE,
7417
- Widget.PROGRESS
7859
+ BaseComponent.BUTTON,
7860
+ BaseComponent.LIST,
7861
+ BaseComponent.FILE,
7862
+ BaseComponent.PROGRESS
7418
7863
  ];
7419
7864
 
7420
7865
  constructor( type, name, value, options = {} ) {
@@ -7425,7 +7870,7 @@ class Widget {
7425
7870
  this._initialValue = value;
7426
7871
 
7427
7872
  const root = document.createElement( 'div' );
7428
- root.className = "lexwidget";
7873
+ root.className = "lexcomponent";
7429
7874
 
7430
7875
  if( options.id )
7431
7876
  {
@@ -7442,7 +7887,7 @@ class Widget {
7442
7887
  root.className += " " + options.className;
7443
7888
  }
7444
7889
 
7445
- if( type != Widget.TITLE )
7890
+ if( type != BaseComponent.TITLE )
7446
7891
  {
7447
7892
  if( options.width )
7448
7893
  {
@@ -7461,7 +7906,7 @@ class Widget {
7461
7906
  root.style.height = root.style.minHeight = options.height;
7462
7907
  }
7463
7908
 
7464
- LX.widgetResizeObserver.observe( root );
7909
+ LX.componentResizeObserver.observe( root );
7465
7910
  }
7466
7911
 
7467
7912
  if( name != undefined )
@@ -7469,7 +7914,7 @@ class Widget {
7469
7914
  if( !( options.hideName ?? false ) )
7470
7915
  {
7471
7916
  let domName = document.createElement( 'div' );
7472
- domName.className = "lexwidgetname";
7917
+ domName.className = "lexcomponentname";
7473
7918
 
7474
7919
  if( options.justifyName )
7475
7920
  {
@@ -7531,7 +7976,7 @@ class Widget {
7531
7976
  }
7532
7977
 
7533
7978
  _canPaste() {
7534
- let pasteAllowed = this.type === Widget.CUSTOM ?
7979
+ let pasteAllowed = this.type === BaseComponent.CUSTOM ?
7535
7980
  ( navigator.clipboard.customIdx !== undefined && this.customIdx == navigator.clipboard.customIdx ) : navigator.clipboard.type === this.type;
7536
7981
 
7537
7982
  pasteAllowed &= ( this.disabled !== true );
@@ -7568,7 +8013,7 @@ class Widget {
7568
8013
 
7569
8014
  if( this.onSetValue )
7570
8015
  {
7571
- let resetButton = this.root.querySelector( ".lexwidgetname .lexicon" );
8016
+ let resetButton = this.root.querySelector( ".lexcomponentname .lexicon" );
7572
8017
  if( resetButton )
7573
8018
  {
7574
8019
  resetButton.style.display = ( value != this.value() ? "block" : "none" );
@@ -7594,7 +8039,7 @@ class Widget {
7594
8039
 
7595
8040
  oncontextmenu( e ) {
7596
8041
 
7597
- if( Widget.NO_CONTEXT_TYPES.includes( this.type ) )
8042
+ if( BaseComponent.NO_CONTEXT_TYPES.includes( this.type ) )
7598
8043
  {
7599
8044
  return;
7600
8045
  }
@@ -7625,41 +8070,42 @@ class Widget {
7625
8070
 
7626
8071
  switch( this.type )
7627
8072
  {
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 }` );
8073
+ case BaseComponent.TEXT: return "Text";
8074
+ case BaseComponent.TEXTAREA: return "TextArea";
8075
+ case BaseComponent.BUTTON: return "Button";
8076
+ case BaseComponent.SELECT: return "Select";
8077
+ case BaseComponent.CHECKBOX: return "Checkbox";
8078
+ case BaseComponent.TOGGLE: return "Toggle";
8079
+ case BaseComponent.RADIO: return "Radio";
8080
+ case BaseComponent.COLOR: return "Color";
8081
+ case BaseComponent.RANGE: return "Range";
8082
+ case BaseComponent.NUMBER: return "Number";
8083
+ case BaseComponent.VECTOR: return "Vector";
8084
+ case BaseComponent.TREE: return "Tree";
8085
+ case BaseComponent.PROGRESS: return "Progress";
8086
+ case BaseComponent.FILE: return "File";
8087
+ case BaseComponent.LAYERS: return "Layers";
8088
+ case BaseComponent.ARRAY: return "Array";
8089
+ case BaseComponent.LIST: return "List";
8090
+ case BaseComponent.TAGS: return "Tags";
8091
+ case BaseComponent.CURVE: return "Curve";
8092
+ case BaseComponent.KNOB: return "Knob";
8093
+ case BaseComponent.SIZE: return "Size";
8094
+ case BaseComponent.PAD: return "Pad";
8095
+ case BaseComponent.FORM: return "Form";
8096
+ case BaseComponent.DIAL: return "Dial";
8097
+ case BaseComponent.COUNTER: return "Counter";
8098
+ case BaseComponent.TABLE: return "Table";
8099
+ case BaseComponent.TABS: return "Tabs";
8100
+ case BaseComponent.DATE: return "Date";
8101
+ case BaseComponent.MAP2D: return "Map2D";
8102
+ case BaseComponent.RATE: return "Rate";
8103
+ case BaseComponent.LABEL: return "Label";
8104
+ case BaseComponent.BLANK: return "Blank";
8105
+ case BaseComponent.CUSTOM: return this.customName;
8106
+ }
8107
+
8108
+ console.error( `Unknown Component type: ${ this.type }` );
7663
8109
  }
7664
8110
 
7665
8111
  refresh() {
@@ -7667,52 +8113,52 @@ class Widget {
7667
8113
  }
7668
8114
  }
7669
8115
 
7670
- LX.Widget = Widget;
8116
+ LX.BaseComponent = BaseComponent;
7671
8117
 
7672
- function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
8118
+ function ADD_CUSTOM_COMPONENT( customComponentName, options = {} )
7673
8119
  {
7674
8120
  let customIdx = LX.guidGenerator();
7675
8121
 
7676
- LX.Panel.prototype[ 'add' + customWidgetName ] = function( name, instance, callback ) {
8122
+ LX.Panel.prototype[ 'add' + customComponentName ] = function( name, instance, callback ) {
7677
8123
 
7678
8124
  const userParams = Array.from( arguments ).slice( 3 );
7679
8125
 
7680
- let widget = new Widget( Widget.CUSTOM, name, null, options );
7681
- this._attachWidget( widget );
8126
+ let component = new BaseComponent( BaseComponent.CUSTOM, name, null, options );
8127
+ this._attachComponent( component );
7682
8128
 
7683
- widget.customName = customWidgetName;
7684
- widget.customIdx = customIdx;
8129
+ component.customName = customComponentName;
8130
+ component.customIdx = customIdx;
7685
8131
 
7686
- widget.onGetValue = () => {
8132
+ component.onGetValue = () => {
7687
8133
  return instance;
7688
8134
  };
7689
8135
 
7690
- widget.onSetValue = ( newValue, skipCallback, event ) => {
8136
+ component.onSetValue = ( newValue, skipCallback, event ) => {
7691
8137
  instance = newValue;
7692
- refresh_widget();
8138
+ _refreshComponent();
7693
8139
  element.querySelector( ".lexcustomitems" ).toggleAttribute( 'hidden', false );
7694
8140
  if( !skipCallback )
7695
8141
  {
7696
- widget._trigger( new LX.IEvent( name, instance, event ), callback );
8142
+ component._trigger( new LX.IEvent( name, instance, event ), callback );
7697
8143
  }
7698
8144
  };
7699
8145
 
7700
- widget.onResize = ( rect ) => {
7701
- const realNameWidth = ( widget.root.domName?.style.width ?? "0px" );
8146
+ component.onResize = ( rect ) => {
8147
+ const realNameWidth = ( component.root.domName?.style.width ?? "0px" );
7702
8148
  container.style.width = `calc( 100% - ${ realNameWidth })`;
7703
8149
  };
7704
8150
 
7705
- const element = widget.root;
8151
+ const element = component.root;
7706
8152
 
7707
- let container, customWidgetsDom;
8153
+ let container, customComponentsDom;
7708
8154
  let defaultInstance = options.default ?? {};
7709
8155
 
7710
8156
  // Add instance button
7711
8157
 
7712
- const refresh_widget = () => {
8158
+ const _refreshComponent = () => {
7713
8159
 
7714
8160
  if( container ) container.remove();
7715
- if( customWidgetsDom ) customWidgetsDom.remove();
8161
+ if( customComponentsDom ) customComponentsDom.remove();
7716
8162
 
7717
8163
  container = document.createElement('div');
7718
8164
  container.className = "lexcustomcontainer w-full";
@@ -7722,7 +8168,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7722
8168
  const customIcon = LX.makeIcon( options.icon ?? "Box" );
7723
8169
  const menuIcon = LX.makeIcon( "Menu" );
7724
8170
 
7725
- let buttonName = customWidgetName + (!instance ? " [empty]" : "");
8171
+ let buttonName = customComponentName + (!instance ? " [empty]" : "");
7726
8172
  let buttonEl = this.addButton(null, buttonName, (value, event) => {
7727
8173
  if( instance )
7728
8174
  {
@@ -7732,9 +8178,9 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7732
8178
  else
7733
8179
  {
7734
8180
  LX.addContextMenu(null, event, c => {
7735
- c.add("New " + customWidgetName, () => {
8181
+ c.add("New " + customComponentName, () => {
7736
8182
  instance = {};
7737
- refresh_widget();
8183
+ _refreshComponent();
7738
8184
  element.querySelector(".lexcustomitems").toggleAttribute('hidden', false);
7739
8185
  element.dataset["opened"] = !element.querySelector(".lexcustomitems").hasAttribute("hidden");
7740
8186
  });
@@ -7756,7 +8202,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7756
8202
  LX.addContextMenu(null, e, c => {
7757
8203
  c.add("Clear", () => {
7758
8204
  instance = null;
7759
- refresh_widget();
8205
+ _refreshComponent();
7760
8206
  });
7761
8207
  });
7762
8208
  });
@@ -7764,14 +8210,14 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7764
8210
 
7765
8211
  // Show elements
7766
8212
 
7767
- customWidgetsDom = document.createElement('div');
7768
- customWidgetsDom.className = "lexcustomitems";
7769
- customWidgetsDom.toggleAttribute('hidden', true);
7770
- element.appendChild( customWidgetsDom );
8213
+ customComponentsDom = document.createElement('div');
8214
+ customComponentsDom.className = "lexcustomitems";
8215
+ customComponentsDom.toggleAttribute('hidden', true);
8216
+ element.appendChild( customComponentsDom );
7771
8217
 
7772
8218
  if( instance )
7773
8219
  {
7774
- this.queue( customWidgetsDom );
8220
+ this.queue( customComponentsDom );
7775
8221
 
7776
8222
  const on_instance_changed = ( key, value, event ) => {
7777
8223
  const setter = options[ `_set_${ key }` ];
@@ -7783,7 +8229,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7783
8229
  {
7784
8230
  instance[ key ] = value;
7785
8231
  }
7786
- widget._trigger( new LX.IEvent( name, instance, event ), callback );
8232
+ component._trigger( new LX.IEvent( name, instance, event ), callback );
7787
8233
  };
7788
8234
 
7789
8235
  for( let key in defaultInstance )
@@ -7848,11 +8294,11 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7848
8294
  }
7849
8295
  };
7850
8296
 
7851
- refresh_widget();
8297
+ _refreshComponent();
7852
8298
  };
7853
8299
  }
7854
8300
 
7855
- LX.ADD_CUSTOM_WIDGET = ADD_CUSTOM_WIDGET;
8301
+ LX.ADD_CUSTOM_COMPONENT = ADD_CUSTOM_COMPONENT;
7856
8302
 
7857
8303
  /**
7858
8304
  * @class NodeTree
@@ -8424,14 +8870,14 @@ LX.NodeTree = NodeTree;
8424
8870
 
8425
8871
  /**
8426
8872
  * @class Blank
8427
- * @description Blank Widget
8873
+ * @description Blank Component
8428
8874
  */
8429
8875
 
8430
- class Blank extends Widget {
8876
+ class Blank extends BaseComponent {
8431
8877
 
8432
8878
  constructor( width, height ) {
8433
8879
 
8434
- super( Widget.BLANK );
8880
+ super( BaseComponent.BLANK );
8435
8881
 
8436
8882
  this.root.style.width = width ?? "auto";
8437
8883
  this.root.style.height = height ?? "8px";
@@ -8442,17 +8888,17 @@ LX.Blank = Blank;
8442
8888
 
8443
8889
  /**
8444
8890
  * @class Title
8445
- * @description Title Widget
8891
+ * @description Title Component
8446
8892
  */
8447
8893
 
8448
- class Title extends Widget {
8894
+ class Title extends BaseComponent {
8449
8895
 
8450
8896
  constructor( name, options = {} ) {
8451
8897
 
8452
- console.assert( name, "Can't create Title Widget without text!" );
8898
+ console.assert( name, "Can't create Title Component without text!" );
8453
8899
 
8454
- // Note: Titles are not registered in Panel.widgets by now
8455
- super( Widget.TITLE, null, null, options );
8900
+ // Note: Titles are not registered in Panel.components by now
8901
+ super( BaseComponent.TITLE, null, null, options );
8456
8902
 
8457
8903
  this.root.className = `lextitle ${ this.root.className }`;
8458
8904
 
@@ -8486,14 +8932,14 @@ LX.Title = Title;
8486
8932
 
8487
8933
  /**
8488
8934
  * @class TextInput
8489
- * @description TextInput Widget
8935
+ * @description TextInput Component
8490
8936
  */
8491
8937
 
8492
- class TextInput extends Widget {
8938
+ class TextInput extends BaseComponent {
8493
8939
 
8494
8940
  constructor( name, value, callback, options = {} ) {
8495
8941
 
8496
- super( Widget.TEXT, name, String( value ), options );
8942
+ super( BaseComponent.TEXT, name, String( value ), options );
8497
8943
 
8498
8944
  this.onGetValue = () => {
8499
8945
  return value;
@@ -8626,14 +9072,14 @@ LX.TextInput = TextInput;
8626
9072
 
8627
9073
  /**
8628
9074
  * @class TextArea
8629
- * @description TextArea Widget
9075
+ * @description TextArea Component
8630
9076
  */
8631
9077
 
8632
- class TextArea extends Widget {
9078
+ class TextArea extends BaseComponent {
8633
9079
 
8634
9080
  constructor( name, value, callback, options = {} ) {
8635
9081
 
8636
- super( Widget.TEXTAREA, name, value, options );
9082
+ super( BaseComponent.TEXTAREA, name, value, options );
8637
9083
 
8638
9084
  this.onGetValue = () => {
8639
9085
  return value;
@@ -8727,14 +9173,14 @@ LX.TextArea = TextArea;
8727
9173
 
8728
9174
  /**
8729
9175
  * @class Button
8730
- * @description Button Widget
9176
+ * @description Button Component
8731
9177
  */
8732
9178
 
8733
- class Button extends Widget {
9179
+ class Button extends BaseComponent {
8734
9180
 
8735
9181
  constructor( name, value, callback, options = {} ) {
8736
9182
 
8737
- super( Widget.BUTTON, name, null, options );
9183
+ super( BaseComponent.BUTTON, name, null, options );
8738
9184
 
8739
9185
  this.onGetValue = () => {
8740
9186
  const swapInput = wValue.querySelector( "input" );
@@ -8949,10 +9395,10 @@ LX.Button = Button;
8949
9395
 
8950
9396
  /**
8951
9397
  * @class ComboButtons
8952
- * @description ComboButtons Widget
9398
+ * @description ComboButtons Component
8953
9399
  */
8954
9400
 
8955
- class ComboButtons extends Widget {
9401
+ class ComboButtons extends BaseComponent {
8956
9402
 
8957
9403
  constructor( name, values, options = {} ) {
8958
9404
 
@@ -9067,7 +9513,7 @@ class ComboButtons extends Widget {
9067
9513
  currentValue = currentValue[ 0 ];
9068
9514
  }
9069
9515
 
9070
- super( Widget.BUTTONS, name, null, options );
9516
+ super( BaseComponent.BUTTONS, name, null, options );
9071
9517
 
9072
9518
  this.onGetValue = () => {
9073
9519
  return currentValue;
@@ -9110,16 +9556,16 @@ LX.ComboButtons = ComboButtons;
9110
9556
 
9111
9557
  /**
9112
9558
  * @class Card
9113
- * @description Card Widget
9559
+ * @description Card Component
9114
9560
  */
9115
9561
 
9116
- class Card extends Widget {
9562
+ class Card extends BaseComponent {
9117
9563
 
9118
9564
  constructor( name, options = {} ) {
9119
9565
 
9120
9566
  options.hideName = true;
9121
9567
 
9122
- super( Widget.CARD, name, null, options );
9568
+ super( BaseComponent.CARD, name, null, options );
9123
9569
 
9124
9570
  let container = document.createElement('div');
9125
9571
  container.className = "lexcard";
@@ -9173,10 +9619,10 @@ LX.Card = Card;
9173
9619
 
9174
9620
  /**
9175
9621
  * @class Form
9176
- * @description Form Widget
9622
+ * @description Form Component
9177
9623
  */
9178
9624
 
9179
- class Form extends Widget {
9625
+ class Form extends BaseComponent {
9180
9626
 
9181
9627
  constructor( name, data, callback, options = {} ) {
9182
9628
 
@@ -9189,7 +9635,7 @@ class Form extends Widget {
9189
9635
  // Always hide name for this one
9190
9636
  options.hideName = true;
9191
9637
 
9192
- super( Widget.FORM, name, null, options );
9638
+ super( BaseComponent.FORM, name, null, options );
9193
9639
 
9194
9640
  this.onGetValue = () => {
9195
9641
  return container.formData;
@@ -9197,18 +9643,18 @@ class Form extends Widget {
9197
9643
 
9198
9644
  this.onSetValue = ( newValue, skipCallback, event ) => {
9199
9645
  container.formData = newValue;
9200
- const entries = container.querySelectorAll( ".lexwidget" );
9646
+ const entries = container.querySelectorAll( ".lexcomponent" );
9201
9647
  for( let i = 0; i < entries.length; ++i )
9202
9648
  {
9203
9649
  const entry = entries[ i ];
9204
- if( entry.jsInstance.type != LX.Widget.TEXT )
9650
+ if( entry.jsInstance.type != BaseComponent.TEXT )
9205
9651
  {
9206
9652
  continue;
9207
9653
  }
9208
- let entryName = entries[ i ].querySelector( ".lexwidgetname" ).innerText;
9654
+ let entryName = entries[ i ].querySelector( ".lexcomponentname" ).innerText;
9209
9655
  let entryInput = entries[ i ].querySelector( ".lextext input" );
9210
9656
  entryInput.value = newValue[ entryName ] ?? "";
9211
- Widget._dispatchEvent( entryInput, "focusout", skipCallback );
9657
+ BaseComponent._dispatchEvent( entryInput, "focusout", skipCallback );
9212
9658
  }
9213
9659
  };
9214
9660
 
@@ -9238,10 +9684,10 @@ class Form extends Widget {
9238
9684
  container.appendChild( label.root );
9239
9685
  }
9240
9686
 
9241
- entryData.textWidget = new LX.TextInput( null, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
9687
+ entryData.textComponent = new LX.TextInput( null, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
9242
9688
  container.formData[ entry ] = value;
9243
9689
  }, entryData );
9244
- container.appendChild( entryData.textWidget.root );
9690
+ container.appendChild( entryData.textComponent.root );
9245
9691
 
9246
9692
  container.formData[ entry ] = entryData.constructor == Object ? entryData.value : entryData;
9247
9693
  }
@@ -9266,7 +9712,7 @@ class Form extends Widget {
9266
9712
  {
9267
9713
  let entryData = data[ entry ];
9268
9714
 
9269
- if( !entryData.textWidget.valid() )
9715
+ if( !entryData.textComponent.valid() )
9270
9716
  {
9271
9717
  return;
9272
9718
  }
@@ -9286,14 +9732,14 @@ LX.Form = Form;
9286
9732
 
9287
9733
  /**
9288
9734
  * @class Select
9289
- * @description Select Widget
9735
+ * @description Select Component
9290
9736
  */
9291
9737
 
9292
- class Select extends Widget {
9738
+ class Select extends BaseComponent {
9293
9739
 
9294
9740
  constructor( name, values, value, callback, options = {} ) {
9295
9741
 
9296
- super( Widget.SELECT, name, value, options );
9742
+ super( BaseComponent.SELECT, name, value, options );
9297
9743
 
9298
9744
  this.onGetValue = () => {
9299
9745
  return value;
@@ -9366,7 +9812,7 @@ class Select extends Widget {
9366
9812
  options.overflowContainerX = options.overflowContainerY = options.overflowContainer;
9367
9813
  }
9368
9814
 
9369
- const _placeOptions = ( parent ) => {
9815
+ const _placeOptions = ( parent, forceLastPlacement ) => {
9370
9816
 
9371
9817
  const selectRoot = selectedOption.root;
9372
9818
  const rect = selectRoot.getBoundingClientRect();
@@ -9396,8 +9842,8 @@ class Select extends Widget {
9396
9842
  parent.style.top = ( topPosition + selectRoot.offsetHeight ) + 'px';
9397
9843
  list.style.height = ""; // set auto height by default
9398
9844
 
9399
- const failBelow = ( topPosition + listHeight ) > maxY;
9400
- const failAbove = ( topPosition - listHeight ) < 0;
9845
+ const failAbove = forceLastPlacement ? this._lastPlacement[ 0 ] : ( topPosition - listHeight ) < 0;
9846
+ const failBelow = forceLastPlacement ? this._lastPlacement[ 1 ] : ( topPosition + listHeight ) > maxY;
9401
9847
  if( failBelow && !failAbove )
9402
9848
  {
9403
9849
  parent.style.top = ( topPosition - listHeight ) + 'px';
@@ -9408,6 +9854,8 @@ class Select extends Widget {
9408
9854
  {
9409
9855
  list.style.height = `${ maxY - topPosition - 32 }px`; // 32px margin
9410
9856
  }
9857
+
9858
+ this._lastPlacement = [ failAbove, failBelow ];
9411
9859
  }
9412
9860
 
9413
9861
  // Manage horizontal aspect
@@ -9521,7 +9969,7 @@ class Select extends Widget {
9521
9969
  {
9522
9970
  const filterOptions = LX.deepCopy( options );
9523
9971
  filterOptions.placeholder = filterOptions.placeholder ?? "Search...";
9524
- filterOptions.skipWidget = filterOptions.skipWidget ?? true;
9972
+ filterOptions.skipComponent = filterOptions.skipComponent ?? true;
9525
9973
  filterOptions.trigger = "input";
9526
9974
  filterOptions.icon = "Search";
9527
9975
  filterOptions.className = "lexfilter";
@@ -9530,6 +9978,7 @@ class Select extends Widget {
9530
9978
  filter = new LX.TextInput(null, options.filterValue ?? "", ( v ) => {
9531
9979
  const filteredOptions = this._filterOptions( values, v );
9532
9980
  list.refresh( filteredOptions );
9981
+ _placeOptions( listDialog, true );
9533
9982
  }, filterOptions );
9534
9983
  filter.root.querySelector( ".lextext" ).style.border = "1px solid transparent";
9535
9984
 
@@ -9563,7 +10012,7 @@ class Select extends Widget {
9563
10012
 
9564
10013
  let option = document.createElement( "div" );
9565
10014
  option.className = "option";
9566
- option.innerHTML = iValue;
10015
+ option.innerHTML = ( LX.makeIcon( "Inbox", { svgClass: "mr-2" } ).innerHTML + iValue );
9567
10016
 
9568
10017
  let li = document.createElement( "li" );
9569
10018
  li.className = "lexselectitem empty";
@@ -9672,7 +10121,7 @@ class Select extends Widget {
9672
10121
  const emptyFilter = !value.length;
9673
10122
  let filteredOptions = [];
9674
10123
 
9675
- // Add widgets
10124
+ // Add components
9676
10125
  for( let i = 0; i < options.length; i++ )
9677
10126
  {
9678
10127
  let o = options[ i ];
@@ -9695,16 +10144,16 @@ LX.Select = Select;
9695
10144
 
9696
10145
  /**
9697
10146
  * @class Curve
9698
- * @description Curve Widget
10147
+ * @description Curve Component
9699
10148
  */
9700
10149
 
9701
- class Curve extends Widget {
10150
+ class Curve extends BaseComponent {
9702
10151
 
9703
10152
  constructor( name, values, callback, options = {} ) {
9704
10153
 
9705
10154
  let defaultValues = JSON.parse( JSON.stringify( values ) );
9706
10155
 
9707
- super( Widget.CURVE, name, defaultValues, options );
10156
+ super( BaseComponent.CURVE, name, defaultValues, options );
9708
10157
 
9709
10158
  this.onGetValue = () => {
9710
10159
  return JSON.parse(JSON.stringify( curveInstance.element.value ));
@@ -9756,16 +10205,16 @@ LX.Curve = Curve;
9756
10205
 
9757
10206
  /**
9758
10207
  * @class Dial
9759
- * @description Dial Widget
10208
+ * @description Dial Component
9760
10209
  */
9761
10210
 
9762
- class Dial extends Widget {
10211
+ class Dial extends BaseComponent {
9763
10212
 
9764
10213
  constructor( name, values, callback, options = {} ) {
9765
10214
 
9766
10215
  let defaultValues = JSON.parse( JSON.stringify( values ) );
9767
10216
 
9768
- super( Widget.DIAL, name, defaultValues, options );
10217
+ super( BaseComponent.DIAL, name, defaultValues, options );
9769
10218
 
9770
10219
  this.onGetValue = () => {
9771
10220
  return JSON.parse( JSON.stringify( dialInstance.element.value ) );
@@ -9813,14 +10262,14 @@ LX.Dial = Dial;
9813
10262
 
9814
10263
  /**
9815
10264
  * @class Layers
9816
- * @description Layers Widget
10265
+ * @description Layers Component
9817
10266
  */
9818
10267
 
9819
- class Layers extends Widget {
10268
+ class Layers extends BaseComponent {
9820
10269
 
9821
10270
  constructor( name, value, callback, options = {} ) {
9822
10271
 
9823
- super( Widget.LAYERS, name, value, options );
10272
+ super( BaseComponent.LAYERS, name, value, options );
9824
10273
 
9825
10274
  this.onGetValue = () => {
9826
10275
  return value;
@@ -9897,16 +10346,16 @@ LX.Layers = Layers;
9897
10346
 
9898
10347
  /**
9899
10348
  * @class ItemArray
9900
- * @description ItemArray Widget
10349
+ * @description ItemArray Component
9901
10350
  */
9902
10351
 
9903
- class ItemArray extends Widget {
10352
+ class ItemArray extends BaseComponent {
9904
10353
 
9905
10354
  constructor( name, values = [], callback, options = {} ) {
9906
10355
 
9907
10356
  options.nameWidth = "100%";
9908
10357
 
9909
- super( Widget.ARRAY, name, null, options );
10358
+ super( BaseComponent.ARRAY, name, null, options );
9910
10359
 
9911
10360
  this.onGetValue = () => {
9912
10361
  return values;
@@ -9961,41 +10410,41 @@ class ItemArray extends Widget {
9961
10410
  {
9962
10411
  const value = values[ i ];
9963
10412
  let baseclass = options.innerValues ? 'select' : value.constructor;
9964
- let widget = null;
10413
+ let component = null;
9965
10414
 
9966
10415
  switch( baseclass )
9967
10416
  {
9968
10417
  case String:
9969
- widget = new LX.TextInput(i + "", value, function(value, event) {
10418
+ component = new LX.TextInput(i + "", value, function(value, event) {
9970
10419
  values[ i ] = value;
9971
10420
  callback( values );
9972
10421
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9973
10422
  break;
9974
10423
  case Number:
9975
- widget = new NumberInput(i + "", value, function(value, event) {
10424
+ component = new NumberInput(i + "", value, function(value, event) {
9976
10425
  values[ i ] = value;
9977
10426
  callback( values );
9978
10427
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9979
10428
  break;
9980
10429
  case 'select':
9981
- widget = new Select(i + "", options.innerValues, value, function(value, event) {
10430
+ component = new Select(i + "", options.innerValues, value, function(value, event) {
9982
10431
  values[ i ] = value;
9983
10432
  callback( values );
9984
10433
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9985
10434
  break;
9986
10435
  }
9987
10436
 
9988
- console.assert( widget, `Value of type ${ baseclass } cannot be modified in ItemArray` );
10437
+ console.assert( component, `Value of type ${ baseclass } cannot be modified in ItemArray` );
9989
10438
 
9990
- arrayItems.appendChild( widget.root );
10439
+ arrayItems.appendChild( component.root );
9991
10440
 
9992
- const removeWidget = new LX.Button( null, "", ( v, event) => {
10441
+ const removeComponent = new LX.Button( null, "", ( v, event) => {
9993
10442
  values.splice( values.indexOf( value ), 1 );
9994
10443
  this._updateItems();
9995
10444
  this._trigger( new LX.IEvent(name, values, event), callback );
9996
10445
  }, { title: "Remove item", icon: "Trash3"} );
9997
10446
 
9998
- widget.root.appendChild( removeWidget.root );
10447
+ component.root.appendChild( removeComponent.root );
9999
10448
  }
10000
10449
 
10001
10450
  const addButton = new LX.Button(null, LX.makeIcon( "Plus", { svgClass: "sm" } ).innerHTML + "Add item", (v, event) => {
@@ -10015,14 +10464,14 @@ LX.ItemArray = ItemArray;
10015
10464
 
10016
10465
  /**
10017
10466
  * @class List
10018
- * @description List Widget
10467
+ * @description List Component
10019
10468
  */
10020
10469
 
10021
- class List extends Widget {
10470
+ class List extends BaseComponent {
10022
10471
 
10023
10472
  constructor( name, values, value, callback, options = {} ) {
10024
10473
 
10025
- super( Widget.LIST, name, value, options );
10474
+ super( BaseComponent.LIST, name, value, options );
10026
10475
 
10027
10476
  this.onGetValue = () => {
10028
10477
  return value;
@@ -10115,17 +10564,17 @@ LX.List = List;
10115
10564
 
10116
10565
  /**
10117
10566
  * @class Tags
10118
- * @description Tags Widget
10567
+ * @description Tags Component
10119
10568
  */
10120
10569
 
10121
- class Tags extends Widget {
10570
+ class Tags extends BaseComponent {
10122
10571
 
10123
10572
  constructor( name, value, callback, options = {} ) {
10124
10573
 
10125
10574
  value = value.replace( /\s/g, '' ).split( ',' );
10126
10575
 
10127
10576
  let defaultValue = [].concat( value );
10128
- super( Widget.TAGS, name, defaultValue, options );
10577
+ super( BaseComponent.TAGS, name, defaultValue, options );
10129
10578
 
10130
10579
  this.onGetValue = () => {
10131
10580
  return [].concat( value );
@@ -10204,19 +10653,19 @@ LX.Tags = Tags;
10204
10653
 
10205
10654
  /**
10206
10655
  * @class Checkbox
10207
- * @description Checkbox Widget
10656
+ * @description Checkbox Component
10208
10657
  */
10209
10658
 
10210
- class Checkbox extends Widget {
10659
+ class Checkbox extends BaseComponent {
10211
10660
 
10212
10661
  constructor( name, value, callback, options = {} ) {
10213
10662
 
10214
10663
  if( !name && !options.label )
10215
10664
  {
10216
- throw( "Set Widget Name or at least a label!" );
10665
+ throw( "Set Component Name or at least a label!" );
10217
10666
  }
10218
10667
 
10219
- super( Widget.CHECKBOX, name, value, options );
10668
+ super( BaseComponent.CHECKBOX, name, value, options );
10220
10669
 
10221
10670
  this.onGetValue = () => {
10222
10671
  return value;
@@ -10287,19 +10736,19 @@ LX.Checkbox = Checkbox;
10287
10736
 
10288
10737
  /**
10289
10738
  * @class Toggle
10290
- * @description Toggle Widget
10739
+ * @description Toggle Component
10291
10740
  */
10292
10741
 
10293
- class Toggle extends Widget {
10742
+ class Toggle extends BaseComponent {
10294
10743
 
10295
10744
  constructor( name, value, callback, options = {} ) {
10296
10745
 
10297
10746
  if( !name && !options.label )
10298
10747
  {
10299
- throw( "Set Widget Name or at least a label!" );
10748
+ throw( "Set Component Name or at least a label!" );
10300
10749
  }
10301
10750
 
10302
- super( Widget.TOGGLE, name, value, options );
10751
+ super( BaseComponent.TOGGLE, name, value, options );
10303
10752
 
10304
10753
  this.onGetValue = () => {
10305
10754
  return toggle.checked;
@@ -10371,14 +10820,14 @@ LX.Toggle = Toggle;
10371
10820
 
10372
10821
  /**
10373
10822
  * @class RadioGroup
10374
- * @description RadioGroup Widget
10823
+ * @description RadioGroup Component
10375
10824
  */
10376
10825
 
10377
- class RadioGroup extends Widget {
10826
+ class RadioGroup extends BaseComponent {
10378
10827
 
10379
10828
  constructor( name, label, values, callback, options = {} ) {
10380
10829
 
10381
- super( Widget.RADIO, name, null, options );
10830
+ super( BaseComponent.RADIO, name, null, options );
10382
10831
 
10383
10832
  let currentIndex = null;
10384
10833
 
@@ -10450,10 +10899,10 @@ LX.RadioGroup = RadioGroup;
10450
10899
 
10451
10900
  /**
10452
10901
  * @class ColorInput
10453
- * @description ColorInput Widget
10902
+ * @description ColorInput Component
10454
10903
  */
10455
10904
 
10456
- class ColorInput extends Widget {
10905
+ class ColorInput extends BaseComponent {
10457
10906
 
10458
10907
  constructor( name, value, callback, options = {} ) {
10459
10908
 
@@ -10462,12 +10911,12 @@ class ColorInput extends Widget {
10462
10911
  const useAlpha = options.useAlpha ??
10463
10912
  ( ( value.constructor === Object && 'a' in value ) || ( value.constructor === String && [ 5, 9 ].includes( value.length ) ) );
10464
10913
 
10465
- const widgetColor = new LX.Color( value );
10914
+ const componentColor = new LX.Color( value );
10466
10915
 
10467
10916
  // Force always hex internally
10468
- value = useAlpha ? widgetColor.hex : widgetColor.hex.substr( 0, 7 );
10917
+ value = useAlpha ? componentColor.hex : componentColor.hex.substr( 0, 7 );
10469
10918
 
10470
- super( Widget.COLOR, name, value, options );
10919
+ super( BaseComponent.COLOR, name, value, options );
10471
10920
 
10472
10921
  this.onGetValue = () => {
10473
10922
  const currentColor = new LX.Color( value );
@@ -10487,7 +10936,7 @@ class ColorInput extends Widget {
10487
10936
 
10488
10937
  if( !this._skipTextUpdate )
10489
10938
  {
10490
- textWidget.set( value, true, event );
10939
+ textComponent.set( value, true, event );
10491
10940
  }
10492
10941
 
10493
10942
  if( !skipCallback )
@@ -10555,15 +11004,15 @@ class ColorInput extends Widget {
10555
11004
  colorSampleRGB.style.width = "18px";
10556
11005
  }
10557
11006
 
10558
- const textWidget = new LX.TextInput( null, value, v => {
11007
+ const textComponent = new LX.TextInput( null, value, v => {
10559
11008
  this._skipTextUpdate = true;
10560
11009
  this.set( v );
10561
11010
  delete this._skipTextUpdate;
10562
11011
  this.picker.fromHexColor( v );
10563
11012
  }, { width: "calc( 100% - 24px )", disabled: options.disabled });
10564
11013
 
10565
- textWidget.root.style.marginLeft = "6px";
10566
- container.appendChild( textWidget.root );
11014
+ textComponent.root.style.marginLeft = "6px";
11015
+ container.appendChild( textComponent.root );
10567
11016
 
10568
11017
  LX.doAsync( this.onResize.bind( this ) );
10569
11018
  }
@@ -10573,45 +11022,117 @@ LX.ColorInput = ColorInput;
10573
11022
 
10574
11023
  /**
10575
11024
  * @class RangeInput
10576
- * @description RangeInput Widget
11025
+ * @description RangeInput Component
10577
11026
  */
10578
11027
 
10579
- class RangeInput extends Widget {
11028
+ class RangeInput extends BaseComponent {
10580
11029
 
10581
11030
  constructor( name, value, callback, options = {} ) {
10582
11031
 
10583
- super( Widget.RANGE, name, value, options );
11032
+ const ogValue = LX.deepCopy( value );
11033
+
11034
+ super( BaseComponent.RANGE, name, LX.deepCopy( ogValue ), options );
11035
+
11036
+ const isRangeValue = ( value.constructor == Array && value.length == 2 );
11037
+ if( isRangeValue )
11038
+ {
11039
+ value = ogValue[ 0 ];
11040
+ options.fill = false; // Range inputs do not fill by default
11041
+ }
10584
11042
 
10585
11043
  this.onGetValue = () => {
10586
- return value;
11044
+ let finalValue = value;
11045
+ if( isRangeValue )
11046
+ {
11047
+ finalValue = [ value, ogValue[ 1 ] ];
11048
+ }
11049
+ else if( options.left )
11050
+ {
11051
+ finalValue = ( ( +slider.max ) - value + ( +slider.min ) );
11052
+ }
11053
+ return finalValue;
10587
11054
  };
10588
11055
 
10589
11056
  this.onSetValue = ( newValue, skipCallback, event ) => {
10590
11057
 
10591
- if( isNaN( newValue ) )
11058
+ let newTpContent = "";
11059
+
11060
+ const diff = ( options.max - options.min );
11061
+
11062
+ if( isRangeValue )
10592
11063
  {
10593
- return;
11064
+ slider.value = value = LX.clamp( +newValue[ 0 ], +slider.min, +slider.max );
11065
+ this._maxSlider.value = ogValue[ 1 ] = LX.clamp( +newValue[ 1 ], +slider.min, +slider.max );
11066
+
11067
+ // Update the range slider
11068
+ const diffOffset = ( value / diff ) - 0.5;
11069
+ const diffMaxOffset = ( ogValue[ 1 ] / diff ) - 0.5;
11070
+ const remappedMin = LX.remapRange( value, options.min, options.max, 0, 1 );
11071
+ const remappedMax = LX.remapRange( ogValue[ 1 ], options.min, options.max, 0, 1 );
11072
+ slider.style.setProperty("--range-min-value", `${ remappedMin * 100 }%`);
11073
+ slider.style.setProperty("--range-max-value", `${ remappedMax * 100 }%`);
11074
+ slider.style.setProperty("--range-fix-min-offset", `${ -diffOffset }rem`);
11075
+ slider.style.setProperty("--range-fix-max-offset", `${ diffMaxOffset }rem`);
11076
+
11077
+ container.dataset[ "tooltipOffsetX" ] = container.offsetWidth * remappedMin + container.offsetWidth * ( remappedMax - remappedMin ) * 0.5 - ( container.offsetWidth * 0.5 );
11078
+ newTpContent = `${ value } - ${ ogValue[ 1 ] }`;
11079
+ }
11080
+ else
11081
+ {
11082
+ if( isNaN( newValue ) )
11083
+ {
11084
+ return;
11085
+ }
11086
+
11087
+ slider.value = value = LX.clamp( +newValue, +slider.min, +slider.max );
11088
+ const remapped = LX.remapRange( value, options.min, options.max, 0, 1 ) * 0.5;
11089
+ container.dataset[ "tooltipOffsetX" ] = container.offsetWidth * remapped - ( container.offsetWidth * 0.5 );
11090
+ newTpContent = `${ value }`;
10594
11091
  }
10595
11092
 
10596
- slider.value = value = LX.clamp( +newValue, +slider.min, +slider.max );
11093
+ container.dataset[ "tooltipContent" ] = newTpContent;
11094
+ if( this._labelTooltip )
11095
+ {
11096
+ this._labelTooltip.innerHTML = newTpContent;
11097
+ }
10597
11098
 
10598
11099
  if( !skipCallback )
10599
11100
  {
10600
- this._trigger( new LX.IEvent( name, options.left ? ( ( +slider.max ) - value + ( +slider.min ) ) : value, event ), callback );
11101
+ let finalValue = value;
11102
+ if( isRangeValue )
11103
+ {
11104
+ finalValue = [ value, ogValue[ 1 ] ];
11105
+ }
11106
+ else if( options.left )
11107
+ {
11108
+ finalValue = ( ( +slider.max ) - value + ( +slider.min ) );
11109
+ }
11110
+
11111
+ this._trigger( new LX.IEvent( name, finalValue, event ), callback );
10601
11112
  }
10602
11113
  };
10603
11114
 
10604
11115
  this.onResize = ( rect ) => {
10605
11116
  const realNameWidth = ( this.root.domName?.style.width ?? "0px" );
10606
11117
  container.style.width = options.inputWidth ?? `calc( 100% - ${ realNameWidth })`;
11118
+ if( isRangeValue )
11119
+ {
11120
+ const diff = ( options.max - options.min );
11121
+ const diffOffset = ( value / diff ) - 0.5;
11122
+ const diffMaxOffset = ( ogValue[ 1 ] / diff ) - 0.5;
11123
+ slider.style.setProperty("--range-min-value", `${ LX.remapRange( value, options.min, options.max, 0, 1 ) * 100 }%`);
11124
+ slider.style.setProperty("--range-max-value", `${ LX.remapRange( ogValue[ 1 ], options.min, options.max, 0, 1 ) * 100 }%`);
11125
+ slider.style.setProperty("--range-fix-min-offset", `${ -diffOffset }rem`);
11126
+ slider.style.setProperty("--range-fix-max-offset", `${ diffMaxOffset }rem`);
11127
+ }
10607
11128
  };
10608
11129
 
10609
11130
  const container = document.createElement( 'div' );
10610
- container.className = "lexrange";
11131
+ container.className = "lexrange relative";
10611
11132
  this.root.appendChild( container );
10612
11133
 
10613
11134
  let slider = document.createElement( 'input' );
10614
- slider.className = "lexrangeslider " + ( options.className ?? "" );
11135
+ slider.className = "lexrangeslider " + ( isRangeValue ? "pointer-events-none " : "" ) + ( options.className ?? "" );
10615
11136
  slider.min = options.min ?? 0;
10616
11137
  slider.max = options.max ?? 100;
10617
11138
  slider.step = options.step ?? 1;
@@ -10623,16 +11144,9 @@ class RangeInput extends Widget {
10623
11144
  value = LX.clamp( value, +slider.min, +slider.max );
10624
11145
  }
10625
11146
 
10626
- if( options.left )
10627
- {
10628
- value = ( ( +slider.max ) - value + ( +slider.min ) );
10629
- }
10630
-
10631
- slider.value = value;
10632
- container.appendChild( slider );
10633
-
10634
11147
  if( options.left ?? false )
10635
11148
  {
11149
+ value = ( ( +slider.max ) - value + ( +slider.min ) );
10636
11150
  slider.classList.add( "left" );
10637
11151
  }
10638
11152
 
@@ -10641,33 +11155,80 @@ class RangeInput extends Widget {
10641
11155
  slider.classList.add( "no-fill" );
10642
11156
  }
10643
11157
 
11158
+ slider.value = value;
11159
+ container.appendChild( slider );
11160
+
10644
11161
  slider.addEventListener( "input", e => {
10645
- this.set( e.target.valueAsNumber, false, e );
11162
+ this.set( isRangeValue ? [ e.target.valueAsNumber, ogValue[ 1 ] ] : e.target.valueAsNumber, false, e );
10646
11163
  }, { passive: false });
10647
11164
 
10648
- slider.addEventListener( "mousedown", function( e ) {
10649
- if( options.onPress )
10650
- {
10651
- options.onPress.bind( slider )( e, slider );
10652
- }
10653
- }, false );
11165
+ // If its a range value, we need to update the slider using the thumbs
11166
+ if( !isRangeValue )
11167
+ {
11168
+ slider.addEventListener( "mousedown", function( e ) {
11169
+ if( options.onPress )
11170
+ {
11171
+ options.onPress.bind( slider )( e, slider );
11172
+ }
11173
+ }, false );
10654
11174
 
10655
- slider.addEventListener( "mouseup", function( e ) {
10656
- if( options.onRelease )
10657
- {
10658
- options.onRelease.bind( slider )( e, slider );
10659
- }
10660
- }, false );
11175
+ slider.addEventListener( "mouseup", function( e ) {
11176
+ if( options.onRelease )
11177
+ {
11178
+ options.onRelease.bind( slider )( e, slider );
11179
+ }
11180
+ }, false );
11181
+ }
10661
11182
 
10662
11183
  // Method to change min, max, step parameters
10663
11184
  this.setLimits = ( newMin, newMax, newStep ) => {
10664
11185
  slider.min = newMin ?? slider.min;
10665
11186
  slider.max = newMax ?? slider.max;
10666
11187
  slider.step = newStep ?? slider.step;
10667
- Widget._dispatchEvent( slider, "input", true );
11188
+ BaseComponent._dispatchEvent( slider, "input", true );
10668
11189
  };
10669
11190
 
10670
- LX.doAsync( this.onResize.bind( this ) );
11191
+ LX.doAsync( () => {
11192
+
11193
+ this.onResize();
11194
+
11195
+ let offsetX = 0;
11196
+ if( isRangeValue )
11197
+ {
11198
+ const remappedMin = LX.remapRange( value, options.min, options.max, 0, 1 );
11199
+ const remappedMax = LX.remapRange( ogValue[ 1 ], options.min, options.max, 0, 1 );
11200
+ offsetX = container.offsetWidth * remappedMin + container.offsetWidth * ( remappedMax - remappedMin ) * 0.5 - ( container.offsetWidth * 0.5 );
11201
+ }
11202
+ else
11203
+ {
11204
+ const remapped = LX.remapRange( value, options.min, options.max, 0, 1 ) * 0.5;
11205
+ offsetX = container.offsetWidth * remapped - ( container.offsetWidth * 0.5 );
11206
+ }
11207
+ LX.asTooltip( container, `${ value }${ isRangeValue ? `- ${ ogValue[ 1 ] }` : `` }`, { offsetX, callback: ( tpDom ) => {
11208
+ this._labelTooltip = tpDom;
11209
+ } } );
11210
+ } );
11211
+
11212
+ if( ogValue.constructor == Array ) // Its a range value
11213
+ {
11214
+ let maxSlider = document.createElement( 'input' );
11215
+ maxSlider.className = "lexrangeslider no-fill pointer-events-none overlap absolute top-0 left-0 " + ( options.className ?? "" );
11216
+ maxSlider.min = options.min ?? 0;
11217
+ maxSlider.max = options.max ?? 100;
11218
+ maxSlider.step = options.step ?? 1;
11219
+ maxSlider.type = "range";
11220
+ maxSlider.disabled = options.disabled ?? false;
11221
+ this._maxSlider = maxSlider;
11222
+
11223
+ let maxRangeValue = ogValue[ 1 ];
11224
+ maxSlider.value = maxRangeValue = LX.clamp( maxRangeValue, +maxSlider.min, +maxSlider.max );
11225
+ container.appendChild( maxSlider );
11226
+
11227
+ maxSlider.addEventListener( "input", e => {
11228
+ ogValue[ 1 ] = +e.target.valueAsNumber;
11229
+ this.set( [ value, ogValue[ 1 ] ], false, e );
11230
+ }, { passive: false });
11231
+ }
10671
11232
  }
10672
11233
  }
10673
11234
 
@@ -10675,14 +11236,14 @@ LX.RangeInput = RangeInput;
10675
11236
 
10676
11237
  /**
10677
11238
  * @class NumberInput
10678
- * @description NumberInput Widget
11239
+ * @description NumberInput Component
10679
11240
  */
10680
11241
 
10681
- class NumberInput extends Widget {
11242
+ class NumberInput extends BaseComponent {
10682
11243
 
10683
11244
  constructor( name, value, callback, options = {} ) {
10684
11245
 
10685
- super( Widget.NUMBER, name, value, options );
11246
+ super( BaseComponent.NUMBER, name, value, options );
10686
11247
 
10687
11248
  this.onGetValue = () => {
10688
11249
  return value;
@@ -10759,7 +11320,7 @@ class NumberInput extends Widget {
10759
11320
  // Add slider below
10760
11321
  if( !options.skipSlider && options.min !== undefined && options.max !== undefined )
10761
11322
  {
10762
- let sliderBox = LX.makeContainer( [ "100%", "auto" ], "", "", box );
11323
+ let sliderBox = LX.makeContainer( [ "100%", "auto" ], "z-1 input-box", "", box );
10763
11324
  let slider = document.createElement( 'input' );
10764
11325
  slider.className = "lexinputslider";
10765
11326
  slider.min = options.min;
@@ -10897,17 +11458,17 @@ LX.NumberInput = NumberInput;
10897
11458
 
10898
11459
  /**
10899
11460
  * @class Vector
10900
- * @description Vector Widget
11461
+ * @description Vector Component
10901
11462
  */
10902
11463
 
10903
- class Vector extends Widget {
11464
+ class Vector extends BaseComponent {
10904
11465
 
10905
11466
  constructor( numComponents, name, value, callback, options = {} ) {
10906
11467
 
10907
11468
  numComponents = LX.clamp( numComponents, 2, 4 );
10908
11469
  value = value ?? new Array( numComponents ).fill( 0 );
10909
11470
 
10910
- super( Widget.VECTOR, name, [].concat( value ), options );
11471
+ super( BaseComponent.VECTOR, name, [].concat( value ), options );
10911
11472
 
10912
11473
  this.onGetValue = () => {
10913
11474
  let inputs = this.root.querySelectorAll( "input[type='number']" );
@@ -11002,13 +11563,13 @@ class Vector extends Widget {
11002
11563
  for( let v of that.querySelectorAll(".vecinput") )
11003
11564
  {
11004
11565
  v.value = LX.round( +v.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ), options.precision );
11005
- Widget._dispatchEvent( v, "change" );
11566
+ BaseComponent._dispatchEvent( v, "change" );
11006
11567
  }
11007
11568
  }
11008
11569
  else
11009
11570
  {
11010
11571
  this.value = LX.round( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ), options.precision );
11011
- Widget._dispatchEvent( vecinput, "change" );
11572
+ BaseComponent._dispatchEvent( vecinput, "change" );
11012
11573
  }
11013
11574
  }, { passive: false } );
11014
11575
 
@@ -11082,13 +11643,13 @@ class Vector extends Widget {
11082
11643
  for( let v of that.root.querySelectorAll( ".vecinput" ) )
11083
11644
  {
11084
11645
  v.value = LX.round( +v.valueAsNumber + mult * dt, options.precision );
11085
- Widget._dispatchEvent( v, "change" );
11646
+ BaseComponent._dispatchEvent( v, "change" );
11086
11647
  }
11087
11648
  }
11088
11649
  else
11089
11650
  {
11090
11651
  vecinput.value = LX.round( +vecinput.valueAsNumber + mult * dt, options.precision );
11091
- Widget._dispatchEvent( vecinput, "change" );
11652
+ BaseComponent._dispatchEvent( vecinput, "change" );
11092
11653
  }
11093
11654
  }
11094
11655
 
@@ -11147,14 +11708,14 @@ LX.Vector = Vector;
11147
11708
 
11148
11709
  /**
11149
11710
  * @class SizeInput
11150
- * @description SizeInput Widget
11711
+ * @description SizeInput Component
11151
11712
  */
11152
11713
 
11153
- class SizeInput extends Widget {
11714
+ class SizeInput extends BaseComponent {
11154
11715
 
11155
11716
  constructor( name, value, callback, options = {} ) {
11156
11717
 
11157
- super( Widget.SIZE, name, value, options );
11718
+ super( BaseComponent.SIZE, name, value, options );
11158
11719
 
11159
11720
  this.onGetValue = () => {
11160
11721
  const value = [];
@@ -11235,10 +11796,10 @@ LX.SizeInput = SizeInput;
11235
11796
 
11236
11797
  /**
11237
11798
  * @class OTPInput
11238
- * @description OTPInput Widget
11799
+ * @description OTPInput Component
11239
11800
  */
11240
11801
 
11241
- class OTPInput extends Widget {
11802
+ class OTPInput extends BaseComponent {
11242
11803
 
11243
11804
  constructor( name, value, callback, options = {} ) {
11244
11805
 
@@ -11251,7 +11812,7 @@ class OTPInput extends Widget {
11251
11812
  value = "x".repeat( patternSize );
11252
11813
  }
11253
11814
 
11254
- super( Widget.OTP, name, value, options );
11815
+ super( BaseComponent.OTP, name, value, options );
11255
11816
 
11256
11817
  this.onGetValue = () => {
11257
11818
  return +value;
@@ -11393,14 +11954,14 @@ LX.OTPInput = OTPInput;
11393
11954
 
11394
11955
  /**
11395
11956
  * @class Pad
11396
- * @description Pad Widget
11957
+ * @description Pad Component
11397
11958
  */
11398
11959
 
11399
- class Pad extends Widget {
11960
+ class Pad extends BaseComponent {
11400
11961
 
11401
11962
  constructor( name, value, callback, options = {} ) {
11402
11963
 
11403
- super( Widget.PAD, name, null, options );
11964
+ super( BaseComponent.PAD, name, null, options );
11404
11965
 
11405
11966
  this.onGetValue = () => {
11406
11967
  return thumb.value.xy;
@@ -11513,14 +12074,14 @@ LX.Pad = Pad;
11513
12074
 
11514
12075
  /**
11515
12076
  * @class Progress
11516
- * @description Progress Widget
12077
+ * @description Progress Component
11517
12078
  */
11518
12079
 
11519
- class Progress extends Widget {
12080
+ class Progress extends BaseComponent {
11520
12081
 
11521
12082
  constructor( name, value, options = {} ) {
11522
12083
 
11523
- super( Widget.PROGRESS, name, value, options );
12084
+ super( BaseComponent.PROGRESS, name, value, options );
11524
12085
 
11525
12086
  this.onGetValue = () => {
11526
12087
  return progress.value;
@@ -11650,14 +12211,14 @@ LX.Progress = Progress;
11650
12211
 
11651
12212
  /**
11652
12213
  * @class FileInput
11653
- * @description FileInput Widget
12214
+ * @description FileInput Component
11654
12215
  */
11655
12216
 
11656
- class FileInput extends Widget {
12217
+ class FileInput extends BaseComponent {
11657
12218
 
11658
12219
  constructor( name, callback, options = { } ) {
11659
12220
 
11660
- super( Widget.FILE, name, null, options );
12221
+ super( BaseComponent.FILE, name, null, options );
11661
12222
 
11662
12223
  let local = options.local ?? true;
11663
12224
  let type = options.type ?? 'text';
@@ -11735,16 +12296,16 @@ LX.FileInput = FileInput;
11735
12296
 
11736
12297
  /**
11737
12298
  * @class Tree
11738
- * @description Tree Widget
12299
+ * @description Tree Component
11739
12300
  */
11740
12301
 
11741
- class Tree extends Widget {
12302
+ class Tree extends BaseComponent {
11742
12303
 
11743
12304
  constructor( name, data, options = {} ) {
11744
12305
 
11745
12306
  options.hideName = true;
11746
12307
 
11747
- super( Widget.TREE, name, null, options );
12308
+ super( BaseComponent.TREE, name, null, options );
11748
12309
 
11749
12310
  let container = document.createElement('div');
11750
12311
  container.className = "lextree";
@@ -11817,16 +12378,16 @@ LX.Tree = Tree;
11817
12378
 
11818
12379
  /**
11819
12380
  * @class TabSections
11820
- * @description TabSections Widget
12381
+ * @description TabSections Component
11821
12382
  */
11822
12383
 
11823
- class TabSections extends Widget {
12384
+ class TabSections extends BaseComponent {
11824
12385
 
11825
12386
  constructor( name, tabs, options = {} ) {
11826
12387
 
11827
12388
  options.hideName = true;
11828
12389
 
11829
- super( Widget.TABS, name, null, options );
12390
+ super( BaseComponent.TABS, name, null, options );
11830
12391
 
11831
12392
  if( tabs.constructor != Array )
11832
12393
  {
@@ -11872,7 +12433,7 @@ class TabSections extends Widget {
11872
12433
 
11873
12434
  let infoContainer = document.createElement( "div" );
11874
12435
  infoContainer.id = tab.name.replace( /\s/g, '' );
11875
- infoContainer.className = "widgets";
12436
+ infoContainer.className = "components";
11876
12437
  infoContainer.toggleAttribute( "hidden", !( tab.selected ?? false ) );
11877
12438
  container.appendChild( infoContainer );
11878
12439
 
@@ -11881,7 +12442,7 @@ class TabSections extends Widget {
11881
12442
  tabContainer.querySelectorAll( ".lextab" ).forEach( e => { e.classList.remove( "selected" ); } );
11882
12443
  tabEl.classList.add( "selected" );
11883
12444
  // Hide all tabs content
11884
- container.querySelectorAll(".widgets").forEach( e => { e.toggleAttribute( "hidden", true ); } );
12445
+ container.querySelectorAll(".components").forEach( e => { e.toggleAttribute( "hidden", true ); } );
11885
12446
  // Show tab content
11886
12447
  const el = container.querySelector( '#' + infoContainer.id );
11887
12448
  el.toggleAttribute( "hidden" );
@@ -11924,14 +12485,14 @@ LX.TabSections = TabSections;
11924
12485
 
11925
12486
  /**
11926
12487
  * @class Counter
11927
- * @description Counter Widget
12488
+ * @description Counter Component
11928
12489
  */
11929
12490
 
11930
- class Counter extends Widget {
12491
+ class Counter extends BaseComponent {
11931
12492
 
11932
12493
  constructor( name, value, callback, options = { } ) {
11933
12494
 
11934
- super( Widget.COUNTER, name, value, options );
12495
+ super( BaseComponent.COUNTER, name, value, options );
11935
12496
 
11936
12497
  this.onGetValue = () => {
11937
12498
  return counterText.count;
@@ -11994,10 +12555,10 @@ LX.Counter = Counter;
11994
12555
 
11995
12556
  /**
11996
12557
  * @class Table
11997
- * @description Table Widget
12558
+ * @description Table Component
11998
12559
  */
11999
12560
 
12000
- class Table extends Widget {
12561
+ class Table extends BaseComponent {
12001
12562
 
12002
12563
  constructor( name, data, options = { } ) {
12003
12564
 
@@ -12006,7 +12567,7 @@ class Table extends Widget {
12006
12567
  throw( "Data is needed to create a table!" );
12007
12568
  }
12008
12569
 
12009
- super( Widget.TABLE, name, null, options );
12570
+ super( BaseComponent.TABLE, name, null, options );
12010
12571
 
12011
12572
  this.onResize = ( rect ) => {
12012
12573
  const realNameWidth = ( this.root.domName?.style.width ?? "0px" );
@@ -12056,7 +12617,7 @@ class Table extends Widget {
12056
12617
  {
12057
12618
  const filterOptions = LX.deepCopy( options );
12058
12619
  filterOptions.placeholder = `Filter ${ this.filter }...`;
12059
- filterOptions.skipWidget = true;
12620
+ filterOptions.skipComponent = true;
12060
12621
  filterOptions.trigger = "input";
12061
12622
  filterOptions.inputClass = "outline";
12062
12623
 
@@ -12075,9 +12636,9 @@ class Table extends Widget {
12075
12636
 
12076
12637
  for( let f of this.customFilters )
12077
12638
  {
12078
- f.widget = new LX.Button(null, icon.innerHTML + f.name, ( v ) => {
12639
+ f.component = new LX.Button(null, icon.innerHTML + f.name, ( v ) => {
12079
12640
 
12080
- const spanName = f.widget.root.querySelector( "span" );
12641
+ const spanName = f.component.root.querySelector( "span" );
12081
12642
 
12082
12643
  if( f.options )
12083
12644
  {
@@ -12098,7 +12659,7 @@ class Table extends Widget {
12098
12659
  };
12099
12660
  return item;
12100
12661
  } );
12101
- new LX.DropdownMenu( f.widget.root, menuOptions, { side: "bottom", align: "start" });
12662
+ new LX.DropdownMenu( f.component.root, menuOptions, { side: "bottom", align: "start" });
12102
12663
  }
12103
12664
  else if( f.type == "range" )
12104
12665
  {
@@ -12117,12 +12678,14 @@ class Table extends Widget {
12117
12678
  f.start = v;
12118
12679
  const inUse = ( f.start != f.min || f.end != f.max );
12119
12680
  spanName.innerHTML = icon.innerHTML + f.name + ( inUse ? separatorHtml + LX.badge( `${ f.start } - ${ f.end } ${ f.units ?? "" }`, "bg-tertiary fg-secondary text-sm border-0" ) : "" );
12681
+ if( inUse ) this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
12120
12682
  this.refresh();
12121
12683
  }, { skipSlider: true, min: f.min, max: f.max, step: f.step, units: f.units } );
12122
12684
  panel.addNumber( null, f.end, (v) => {
12123
12685
  f.end = v;
12124
12686
  const inUse = ( f.start != f.min || f.end != f.max );
12125
12687
  spanName.innerHTML = icon.innerHTML + f.name + ( inUse ? separatorHtml + LX.badge( `${ f.start } - ${ f.end } ${ f.units ?? "" }`, "bg-tertiary fg-secondary text-sm border-0" ) : "" );
12688
+ if( inUse ) this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
12126
12689
  this.refresh();
12127
12690
  }, { skipSlider: true, min: f.min, max: f.max, step: f.step, units: f.units } );
12128
12691
  panel.addButton( null, "Reset", () => {
@@ -12135,11 +12698,43 @@ class Table extends Widget {
12135
12698
  };
12136
12699
  panel.refresh();
12137
12700
  container.appendChild( panel.root );
12138
- new LX.Popover( f.widget.root, [ container ], { side: "bottom" } );
12701
+ new LX.Popover( f.component.root, [ container ], { side: "bottom" } );
12702
+ }
12703
+ else if( f.type == "date" )
12704
+ {
12705
+ const container = LX.makeContainer( ["auto", "auto"], "text-md" );
12706
+ const panel = new LX.Panel();
12707
+ LX.makeContainer( ["100%", "auto"], "px-3 p-2 pb-0 text-md font-medium", f.name, container );
12708
+
12709
+ panel.refresh = () => {
12710
+ panel.clear();
12711
+
12712
+ // Generate default value once the filter is used
12713
+ if( !f.default )
12714
+ {
12715
+ const date = new Date();
12716
+ const todayStringDate = `${ date.getDate() }/${ date.getMonth() + 1 }/${ date.getFullYear() }`;
12717
+ f.default = [ todayStringDate, todayStringDate ];
12718
+ }
12719
+
12720
+ const calendar = new LX.CalendarRange( f.value, {
12721
+ onChange: ( dateRange ) => {
12722
+ f.value = dateRange;
12723
+ spanName.innerHTML = icon.innerHTML + f.name + ( separatorHtml + LX.badge( `${ calendar.getFullDate() }`, "bg-tertiary fg-secondary text-sm border-0" ) );
12724
+ this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
12725
+ this.refresh();
12726
+ }
12727
+ });
12728
+
12729
+ panel.attach( calendar );
12730
+ };
12731
+ panel.refresh();
12732
+ container.appendChild( panel.root );
12733
+ new LX.Popover( f.component.root, [ container ], { side: "bottom" } );
12139
12734
  }
12140
12735
 
12141
12736
  }, { buttonClass: "px-2 primary dashed" } );
12142
- headerContainer.appendChild( f.widget.root );
12737
+ headerContainer.appendChild( f.component.root );
12143
12738
  }
12144
12739
 
12145
12740
  this._resetCustomFiltersBtn = new LX.Button(null, "resetButton", ( v ) => {
@@ -12147,12 +12742,16 @@ class Table extends Widget {
12147
12742
  this._resetCustomFiltersBtn.root.classList.add( "hidden" );
12148
12743
  for( let f of this.customFilters )
12149
12744
  {
12150
- f.widget.root.querySelector( "span" ).innerHTML = ( icon.innerHTML + f.name );
12745
+ f.component.root.querySelector( "span" ).innerHTML = ( icon.innerHTML + f.name );
12151
12746
  if( f.type == "range" )
12152
12747
  {
12153
12748
  f.start = f.min;
12154
12749
  f.end = f.max;
12155
12750
  }
12751
+ else if( f.type == "date" )
12752
+ {
12753
+ delete f.default;
12754
+ }
12156
12755
  }
12157
12756
  this.refresh();
12158
12757
  }, { title: "Reset filters", tooltip: true, icon: "X" } );
@@ -12434,7 +13033,30 @@ class Table extends Widget {
12434
13033
  }
12435
13034
  }
12436
13035
  else if( f.type == "date" )
12437
- ;
13036
+ {
13037
+ acfMap[ acfName ] = acfMap[ acfName ] ?? false;
13038
+
13039
+ const filterColIndex = data.head.indexOf( acfName );
13040
+ if( filterColIndex > -1 )
13041
+ {
13042
+ if( !f.default )
13043
+ {
13044
+ const date = new Date();
13045
+ const todayStringDate = `${ date.getDate() }/${ date.getMonth() + 1 }/${ date.getFullYear() }`;
13046
+ f.value = [ todayStringDate, todayStringDate ];
13047
+ acfMap[ acfName ] |= true;
13048
+ continue;
13049
+ }
13050
+
13051
+ f.value = f.value ?? f.default;
13052
+
13053
+ const dateString = bodyData[ filterColIndex ];
13054
+ const date = LX.dateFromDateString( dateString );
13055
+ const minDate = LX.dateFromDateString( f.value[ 0 ] );
13056
+ const maxDate = LX.dateFromDateString( f.value[ 1 ] );
13057
+ acfMap[ acfName ] |= ( date >= minDate ) && ( date <= maxDate );
13058
+ }
13059
+ }
12438
13060
  }
12439
13061
 
12440
13062
  const show = Object.values( acfMap ).reduce( ( e, acc ) => acc *= e, true );
@@ -12679,14 +13301,14 @@ LX.Table = Table;
12679
13301
 
12680
13302
  /**
12681
13303
  * @class DatePicker
12682
- * @description DatePicker Widget
13304
+ * @description DatePicker Component
12683
13305
  */
12684
13306
 
12685
- class DatePicker extends Widget {
13307
+ class DatePicker extends BaseComponent {
12686
13308
 
12687
13309
  constructor( name, dateValue, callback, options = { } ) {
12688
13310
 
12689
- super( Widget.DATE, name, null, options );
13311
+ super( BaseComponent.DATE, name, null, options );
12690
13312
 
12691
13313
  const dateAsRange = ( dateValue?.constructor === Array );
12692
13314
 
@@ -12759,10 +13381,6 @@ class DatePicker extends Widget {
12759
13381
  const calendarIcon = LX.makeIcon( "Calendar" );
12760
13382
  const calendarButton = new LX.Button( null, d0, () => {
12761
13383
  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
13384
  }, { buttonClass: `flex flex-row px-3 ${ emptyDate ? "" : "fg-tertiary" } justify-between` } );
12767
13385
  calendarButton.root.querySelector( "button" ).appendChild( calendarIcon );
12768
13386
  calendarButton.root.style.width = "100%";
@@ -12777,10 +13395,6 @@ class DatePicker extends Widget {
12777
13395
  const calendarIcon = LX.makeIcon( "Calendar" );
12778
13396
  const calendarButton = new LX.Button( null, d1, () => {
12779
13397
  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
13398
  }, { buttonClass: `flex flex-row px-3 ${ emptyDate ? "" : "fg-tertiary" } justify-between` } );
12785
13399
  calendarButton.root.querySelector( "button" ).appendChild( calendarIcon );
12786
13400
  calendarButton.root.style.width = "100%";
@@ -12805,14 +13419,14 @@ LX.DatePicker = DatePicker;
12805
13419
 
12806
13420
  /**
12807
13421
  * @class Map2D
12808
- * @description Map2D Widget
13422
+ * @description Map2D Component
12809
13423
  */
12810
13424
 
12811
- class Map2D extends Widget {
13425
+ class Map2D extends BaseComponent {
12812
13426
 
12813
13427
  constructor( name, points, callback, options = {} ) {
12814
13428
 
12815
- super( Widget.MAP2D, name, null, options );
13429
+ super( BaseComponent.MAP2D, name, null, options );
12816
13430
 
12817
13431
  this.onGetValue = () => {
12818
13432
  return this.map2d.weightsObj;
@@ -12850,6 +13464,127 @@ class Map2D extends Widget {
12850
13464
 
12851
13465
  LX.Map2D = Map2D;
12852
13466
 
13467
+ /**
13468
+ * @class Rate
13469
+ * @description Rate Component
13470
+ */
13471
+
13472
+ class Rate extends BaseComponent {
13473
+
13474
+ constructor( name, value, callback, options = {} ) {
13475
+
13476
+ const allowHalf = options.allowHalf ?? false;
13477
+
13478
+ if( !allowHalf)
13479
+ {
13480
+ value = Math.floor( value );
13481
+ }
13482
+
13483
+ super( BaseComponent.RATE, name, value, options );
13484
+
13485
+ this.onGetValue = () => {
13486
+ return value;
13487
+ };
13488
+
13489
+ this.onSetValue = ( newValue, skipCallback, event ) => {
13490
+
13491
+ value = newValue;
13492
+
13493
+ _updateStars( value );
13494
+
13495
+ if( !skipCallback )
13496
+ {
13497
+ this._trigger( new LX.IEvent( name, newValue, event ), callback );
13498
+ }
13499
+ };
13500
+
13501
+ this.onResize = ( rect ) => {
13502
+ const realNameWidth = ( this.root.domName?.style.width ?? "0px" );
13503
+ container.style.width = `calc( 100% - ${ realNameWidth })`;
13504
+ };
13505
+
13506
+ const container = document.createElement('div');
13507
+ container.className = "lexrate relative";
13508
+ this.root.appendChild( container );
13509
+
13510
+ const starsContainer = LX.makeContainer( ["fit-content", "auto"], "flex flex-row gap-1", "", container );
13511
+ const filledStarsContainer = LX.makeContainer( ["fit-content", "auto"], "absolute top-0 flex flex-row gap-1 pointer-events-none", "", container );
13512
+ const halfStarsContainer = LX.makeContainer( ["fit-content", "auto"], "absolute top-0 flex flex-row gap-1 pointer-events-none", "", container );
13513
+
13514
+ starsContainer.addEventListener("mousemove", e => {
13515
+ const star = e.target;
13516
+ const idx = star.dataset["idx"];
13517
+
13518
+ if( idx !== undefined )
13519
+ {
13520
+ const rect = star.getBoundingClientRect();
13521
+ const half = allowHalf && e.offsetX < ( rect.width * 0.5 );
13522
+ _updateStars( idx - ( half ? 0.5 : 0.0 ) );
13523
+ }
13524
+ }, false );
13525
+
13526
+ starsContainer.addEventListener("mouseleave", e => {
13527
+ _updateStars( value );
13528
+ }, false );
13529
+
13530
+ // Create all layers of stars
13531
+
13532
+ for( let i = 0; i < 5; ++i )
13533
+ {
13534
+ const starIcon = LX.makeIcon( "Star", { svgClass: `lg fill-current fg-secondary` } );
13535
+ starIcon.dataset["idx"] = ( i + 1 );
13536
+ starsContainer.appendChild( starIcon );
13537
+
13538
+ starIcon.addEventListener("click", e => {
13539
+ const rect = e.target.getBoundingClientRect();
13540
+ const half = allowHalf && e.offsetX < ( rect.width * 0.5 );
13541
+ this.set( parseFloat( e.target.dataset["idx"] ) - ( half ? 0.5 : 0.0 ) );
13542
+ }, false );
13543
+
13544
+ const filledStarIcon = LX.makeIcon( "Star", { svgClass: `lg fill-current metallicyellow` } );
13545
+ filledStarsContainer.appendChild( filledStarIcon );
13546
+
13547
+ const halfStarIcon = LX.makeIcon( "StarHalf", { svgClass: `lg fill-current metallicyellow` } );
13548
+ halfStarsContainer.appendChild( halfStarIcon );
13549
+ }
13550
+
13551
+ const _updateStars = ( v ) => {
13552
+
13553
+ for( let i = 0; i < 5; ++i )
13554
+ {
13555
+ const filled = ( v > ( i + 0.5 ) );
13556
+ const starIcon = filledStarsContainer.childNodes[ i ];
13557
+ const halfStarIcon = halfStarsContainer.childNodes[ i ];
13558
+ if( filled )
13559
+ {
13560
+ starIcon.style.opacity = 1;
13561
+ }
13562
+ else
13563
+ {
13564
+ starIcon.style.opacity = 0;
13565
+
13566
+ const halfFilled = allowHalf && ( v > i );
13567
+ if( halfFilled )
13568
+ {
13569
+ halfStarIcon.style.opacity = 1;
13570
+ }
13571
+ else
13572
+ {
13573
+ halfStarIcon.style.opacity = 0;
13574
+ }
13575
+
13576
+ }
13577
+ }
13578
+ };
13579
+
13580
+ _updateStars( value );
13581
+
13582
+ LX.doAsync( this.onResize.bind( this ) );
13583
+ }
13584
+ }
13585
+
13586
+ LX.Rate = Rate;
13587
+
12853
13588
  // panel.js @jxarco
12854
13589
 
12855
13590
  /**
@@ -12888,42 +13623,42 @@ class Panel {
12888
13623
 
12889
13624
  this.root = root;
12890
13625
  this.branches = [];
12891
- this.widgets = {};
13626
+ this.components = {};
12892
13627
 
12893
13628
  this._branchOpen = false;
12894
13629
  this._currentBranch = null;
12895
- this._queue = []; // Append widgets in other locations
12896
- this._inlineWidgetsLeft = -1;
12897
- this._inline_queued_container = null;
13630
+ this._queue = []; // Append components in other locations
13631
+ this._inlineComponentsLeft = -1;
13632
+ this._inlineQueuedContainer = null;
12898
13633
  }
12899
13634
 
12900
13635
  get( name ) {
12901
13636
 
12902
- return this.widgets[ name ];
13637
+ return this.components[ name ];
12903
13638
  }
12904
13639
 
12905
13640
  getValue( name ) {
12906
13641
 
12907
- let widget = this.widgets[ name ];
13642
+ let component = this.components[ name ];
12908
13643
 
12909
- if( !widget )
13644
+ if( !component )
12910
13645
  {
12911
- throw( "No widget called " + name );
13646
+ throw( "No component called " + name );
12912
13647
  }
12913
13648
 
12914
- return widget.value();
13649
+ return component.value();
12915
13650
  }
12916
13651
 
12917
13652
  setValue( name, value, skipCallback ) {
12918
13653
 
12919
- let widget = this.widgets[ name ];
13654
+ let component = this.components[ name ];
12920
13655
 
12921
- if( !widget )
13656
+ if( !component )
12922
13657
  {
12923
- throw( "No widget called " + name );
13658
+ throw( "No component called " + name );
12924
13659
  }
12925
13660
 
12926
- return widget.set( value, skipCallback );
13661
+ return component.set( value, skipCallback );
12927
13662
  }
12928
13663
 
12929
13664
  /**
@@ -12944,18 +13679,18 @@ class Panel {
12944
13679
 
12945
13680
  clear() {
12946
13681
 
12947
- this._branchOpen = false;
12948
13682
  this.branches = [];
13683
+ this._branchOpen = false;
12949
13684
  this._currentBranch = null;
12950
13685
 
12951
- for( let w in this.widgets )
13686
+ for( let w in this.components )
12952
13687
  {
12953
- if( this.widgets[ w ].options && this.widgets[ w ].options.signal )
13688
+ if( this.components[ w ].options && this.components[ w ].options.signal )
12954
13689
  {
12955
- const signal = this.widgets[ w ].options.signal;
13690
+ const signal = this.components[ w ].options.signal;
12956
13691
  for( let i = 0; i < LX.signals[signal].length; i++ )
12957
13692
  {
12958
- if( LX.signals[signal][ i ] == this.widgets[ w ] )
13693
+ if( LX.signals[signal][ i ] == this.components[ w ] )
12959
13694
  {
12960
13695
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
12961
13696
  }
@@ -12967,11 +13702,11 @@ class Panel {
12967
13702
  {
12968
13703
  for( let w = 0; w < this.signals.length; w++ )
12969
13704
  {
12970
- let widget = Object.values(this.signals[ w ])[ 0 ];
12971
- let signal = widget.options.signal;
13705
+ let c = Object.values(this.signals[ w ])[ 0 ];
13706
+ let signal = c.options.signal;
12972
13707
  for( let i = 0; i < LX.signals[signal].length; i++ )
12973
13708
  {
12974
- if( LX.signals[signal][ i ] == widget )
13709
+ if( LX.signals[signal][ i ] == c )
12975
13710
  {
12976
13711
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
12977
13712
  }
@@ -12979,46 +13714,46 @@ class Panel {
12979
13714
  }
12980
13715
  }
12981
13716
 
12982
- this.widgets = {};
13717
+ this.components = {};
12983
13718
  this.root.innerHTML = "";
12984
13719
  }
12985
13720
 
12986
13721
  /**
12987
13722
  * @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()
13723
+ * @param {Number} numberOfComponents Of components that will be placed in the same line
13724
+ * @param {String} className Extra class to customize inline components parent container
13725
+ * @description Next N components will be in the same line. If no number, it will inline all until calling nextLine()
12991
13726
  */
12992
13727
 
12993
- sameLine( number, className ) {
13728
+ sameLine( numberOfComponents, className ) {
12994
13729
 
12995
- this._inline_queued_container = this.queuedContainer;
12996
- this._inlineWidgetsLeft = ( number ?? Infinity );
13730
+ this._inlineQueuedContainer = this.queuedContainer;
13731
+ this._inlineComponentsLeft = ( numberOfComponents ?? Infinity );
12997
13732
  this._inlineExtraClass = className ?? null;
12998
13733
  }
12999
13734
 
13000
13735
  /**
13001
13736
  * @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.
13737
+ * @param {String} className Extra class to customize inline components parent container
13738
+ * @description Stop inlining components. Use it only if the number of components to be inlined is NOT specified.
13004
13739
  */
13005
13740
 
13006
13741
  endLine( className ) {
13007
13742
 
13008
13743
  className = className ?? this._inlineExtraClass;
13009
13744
 
13010
- if( this._inlineWidgetsLeft == -1 )
13745
+ if( this._inlineComponentsLeft == -1 )
13011
13746
  {
13012
- console.warn("No pending widgets to be inlined!");
13747
+ console.warn("No pending components to be inlined!");
13013
13748
  return;
13014
13749
  }
13015
13750
 
13016
- this._inlineWidgetsLeft = -1;
13751
+ this._inlineComponentsLeft = -1;
13017
13752
 
13018
13753
  if( !this._inlineContainer )
13019
13754
  {
13020
13755
  this._inlineContainer = document.createElement('div');
13021
- this._inlineContainer.className = "lexinlinewidgets";
13756
+ this._inlineContainer.className = "lexinlinecomponents";
13022
13757
 
13023
13758
  if( className )
13024
13759
  {
@@ -13027,14 +13762,14 @@ class Panel {
13027
13762
  }
13028
13763
 
13029
13764
  // Push all elements single element or Array[element, container]
13030
- for( let item of this._inlineWidgets )
13765
+ for( let item of this._inlineComponents )
13031
13766
  {
13032
13767
  const isPair = ( item.constructor == Array );
13033
13768
 
13034
13769
  if( isPair )
13035
13770
  {
13036
13771
  // eg. an array, inline items appended later to
13037
- if( this._inline_queued_container )
13772
+ if( this._inlineQueuedContainer )
13038
13773
  {
13039
13774
  this._inlineContainer.appendChild( item[ 0 ] );
13040
13775
  }
@@ -13050,7 +13785,7 @@ class Panel {
13050
13785
  }
13051
13786
  }
13052
13787
 
13053
- if( !this._inline_queued_container )
13788
+ if( !this._inlineQueuedContainer )
13054
13789
  {
13055
13790
  if( this._currentBranch )
13056
13791
  {
@@ -13063,10 +13798,10 @@ class Panel {
13063
13798
  }
13064
13799
  else
13065
13800
  {
13066
- this._inline_queued_container.appendChild( this._inlineContainer );
13801
+ this._inlineQueuedContainer.appendChild( this._inlineContainer );
13067
13802
  }
13068
13803
 
13069
- delete this._inlineWidgets;
13804
+ delete this._inlineComponents;
13070
13805
  delete this._inlineContainer;
13071
13806
  delete this._inlineExtraClass;
13072
13807
  }
@@ -13079,7 +13814,7 @@ class Panel {
13079
13814
  * className: Add class to the branch
13080
13815
  * closed: Set branch collapsed/opened [false]
13081
13816
  * icon: Set branch icon (LX.ICONS)
13082
- * filter: Allow filter widgets in branch by name [false]
13817
+ * filter: Allow filter components in branch by name [false]
13083
13818
  */
13084
13819
 
13085
13820
  branch( name, options = {} ) {
@@ -13100,10 +13835,10 @@ class Panel {
13100
13835
  this.branches.push( branch );
13101
13836
  this.root.appendChild( branch.root );
13102
13837
 
13103
- // Add widget filter
13838
+ // Add component filter
13104
13839
  if( options.filter )
13105
13840
  {
13106
- this._addFilter( options.filter, { callback: this._searchWidgets.bind( this, branch.name ) } );
13841
+ this._addFilter( options.filter, { callback: this._searchComponents.bind( this, branch.name ) } );
13107
13842
  }
13108
13843
 
13109
13844
  return branch;
@@ -13119,27 +13854,27 @@ class Panel {
13119
13854
  }
13120
13855
 
13121
13856
  /*
13122
- Panel Widgets
13857
+ Panel Components
13123
13858
  */
13124
13859
 
13125
- _attachWidget( widget, options = {} ) {
13860
+ _attachComponent( component, options = {} ) {
13126
13861
 
13127
- if( widget.name != undefined )
13862
+ if( component.name != undefined )
13128
13863
  {
13129
- this.widgets[ widget.name ] = widget;
13864
+ this.components[ component.name ] = component;
13130
13865
  }
13131
13866
 
13132
- if( widget.options.signal && !widget.name )
13867
+ if( component.options.signal && !component.name )
13133
13868
  {
13134
13869
  if( !this.signals )
13135
13870
  {
13136
13871
  this.signals = [];
13137
13872
  }
13138
13873
 
13139
- this.signals.push( { [ widget.options.signal ]: widget } );
13874
+ this.signals.push( { [ component.options.signal ]: component } );
13140
13875
  }
13141
13876
 
13142
- const _insertWidget = el => {
13877
+ const _insertComponent = el => {
13143
13878
  if( options.container )
13144
13879
  {
13145
13880
  options.container.appendChild( el );
@@ -13148,9 +13883,9 @@ class Panel {
13148
13883
  {
13149
13884
  if( this._currentBranch )
13150
13885
  {
13151
- if( !options.skipWidget )
13886
+ if( !options.skipComponent )
13152
13887
  {
13153
- this._currentBranch.widgets.push( widget );
13888
+ this._currentBranch.components.push( component );
13154
13889
  }
13155
13890
  this._currentBranch.content.appendChild( el );
13156
13891
  }
@@ -13167,54 +13902,54 @@ class Panel {
13167
13902
  }
13168
13903
  };
13169
13904
 
13170
- const _storeWidget = el => {
13905
+ const _storeComponent = el => {
13171
13906
 
13172
13907
  if( !this.queuedContainer )
13173
13908
  {
13174
- this._inlineWidgets.push( el );
13909
+ this._inlineComponents.push( el );
13175
13910
  }
13176
13911
  // Append content to queued tab container
13177
13912
  else
13178
13913
  {
13179
- this._inlineWidgets.push( [ el, this.queuedContainer ] );
13914
+ this._inlineComponents.push( [ el, this.queuedContainer ] );
13180
13915
  }
13181
13916
  };
13182
13917
 
13183
- // Process inline widgets
13184
- if( this._inlineWidgetsLeft > 0 && !options.skipInlineCount )
13918
+ // Process inline components
13919
+ if( this._inlineComponentsLeft > 0 && !options.skipInlineCount )
13185
13920
  {
13186
- if( !this._inlineWidgets )
13921
+ if( !this._inlineComponents )
13187
13922
  {
13188
- this._inlineWidgets = [];
13923
+ this._inlineComponents = [];
13189
13924
  }
13190
13925
 
13191
- // Store widget and its container
13192
- _storeWidget( widget.root );
13926
+ // Store component and its container
13927
+ _storeComponent( component.root );
13193
13928
 
13194
- this._inlineWidgetsLeft--;
13929
+ this._inlineComponentsLeft--;
13195
13930
 
13196
- // Last widget
13197
- if( !this._inlineWidgetsLeft )
13931
+ // Last component
13932
+ if( !this._inlineComponentsLeft )
13198
13933
  {
13199
13934
  this.endLine();
13200
13935
  }
13201
13936
  }
13202
13937
  else
13203
13938
  {
13204
- _insertWidget( widget.root );
13939
+ _insertComponent( component.root );
13205
13940
  }
13206
13941
 
13207
- return widget;
13942
+ return component;
13208
13943
  }
13209
13944
 
13210
13945
  _addFilter( placeholder, options = {} ) {
13211
13946
 
13212
13947
  options.placeholder = placeholder.constructor == String ? placeholder : "Filter properties..";
13213
- options.skipWidget = options.skipWidget ?? true;
13948
+ options.skipComponent = options.skipComponent ?? true;
13214
13949
  options.skipInlineCount = true;
13215
13950
 
13216
- let widget = new LX.TextInput( null, null, null, options );
13217
- const element = widget.root;
13951
+ let component = new LX.TextInput( null, null, null, options );
13952
+ const element = component.root;
13218
13953
  element.className += " lexfilter";
13219
13954
 
13220
13955
  let input = document.createElement('input');
@@ -13237,7 +13972,7 @@ class Panel {
13237
13972
  return element;
13238
13973
  }
13239
13974
 
13240
- _searchWidgets( branchName, value ) {
13975
+ _searchComponents( branchName, value ) {
13241
13976
 
13242
13977
  for( let b of this.branches )
13243
13978
  {
@@ -13246,8 +13981,8 @@ class Panel {
13246
13981
  continue;
13247
13982
  }
13248
13983
 
13249
- // remove all widgets
13250
- for( let w of b.widgets )
13984
+ // remove all components
13985
+ for( let w of b.components )
13251
13986
  {
13252
13987
  if( w.domEl.classList.contains('lexfilter') )
13253
13988
  {
@@ -13261,8 +13996,8 @@ class Panel {
13261
13996
 
13262
13997
  const emptyFilter = !value.length;
13263
13998
 
13264
- // add widgets
13265
- for( let w of b.widgets )
13999
+ // add components
14000
+ for( let w of b.components )
13266
14001
  {
13267
14002
  if( !emptyFilter )
13268
14003
  {
@@ -13272,7 +14007,7 @@ class Panel {
13272
14007
  if(!name.includes(value)) continue;
13273
14008
  }
13274
14009
 
13275
- // insert filtered widget
14010
+ // insert filtered component
13276
14011
  this.queuedContainer.appendChild( w.domEl );
13277
14012
  }
13278
14013
 
@@ -13343,13 +14078,13 @@ class Panel {
13343
14078
  var element = document.createElement('div');
13344
14079
  element.className = "lexseparator";
13345
14080
 
13346
- let widget = new LX.Widget( LX.Widget.SEPARATOR );
13347
- widget.root = element;
14081
+ let component = new LX.BaseComponent( LX.BaseComponent.SEPARATOR );
14082
+ component.root = element;
13348
14083
 
13349
14084
  if( this._currentBranch )
13350
14085
  {
13351
14086
  this._currentBranch.content.appendChild( element );
13352
- this._currentBranch.widgets.push( widget );
14087
+ this._currentBranch.components.push( component );
13353
14088
  }
13354
14089
  else
13355
14090
  {
@@ -13364,8 +14099,8 @@ class Panel {
13364
14099
  */
13365
14100
 
13366
14101
  addBlank( width, height ) {
13367
- const widget = new LX.Blank( width, height );
13368
- return this._attachWidget( widget );
14102
+ const component = new LX.Blank( width, height );
14103
+ return this._attachComponent( component );
13369
14104
  }
13370
14105
 
13371
14106
  /**
@@ -13380,18 +14115,18 @@ class Panel {
13380
14115
  */
13381
14116
 
13382
14117
  addTitle( name, options = {} ) {
13383
- const widget = new LX.Title( name, options );
13384
- return this._attachWidget( widget );
14118
+ const component = new LX.Title( name, options );
14119
+ return this._attachComponent( component );
13385
14120
  }
13386
14121
 
13387
14122
  /**
13388
14123
  * @method addText
13389
- * @param {String} name Widget name
14124
+ * @param {String} name Component name
13390
14125
  * @param {String} value Text value
13391
14126
  * @param {Function} callback Callback function on change
13392
14127
  * @param {Object} options:
13393
14128
  * hideName: Don't use name as label [false]
13394
- * disabled: Make the widget disabled [false]
14129
+ * disabled: Make the component disabled [false]
13395
14130
  * required: Make the input required
13396
14131
  * placeholder: Add input placeholder
13397
14132
  * icon: Icon (if any) to append at the input start
@@ -13406,18 +14141,18 @@ class Panel {
13406
14141
  */
13407
14142
 
13408
14143
  addText( name, value, callback, options = {} ) {
13409
- const widget = new LX.TextInput( name, value, callback, options );
13410
- return this._attachWidget( widget );
14144
+ const component = new LX.TextInput( name, value, callback, options );
14145
+ return this._attachComponent( component );
13411
14146
  }
13412
14147
 
13413
14148
  /**
13414
14149
  * @method addTextArea
13415
- * @param {String} name Widget name
14150
+ * @param {String} name Component name
13416
14151
  * @param {String} value Text Area value
13417
14152
  * @param {Function} callback Callback function on change
13418
14153
  * @param {Object} options:
13419
14154
  * hideName: Don't use name as label [false]
13420
- * disabled: Make the widget disabled [false]
14155
+ * disabled: Make the component disabled [false]
13421
14156
  * placeholder: Add input placeholder
13422
14157
  * resize: Allow resize [true]
13423
14158
  * trigger: Choose onchange trigger (default, input) [default]
@@ -13428,8 +14163,8 @@ class Panel {
13428
14163
  */
13429
14164
 
13430
14165
  addTextArea( name, value, callback, options = {} ) {
13431
- const widget = new LX.TextArea( name, value, callback, options );
13432
- return this._attachWidget( widget );
14166
+ const component = new LX.TextArea( name, value, callback, options );
14167
+ return this._attachComponent( component );
13433
14168
  }
13434
14169
 
13435
14170
  /**
@@ -13441,19 +14176,19 @@ class Panel {
13441
14176
  addLabel( value, options = {} ) {
13442
14177
  options.disabled = true;
13443
14178
  options.inputClass = ( options.inputClass ?? "" ) + " nobg";
13444
- const widget = this.addText( null, value, null, options );
13445
- widget.type = LX.Widget.LABEL;
13446
- return widget;
14179
+ const component = this.addText( null, value, null, options );
14180
+ component.type = LX.BaseComponent.LABEL;
14181
+ return component;
13447
14182
  }
13448
14183
 
13449
14184
  /**
13450
14185
  * @method addButton
13451
- * @param {String} name Widget name
14186
+ * @param {String} name Component name
13452
14187
  * @param {String} value Button name
13453
14188
  * @param {Function} callback Callback function on click
13454
14189
  * @param {Object} options:
13455
14190
  * hideName: Don't use name as label [false]
13456
- * disabled: Make the widget disabled [false]
14191
+ * disabled: Make the component disabled [false]
13457
14192
  * icon: Icon class to show as button value
13458
14193
  * iconPosition: Icon position (cover|start|end)
13459
14194
  * fileInput: Button click requests a file
@@ -13465,13 +14200,13 @@ class Panel {
13465
14200
  */
13466
14201
 
13467
14202
  addButton( name, value, callback, options = {} ) {
13468
- const widget = new LX.Button( name, value, callback, options );
13469
- return this._attachWidget( widget );
14203
+ const component = new LX.Button( name, value, callback, options );
14204
+ return this._attachComponent( component );
13470
14205
  }
13471
14206
 
13472
14207
  /**
13473
14208
  * @method addComboButtons
13474
- * @param {String} name Widget name
14209
+ * @param {String} name Component name
13475
14210
  * @param {Array} values Each of the {value, callback, selected, disabled} items
13476
14211
  * @param {Object} options:
13477
14212
  * hideName: Don't use name as label [false]
@@ -13482,8 +14217,8 @@ class Panel {
13482
14217
  */
13483
14218
 
13484
14219
  addComboButtons( name, values, options = {} ) {
13485
- const widget = new LX.ComboButtons( name, values, options );
13486
- return this._attachWidget( widget );
14220
+ const component = new LX.ComboButtons( name, values, options );
14221
+ return this._attachComponent( component );
13487
14222
  }
13488
14223
 
13489
14224
  /**
@@ -13498,13 +14233,13 @@ class Panel {
13498
14233
  */
13499
14234
 
13500
14235
  addCard( name, options = {} ) {
13501
- const widget = new LX.Card( name, options );
13502
- return this._attachWidget( widget );
14236
+ const component = new LX.Card( name, options );
14237
+ return this._attachComponent( component );
13503
14238
  }
13504
14239
 
13505
14240
  /**
13506
14241
  * @method addForm
13507
- * @param {String} name Widget name
14242
+ * @param {String} name Component name
13508
14243
  * @param {Object} data Form data
13509
14244
  * @param {Function} callback Callback function on submit form
13510
14245
  * @param {Object} options:
@@ -13517,13 +14252,13 @@ class Panel {
13517
14252
  */
13518
14253
 
13519
14254
  addForm( name, data, callback, options = {} ) {
13520
- const widget = new LX.Form( name, data, callback, options );
13521
- return this._attachWidget( widget );
14255
+ const component = new LX.Form( name, data, callback, options );
14256
+ return this._attachComponent( component );
13522
14257
  }
13523
14258
 
13524
14259
  /**
13525
14260
  * @method addContent
13526
- * @param {String} name Widget name
14261
+ * @param {String} name Component name
13527
14262
  * @param {HTMLElement/String} element
13528
14263
  * @param {Object} options
13529
14264
  */
@@ -13549,15 +14284,15 @@ class Panel {
13549
14284
 
13550
14285
  options.hideName = true;
13551
14286
 
13552
- let widget = new LX.Widget( LX.Widget.CONTENT, name, null, options );
13553
- widget.root.appendChild( element );
14287
+ let component = new LX.BaseComponent( LX.BaseComponent.CONTENT, name, null, options );
14288
+ component.root.appendChild( element );
13554
14289
 
13555
- return this._attachWidget( widget );
14290
+ return this._attachComponent( component );
13556
14291
  }
13557
14292
 
13558
14293
  /**
13559
14294
  * @method addImage
13560
- * @param {String} name Widget name
14295
+ * @param {String} name Component name
13561
14296
  * @param {String} url Image Url
13562
14297
  * @param {Object} options
13563
14298
  * hideName: Don't use name as label [false]
@@ -13576,43 +14311,43 @@ class Panel {
13576
14311
  Object.assign( img.style, options.style ?? {} );
13577
14312
  container.appendChild( img );
13578
14313
 
13579
- let widget = new LX.Widget( LX.Widget.IMAGE, name, null, options );
13580
- widget.root.appendChild( container );
14314
+ let component = new LX.BaseComponent( LX.BaseComponent.IMAGE, name, null, options );
14315
+ component.root.appendChild( container );
13581
14316
 
13582
14317
  // await img.decode();
13583
14318
  img.decode();
13584
14319
 
13585
- return this._attachWidget( widget );
14320
+ return this._attachComponent( component );
13586
14321
  }
13587
14322
 
13588
14323
  /**
13589
14324
  * @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..)
14325
+ * @param {String} name Component name
14326
+ * @param {Array} values Posible options of the select component -> String (for default select) or Object = {value, url} (for images, gifs..)
13592
14327
  * @param {String} value Select by default option
13593
14328
  * @param {Function} callback Callback function on change
13594
14329
  * @param {Object} options:
13595
14330
  * 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]
14331
+ * filter: Add a search bar to the component [false]
14332
+ * disabled: Make the component disabled [false]
13598
14333
  * skipReset: Don't add the reset value button when value changes
13599
14334
  * placeholder: Placeholder for the filter input
13600
14335
  * emptyMsg: Custom message to show when no filtered results
13601
14336
  */
13602
14337
 
13603
14338
  addSelect( name, values, value, callback, options = {} ) {
13604
- const widget = new LX.Select( name, values, value, callback, options );
13605
- return this._attachWidget( widget );
14339
+ const component = new LX.Select( name, values, value, callback, options );
14340
+ return this._attachComponent( component );
13606
14341
  }
13607
14342
 
13608
14343
  /**
13609
14344
  * @method addCurve
13610
- * @param {String} name Widget name
14345
+ * @param {String} name Component name
13611
14346
  * @param {Array of Array} values Array of 2N Arrays of each value of the curve
13612
14347
  * @param {Function} callback Callback function on change
13613
14348
  * @param {Object} options:
13614
14349
  * skipReset: Don't add the reset value button when value changes
13615
- * bgColor: Widget background color
14350
+ * bgColor: Component background color
13616
14351
  * pointsColor: Curve points color
13617
14352
  * lineColor: Curve line color
13618
14353
  * noOverlap: Points do not overlap, replacing themselves if necessary
@@ -13622,18 +14357,18 @@ class Panel {
13622
14357
  */
13623
14358
 
13624
14359
  addCurve( name, values, callback, options = {} ) {
13625
- const widget = new LX.Curve( name, values, callback, options );
13626
- return this._attachWidget( widget );
14360
+ const component = new LX.Curve( name, values, callback, options );
14361
+ return this._attachComponent( component );
13627
14362
  }
13628
14363
 
13629
14364
  /**
13630
14365
  * @method addDial
13631
- * @param {String} name Widget name
14366
+ * @param {String} name Component name
13632
14367
  * @param {Array of Array} values Array of 2N Arrays of each value of the dial
13633
14368
  * @param {Function} callback Callback function on change
13634
14369
  * @param {Object} options:
13635
14370
  * skipReset: Don't add the reset value button when value changes
13636
- * bgColor: Widget background color
14371
+ * bgColor: Component background color
13637
14372
  * pointsColor: Curve points color
13638
14373
  * lineColor: Curve line color
13639
14374
  * noOverlap: Points do not overlap, replacing themselves if necessary
@@ -13643,26 +14378,26 @@ class Panel {
13643
14378
  */
13644
14379
 
13645
14380
  addDial( name, values, callback, options = {} ) {
13646
- const widget = new LX.Dial( name, values, callback, options );
13647
- return this._attachWidget( widget );
14381
+ const component = new LX.Dial( name, values, callback, options );
14382
+ return this._attachComponent( component );
13648
14383
  }
13649
14384
 
13650
14385
  /**
13651
14386
  * @method addLayers
13652
- * @param {String} name Widget name
14387
+ * @param {String} name Component name
13653
14388
  * @param {Number} value Flag value by default option
13654
14389
  * @param {Function} callback Callback function on change
13655
14390
  * @param {Object} options:
13656
14391
  */
13657
14392
 
13658
14393
  addLayers( name, value, callback, options = {} ) {
13659
- const widget = new LX.Layers( name, value, callback, options );
13660
- return this._attachWidget( widget );
14394
+ const component = new LX.Layers( name, value, callback, options );
14395
+ return this._attachComponent( component );
13661
14396
  }
13662
14397
 
13663
14398
  /**
13664
14399
  * @method addArray
13665
- * @param {String} name Widget name
14400
+ * @param {String} name Component name
13666
14401
  * @param {Array} values By default values in the array
13667
14402
  * @param {Function} callback Callback function on change
13668
14403
  * @param {Object} options:
@@ -13670,13 +14405,13 @@ class Panel {
13670
14405
  */
13671
14406
 
13672
14407
  addArray( name, values = [], callback, options = {} ) {
13673
- const widget = new LX.ItemArray( name, values, callback, options );
13674
- return this._attachWidget( widget );
14408
+ const component = new LX.ItemArray( name, values, callback, options );
14409
+ return this._attachComponent( component );
13675
14410
  }
13676
14411
 
13677
14412
  /**
13678
14413
  * @method addList
13679
- * @param {String} name Widget name
14414
+ * @param {String} name Component name
13680
14415
  * @param {Array} values List values
13681
14416
  * @param {String} value Selected list value
13682
14417
  * @param {Function} callback Callback function on change
@@ -13685,13 +14420,13 @@ class Panel {
13685
14420
  */
13686
14421
 
13687
14422
  addList( name, values, value, callback, options = {} ) {
13688
- const widget = new LX.List( name, values, value, callback, options );
13689
- return this._attachWidget( widget );
14423
+ const component = new LX.List( name, values, value, callback, options );
14424
+ return this._attachComponent( component );
13690
14425
  }
13691
14426
 
13692
14427
  /**
13693
14428
  * @method addTags
13694
- * @param {String} name Widget name
14429
+ * @param {String} name Component name
13695
14430
  * @param {String} value Comma separated tags
13696
14431
  * @param {Function} callback Callback function on change
13697
14432
  * @param {Object} options:
@@ -13699,85 +14434,85 @@ class Panel {
13699
14434
  */
13700
14435
 
13701
14436
  addTags( name, value, callback, options = {} ) {
13702
- const widget = new LX.Tags( name, value, callback, options );
13703
- return this._attachWidget( widget );
14437
+ const component = new LX.Tags( name, value, callback, options );
14438
+ return this._attachComponent( component );
13704
14439
  }
13705
14440
 
13706
14441
  /**
13707
14442
  * @method addCheckbox
13708
- * @param {String} name Widget name
14443
+ * @param {String} name Component name
13709
14444
  * @param {Boolean} value Value of the checkbox
13710
14445
  * @param {Function} callback Callback function on change
13711
14446
  * @param {Object} options:
13712
- * disabled: Make the widget disabled [false]
14447
+ * disabled: Make the component disabled [false]
13713
14448
  * label: Checkbox label
13714
- * suboptions: Callback to add widgets in case of TRUE value
14449
+ * suboptions: Callback to add components in case of TRUE value
13715
14450
  * className: Extra classes to customize style
13716
14451
  */
13717
14452
 
13718
14453
  addCheckbox( name, value, callback, options = {} ) {
13719
- const widget = new LX.Checkbox( name, value, callback, options );
13720
- return this._attachWidget( widget );
14454
+ const component = new LX.Checkbox( name, value, callback, options );
14455
+ return this._attachComponent( component );
13721
14456
  }
13722
14457
 
13723
14458
  /**
13724
14459
  * @method addToggle
13725
- * @param {String} name Widget name
14460
+ * @param {String} name Component name
13726
14461
  * @param {Boolean} value Value of the checkbox
13727
14462
  * @param {Function} callback Callback function on change
13728
14463
  * @param {Object} options:
13729
- * disabled: Make the widget disabled [false]
14464
+ * disabled: Make the component disabled [false]
13730
14465
  * label: Toggle label
13731
- * suboptions: Callback to add widgets in case of TRUE value
14466
+ * suboptions: Callback to add components in case of TRUE value
13732
14467
  * className: Customize colors
13733
14468
  */
13734
14469
 
13735
14470
  addToggle( name, value, callback, options = {} ) {
13736
- const widget = new LX.Toggle( name, value, callback, options );
13737
- return this._attachWidget( widget );
14471
+ const component = new LX.Toggle( name, value, callback, options );
14472
+ return this._attachComponent( component );
13738
14473
  }
13739
14474
 
13740
14475
  /**
13741
14476
  * @method addRadioGroup
13742
- * @param {String} name Widget name
14477
+ * @param {String} name Component name
13743
14478
  * @param {String} label Radio label
13744
14479
  * @param {Array} values Radio options
13745
14480
  * @param {Function} callback Callback function on change
13746
14481
  * @param {Object} options:
13747
- * disabled: Make the widget disabled [false]
14482
+ * disabled: Make the component disabled [false]
13748
14483
  * className: Customize colors
13749
14484
  * selected: Index of the default selected option
13750
14485
  */
13751
14486
 
13752
14487
  addRadioGroup( name, label, values, callback, options = {} ) {
13753
- const widget = new LX.RadioGroup( name, label, values, callback, options );
13754
- return this._attachWidget( widget );
14488
+ const component = new LX.RadioGroup( name, label, values, callback, options );
14489
+ return this._attachComponent( component );
13755
14490
  }
13756
14491
 
13757
14492
  /**
13758
14493
  * @method addColor
13759
- * @param {String} name Widget name
14494
+ * @param {String} name Component name
13760
14495
  * @param {String} value Default color (hex)
13761
14496
  * @param {Function} callback Callback function on change
13762
14497
  * @param {Object} options:
13763
- * disabled: Make the widget disabled [false]
14498
+ * disabled: Make the component disabled [false]
13764
14499
  * useRGB: The callback returns color as Array (r, g, b) and not hex [false]
13765
14500
  */
13766
14501
 
13767
14502
  addColor( name, value, callback, options = {} ) {
13768
- const widget = new LX.ColorInput( name, value, callback, options );
13769
- return this._attachWidget( widget );
14503
+ const component = new LX.ColorInput( name, value, callback, options );
14504
+ return this._attachComponent( component );
13770
14505
  }
13771
14506
 
13772
14507
  /**
13773
14508
  * @method addRange
13774
- * @param {String} name Widget name
14509
+ * @param {String} name Component name
13775
14510
  * @param {Number} value Default number value
13776
14511
  * @param {Function} callback Callback function on change
13777
14512
  * @param {Object} options:
13778
14513
  * hideName: Don't use name as label [false]
13779
14514
  * className: Extra classes to customize style
13780
- * disabled: Make the widget disabled [false]
14515
+ * disabled: Make the component disabled [false]
13781
14516
  * left: The slider goes to the left instead of the right
13782
14517
  * fill: Fill slider progress [true]
13783
14518
  * step: Step of the input
@@ -13785,18 +14520,18 @@ class Panel {
13785
14520
  */
13786
14521
 
13787
14522
  addRange( name, value, callback, options = {} ) {
13788
- const widget = new LX.RangeInput( name, value, callback, options );
13789
- return this._attachWidget( widget );
14523
+ const component = new LX.RangeInput( name, value, callback, options );
14524
+ return this._attachComponent( component );
13790
14525
  }
13791
14526
 
13792
14527
  /**
13793
14528
  * @method addNumber
13794
- * @param {String} name Widget name
14529
+ * @param {String} name Component name
13795
14530
  * @param {Number} value Default number value
13796
14531
  * @param {Function} callback Callback function on change
13797
14532
  * @param {Object} options:
13798
14533
  * hideName: Don't use name as label [false]
13799
- * disabled: Make the widget disabled [false]
14534
+ * disabled: Make the component disabled [false]
13800
14535
  * step: Step of the input
13801
14536
  * precision: The number of digits to appear after the decimal point
13802
14537
  * min, max: Min and Max values for the input
@@ -13807,24 +14542,24 @@ class Panel {
13807
14542
  */
13808
14543
 
13809
14544
  addNumber( name, value, callback, options = {} ) {
13810
- const widget = new LX.NumberInput( name, value, callback, options );
13811
- return this._attachWidget( widget );
14545
+ const component = new LX.NumberInput( name, value, callback, options );
14546
+ return this._attachComponent( component );
13812
14547
  }
13813
14548
 
13814
14549
  static VECTOR_COMPONENTS = { 0: 'x', 1: 'y', 2: 'z', 3: 'w' };
13815
14550
 
13816
14551
  _addVector( numComponents, name, value, callback, options = {} ) {
13817
- const widget = new LX.Vector( numComponents, name, value, callback, options );
13818
- return this._attachWidget( widget );
14552
+ const component = new LX.Vector( numComponents, name, value, callback, options );
14553
+ return this._attachComponent( component );
13819
14554
  }
13820
14555
 
13821
14556
  /**
13822
14557
  * @method addVector N (2, 3, 4)
13823
- * @param {String} name Widget name
14558
+ * @param {String} name Component name
13824
14559
  * @param {Array} value Array of N components
13825
14560
  * @param {Function} callback Callback function on change
13826
14561
  * @param {Object} options:
13827
- * disabled: Make the widget disabled [false]
14562
+ * disabled: Make the component disabled [false]
13828
14563
  * step: Step of the inputs
13829
14564
  * min, max: Min and Max values for the inputs
13830
14565
  * onPress: Callback function on mouse down
@@ -13845,43 +14580,43 @@ class Panel {
13845
14580
 
13846
14581
  /**
13847
14582
  * @method addSize
13848
- * @param {String} name Widget name
14583
+ * @param {String} name Component name
13849
14584
  * @param {Number} value Default number value
13850
14585
  * @param {Function} callback Callback function on change
13851
14586
  * @param {Object} options:
13852
14587
  * hideName: Don't use name as label [false]
13853
- * disabled: Make the widget disabled [false]
14588
+ * disabled: Make the component disabled [false]
13854
14589
  * units: Unit as string added to the end of the value
13855
14590
  */
13856
14591
 
13857
14592
  addSize( name, value, callback, options = {} ) {
13858
- const widget = new LX.SizeInput( name, value, callback, options );
13859
- return this._attachWidget( widget );
14593
+ const component = new LX.SizeInput( name, value, callback, options );
14594
+ return this._attachComponent( component );
13860
14595
  }
13861
14596
 
13862
14597
  /**
13863
14598
  * @method addOTP
13864
- * @param {String} name Widget name
14599
+ * @param {String} name Component name
13865
14600
  * @param {String} value Default numeric value in string format
13866
14601
  * @param {Function} callback Callback function on change
13867
14602
  * @param {Object} options:
13868
14603
  * hideName: Don't use name as label [false]
13869
- * disabled: Make the widget disabled [false]
14604
+ * disabled: Make the component disabled [false]
13870
14605
  * pattern: OTP numeric pattern
13871
14606
  */
13872
14607
 
13873
14608
  addOTP( name, value, callback, options = {} ) {
13874
- const widget = new LX.OTPInput( name, value, callback, options );
13875
- return this._attachWidget( widget );
14609
+ const component = new LX.OTPInput( name, value, callback, options );
14610
+ return this._attachComponent( component );
13876
14611
  }
13877
14612
 
13878
14613
  /**
13879
14614
  * @method addPad
13880
- * @param {String} name Widget name
14615
+ * @param {String} name Component name
13881
14616
  * @param {Array} value Pad value
13882
14617
  * @param {Function} callback Callback function on change
13883
14618
  * @param {Object} options:
13884
- * disabled: Make the widget disabled [false]
14619
+ * disabled: Make the component disabled [false]
13885
14620
  * min, max: Min and Max values
13886
14621
  * padSize: Size of the pad (css)
13887
14622
  * onPress: Callback function on mouse down
@@ -13889,13 +14624,13 @@ class Panel {
13889
14624
  */
13890
14625
 
13891
14626
  addPad( name, value, callback, options = {} ) {
13892
- const widget = new LX.Pad( name, value, callback, options );
13893
- return this._attachWidget( widget );
14627
+ const component = new LX.Pad( name, value, callback, options );
14628
+ return this._attachComponent( component );
13894
14629
  }
13895
14630
 
13896
14631
  /**
13897
14632
  * @method addProgress
13898
- * @param {String} name Widget name
14633
+ * @param {String} name Component name
13899
14634
  * @param {Number} value Progress value
13900
14635
  * @param {Object} options:
13901
14636
  * min, max: Min and Max values
@@ -13906,29 +14641,29 @@ class Panel {
13906
14641
  */
13907
14642
 
13908
14643
  addProgress( name, value, options = {} ) {
13909
- const widget = new LX.Progress( name, value, options );
13910
- return this._attachWidget( widget );
14644
+ const component = new LX.Progress( name, value, options );
14645
+ return this._attachComponent( component );
13911
14646
  }
13912
14647
 
13913
14648
  /**
13914
14649
  * @method addFile
13915
- * @param {String} name Widget name
14650
+ * @param {String} name Component name
13916
14651
  * @param {Function} callback Callback function on change
13917
14652
  * @param {Object} options:
13918
14653
  * local: Ask for local file
13919
- * disabled: Make the widget disabled [false]
14654
+ * disabled: Make the component disabled [false]
13920
14655
  * read: Return the file itself (False) or the contents (True)
13921
14656
  * type: type to read as [text (Default), buffer, bin, url]
13922
14657
  */
13923
14658
 
13924
14659
  addFile( name, callback, options = { } ) {
13925
- const widget = new LX.FileInput( name, callback, options );
13926
- return this._attachWidget( widget );
14660
+ const component = new LX.FileInput( name, callback, options );
14661
+ return this._attachComponent( component );
13927
14662
  }
13928
14663
 
13929
14664
  /**
13930
14665
  * @method addTree
13931
- * @param {String} name Widget name
14666
+ * @param {String} name Component name
13932
14667
  * @param {Object} data Data of the tree
13933
14668
  * @param {Object} options:
13934
14669
  * icons: Array of objects with icon button information {name, icon, callback}
@@ -13938,13 +14673,13 @@ class Panel {
13938
14673
  */
13939
14674
 
13940
14675
  addTree( name, data, options = {} ) {
13941
- const widget = new LX.Tree( name, data, options );
13942
- return this._attachWidget( widget );
14676
+ const component = new LX.Tree( name, data, options );
14677
+ return this._attachComponent( component );
13943
14678
  }
13944
14679
 
13945
14680
  /**
13946
14681
  * @method addTabSections
13947
- * @param {String} name Widget name
14682
+ * @param {String} name Component name
13948
14683
  * @param {Array} tabs Contains objects with {
13949
14684
  * name: Name of the tab (if icon, use as title)
13950
14685
  * icon: Icon to be used as the tab icon (optional)
@@ -13959,30 +14694,30 @@ class Panel {
13959
14694
  */
13960
14695
 
13961
14696
  addTabSections( name, tabs, options = {} ) {
13962
- const widget = new LX.TabSections( name, tabs, options );
13963
- return this._attachWidget( widget );
14697
+ const component = new LX.TabSections( name, tabs, options );
14698
+ return this._attachComponent( component );
13964
14699
  }
13965
14700
 
13966
14701
  /**
13967
14702
  * @method addCounter
13968
- * @param {String} name Widget name
14703
+ * @param {String} name Component name
13969
14704
  * @param {Number} value Counter value
13970
14705
  * @param {Function} callback Callback function on change
13971
14706
  * @param {Object} options:
13972
- * disabled: Make the widget disabled [false]
14707
+ * disabled: Make the component disabled [false]
13973
14708
  * min, max: Min and Max values
13974
14709
  * step: Step for adding/substracting
13975
14710
  * label: Text to show below the counter
13976
14711
  */
13977
14712
 
13978
14713
  addCounter( name, value, callback, options = { } ) {
13979
- const widget = new LX.Counter( name, value, callback, options );
13980
- return this._attachWidget( widget );
14714
+ const component = new LX.Counter( name, value, callback, options );
14715
+ return this._attachComponent( component );
13981
14716
  }
13982
14717
 
13983
14718
  /**
13984
14719
  * @method addTable
13985
- * @param {String} name Widget name
14720
+ * @param {String} name Component name
13986
14721
  * @param {Number} data Table data
13987
14722
  * @param {Object} options:
13988
14723
  * hideName: Don't use name as label [false]
@@ -14000,13 +14735,13 @@ class Panel {
14000
14735
  */
14001
14736
 
14002
14737
  addTable( name, data, options = { } ) {
14003
- const widget = new LX.Table( name, data, options );
14004
- return this._attachWidget( widget );
14738
+ const component = new LX.Table( name, data, options );
14739
+ return this._attachComponent( component );
14005
14740
  }
14006
14741
 
14007
14742
  /**
14008
14743
  * @method addDate
14009
- * @param {String} name Widget name
14744
+ * @param {String} name Component name
14010
14745
  * @param {String} dateValue
14011
14746
  * @param {Function} callback
14012
14747
  * @param {Object} options:
@@ -14017,21 +14752,34 @@ class Panel {
14017
14752
  */
14018
14753
 
14019
14754
  addDate( name, dateValue, callback, options = { } ) {
14020
- const widget = new LX.DatePicker( name, dateValue, callback, options );
14021
- return this._attachWidget( widget );
14755
+ const component = new LX.DatePicker( name, dateValue, callback, options );
14756
+ return this._attachComponent( component );
14022
14757
  }
14023
14758
 
14024
14759
  /**
14025
14760
  * @method addMap2D
14026
- * @param {String} name Widget name
14761
+ * @param {String} name Component name
14027
14762
  * @param {Array} points
14028
14763
  * @param {Function} callback
14029
14764
  * @param {Object} options:
14030
14765
  */
14031
14766
 
14032
14767
  addMap2D( name, points, callback, options = { } ) {
14033
- const widget = new LX.Map2D( name, points, callback, options );
14034
- return this._attachWidget( widget );
14768
+ const component = new LX.Map2D( name, points, callback, options );
14769
+ return this._attachComponent( component );
14770
+ }
14771
+
14772
+ /**
14773
+ * @method addRate
14774
+ * @param {String} name Component name
14775
+ * @param {Number} value
14776
+ * @param {Function} callback
14777
+ * @param {Object} options:
14778
+ */
14779
+
14780
+ addRate( name, value, callback, options = { } ) {
14781
+ const component = new LX.Rate( name, value, callback, options );
14782
+ return this._attachComponent( component );
14035
14783
  }
14036
14784
  }
14037
14785
 
@@ -14066,7 +14814,7 @@ class Branch {
14066
14814
 
14067
14815
  this.closed = options.closed ?? false;
14068
14816
  this.root = root;
14069
- this.widgets = [];
14817
+ this.components = [];
14070
14818
 
14071
14819
  // Create element
14072
14820
  var title = document.createElement( 'div' );
@@ -14137,8 +14885,8 @@ class Branch {
14137
14885
  _onMakeFloating() {
14138
14886
 
14139
14887
  const dialog = new LX.Dialog( this.name, p => {
14140
- // add widgets
14141
- for( let w of this.widgets )
14888
+ // Add components
14889
+ for( let w of this.components )
14142
14890
  {
14143
14891
  p.root.appendChild( w.root );
14144
14892
  }
@@ -14149,7 +14897,7 @@ class Branch {
14149
14897
 
14150
14898
  dialog.branchData = {
14151
14899
  name: this.name,
14152
- widgets: this.widgets,
14900
+ components: this.components,
14153
14901
  closed: this.closed,
14154
14902
  panel: this.panel,
14155
14903
  childIndex
@@ -14161,7 +14909,7 @@ class Branch {
14161
14909
  _addBranchSeparator() {
14162
14910
 
14163
14911
  const element = document.createElement('div');
14164
- element.className = "lexwidgetseparator";
14912
+ element.className = "lexcomponentseparator";
14165
14913
  element.style.width = "100%";
14166
14914
  element.style.background = "none";
14167
14915
 
@@ -14214,7 +14962,7 @@ class Branch {
14214
14962
 
14215
14963
  function innerMouseUp(e)
14216
14964
  {
14217
- that._updateWidgets();
14965
+ that._updateComponents();
14218
14966
 
14219
14967
  line.style.height = "0px";
14220
14968
 
@@ -14227,15 +14975,15 @@ class Branch {
14227
14975
  this.content.appendChild( element );
14228
14976
  }
14229
14977
 
14230
- _updateWidgets() {
14978
+ _updateComponents() {
14231
14979
 
14232
14980
  var size = this.grabber.style.marginLeft;
14233
14981
 
14234
- // Update sizes of widgets inside
14235
- for( let i = 0; i < this.widgets.length; i++ )
14982
+ // Update sizes of components inside
14983
+ for( let i = 0; i < this.components.length; i++ )
14236
14984
  {
14237
- let widget = this.widgets[ i ];
14238
- const element = widget.root;
14985
+ let component = this.components[ i ];
14986
+ const element = component.root;
14239
14987
 
14240
14988
  if( element.children.length < 2 )
14241
14989
  {
@@ -14248,10 +14996,10 @@ class Branch {
14248
14996
  name.style.width = size;
14249
14997
  name.style.minWidth = size;
14250
14998
 
14251
- switch( widget.type )
14999
+ switch( component.type )
14252
15000
  {
14253
- case LX.Widget.CUSTOM:
14254
- case LX.Widget.ARRAY:
15001
+ case LX.BaseComponent.CUSTOM:
15002
+ case LX.BaseComponent.ARRAY:
14255
15003
  continue;
14256
15004
  }
14257
15005
  value.style.width = "-moz-calc( 100% - " + size + " )";
@@ -14565,7 +15313,15 @@ class Menubar {
14565
15313
  {
14566
15314
  const data = buttons[ i ];
14567
15315
  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" } );
15316
+ const button = new LX.Button( title, data.label, data.callback, {
15317
+ title,
15318
+ buttonClass: "bg-none",
15319
+ disabled: data.disabled,
15320
+ icon: data.icon,
15321
+ hideName: true,
15322
+ swap: data.swap,
15323
+ iconPosition: "start"
15324
+ } );
14569
15325
  this.buttonContainer.appendChild( button.root );
14570
15326
 
14571
15327
  if( title )
@@ -15139,9 +15895,21 @@ class Sidebar {
15139
15895
  LX.asTooltip( itemDom, key, { side: "right", offset: 16, active: false } );
15140
15896
  }
15141
15897
 
15142
- let itemName = document.createElement( 'a' );
15143
- itemName.innerHTML = key;
15144
- itemDom.appendChild( itemName );
15898
+ LX.makeElement( 'a', "grid-column-start-2", key, itemDom );
15899
+
15900
+ if( options.swap )
15901
+ {
15902
+ itemDom.classList.add( "swap", "inline-grid" );
15903
+ itemDom.querySelector( "a" ).classList.add( "swap-off" );
15904
+
15905
+ const input = document.createElement( "input" );
15906
+ input.className = "p-0 border-0";
15907
+ input.type = "checkbox";
15908
+ itemDom.prepend( input );
15909
+
15910
+ const swapIcon = LX.makeIcon( options.swap, { iconClass: "lexsidebarentryicon swap-on" } );
15911
+ itemDom.appendChild( swapIcon );
15912
+ }
15145
15913
 
15146
15914
  if( options.content )
15147
15915
  {
@@ -15170,8 +15938,14 @@ class Sidebar {
15170
15938
  item.checkbox.set( item.value, true );
15171
15939
  }
15172
15940
 
15941
+ if( options.swap && !( e.target instanceof HTMLInputElement ) )
15942
+ {
15943
+ const swapInput = itemDom.querySelector( "input" );
15944
+ swapInput.checked = !swapInput.checked;
15945
+ }
15946
+
15173
15947
  // Manage selected
15174
- if( this.displaySelected )
15948
+ if( this.displaySelected && !options.skipSelection )
15175
15949
  {
15176
15950
  this.root.querySelectorAll(".lexsidebarentry").forEach( e => e.classList.remove( 'selected' ) );
15177
15951
  entry.classList.add( "selected" );
@@ -15253,6 +16027,14 @@ class Sidebar {
15253
16027
 
15254
16028
  subentry.className = "lexsidebarentry";
15255
16029
  subentry.id = subkey;
16030
+
16031
+ if( suboptions.content )
16032
+ {
16033
+ const parentContainer = LX.makeElement( "div" );
16034
+ parentContainer.appendChild( suboptions.content );
16035
+ subentry.appendChild( parentContainer );
16036
+ }
16037
+
15256
16038
  subentryContainer.appendChild( subentry );
15257
16039
 
15258
16040
  subentry.addEventListener("click", (e) => {
@@ -15261,10 +16043,10 @@ class Sidebar {
15261
16043
  if( f ) f.call( this, subkey, subentry, e );
15262
16044
 
15263
16045
  // Manage selected
15264
- if( this.displaySelected )
16046
+ if( this.displaySelected && !suboptions.skipSelection )
15265
16047
  {
15266
16048
  this.root.querySelectorAll(".lexsidebarentry").forEach( e => e.classList.remove( 'selected' ) );
15267
- entry.classList.add( "selected" );
16049
+ subentry.classList.add( "selected" );
15268
16050
  }
15269
16051
  });
15270
16052
  }
@@ -15283,8 +16065,8 @@ class AssetViewEvent {
15283
16065
  static ASSET_RENAMED = 3;
15284
16066
  static ASSET_CLONED = 4;
15285
16067
  static ASSET_DBLCLICKED = 5;
15286
- static ENTER_FOLDER = 6;
15287
- static ASSET_CHECKED = 7;
16068
+ static ASSET_CHECKED = 6;
16069
+ static ENTER_FOLDER = 7;
15288
16070
 
15289
16071
  constructor( type, item, value ) {
15290
16072
  this.type = type || LX.TreeEvent.NONE;
@@ -15302,8 +16084,8 @@ class AssetViewEvent {
15302
16084
  case AssetViewEvent.ASSET_RENAMED: return "assetview_event_renamed";
15303
16085
  case AssetViewEvent.ASSET_CLONED: return "assetview_event_cloned";
15304
16086
  case AssetViewEvent.ASSET_DBLCLICKED: return "assetview_event_dblclicked";
15305
- case AssetViewEvent.ENTER_FOLDER: return "assetview_event_enter_folder";
15306
16087
  case AssetViewEvent.ASSET_CHECKED: return "assetview_event_checked";
16088
+ case AssetViewEvent.ENTER_FOLDER: return "assetview_event_enter_folder";
15307
16089
  }
15308
16090
  }
15309
16091
  }
@@ -15316,8 +16098,12 @@ LX.AssetViewEvent = AssetViewEvent;
15316
16098
 
15317
16099
  class AssetView {
15318
16100
 
15319
- static LAYOUT_CONTENT = 0;
16101
+ static LAYOUT_GRID = 0;
15320
16102
  static LAYOUT_LIST = 1;
16103
+
16104
+ static CONTENT_SORT_ASC = 0;
16105
+ static CONTENT_SORT_DESC = 1;
16106
+
15321
16107
  static MAX_PAGE_ELEMENTS = 50;
15322
16108
 
15323
16109
  /**
@@ -15326,7 +16112,8 @@ class AssetView {
15326
16112
  constructor( options = {} ) {
15327
16113
 
15328
16114
  this.rootPath = "https://raw.githubusercontent.com/jxarco/lexgui.js/master/";
15329
- this.layout = options.layout ?? AssetView.LAYOUT_CONTENT;
16115
+ this.layout = options.layout ?? AssetView.LAYOUT_GRID;
16116
+ this.sortMode = options.sortMode ?? AssetView.CONTENT_SORT_ASC;
15330
16117
  this.contentPage = 1;
15331
16118
 
15332
16119
  if( options.rootPath )
@@ -15444,9 +16231,9 @@ class AssetView {
15444
16231
  this.leftPanel.clear();
15445
16232
  }
15446
16233
 
15447
- if( this.rightPanel )
16234
+ if( this.toolsPanel )
15448
16235
  {
15449
- this.rightPanel.clear();
16236
+ this.toolsPanel.clear();
15450
16237
  }
15451
16238
  }
15452
16239
 
@@ -15558,7 +16345,7 @@ class AssetView {
15558
16345
  _setContentLayout( layoutMode ) {
15559
16346
 
15560
16347
  this.layout = layoutMode;
15561
-
16348
+ this.toolsPanel.refresh();
15562
16349
  this._refreshContent();
15563
16350
  }
15564
16351
 
@@ -15568,42 +16355,34 @@ class AssetView {
15568
16355
 
15569
16356
  _createContentPanel( area ) {
15570
16357
 
15571
- if( this.rightPanel )
16358
+ if( this.toolsPanel )
15572
16359
  {
15573
- this.rightPanel.clear();
16360
+ this.contentPanel.clear();
15574
16361
  }
15575
16362
  else
15576
16363
  {
15577
- this.rightPanel = area.addPanel({ className: 'lexassetcontentpanel flex flex-col overflow-hidden' });
16364
+ this.toolsPanel = area.addPanel({ className: 'flex flex-col overflow-hidden' });
16365
+ this.contentPanel = area.addPanel({ className: 'lexassetcontentpanel flex flex-col overflow-hidden' });
15578
16366
  }
15579
16367
 
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
- }
16368
+ const _onSort = ( value, event ) => {
16369
+ new LX.DropdownMenu( event.target, [
16370
+ { name: "Name", icon: "ALargeSmall", callback: () => this._sortData( "id" ) },
16371
+ { name: "Type", icon: "Type", callback: () => this._sortData( "type" ) },
16372
+ null,
16373
+ { name: "Ascending", icon: "SortAsc", callback: () => this._sortData( null, AssetView.CONTENT_SORT_ASC ) },
16374
+ { name: "Descending", icon: "SortDesc", callback: () => this._sortData( null, AssetView.CONTENT_SORT_DESC ) }
16375
+ ], { side: "right", align: "start" });
15593
16376
  };
15594
16377
 
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;
16378
+ const _onChangeView = ( value, event ) => {
16379
+ new LX.DropdownMenu( event.target, [
16380
+ { name: "Grid", icon: "LayoutGrid", callback: () => this._setContentLayout( AssetView.LAYOUT_GRID ) },
16381
+ { name: "List", icon: "LayoutList", callback: () => this._setContentLayout( AssetView.LAYOUT_LIST ) }
16382
+ ], { side: "right", align: "start" });
15604
16383
  };
15605
16384
 
15606
- const on_change_page = ( value, event ) => {
16385
+ const _onChangePage = ( value, event ) => {
15607
16386
  if( !this.allowNextPage )
15608
16387
  {
15609
16388
  return;
@@ -15619,64 +16398,71 @@ class AssetView {
15619
16398
  }
15620
16399
  };
15621
16400
 
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();
16401
+ this.toolsPanel.refresh = () => {
16402
+ this.toolsPanel.clear();
16403
+ this.toolsPanel.sameLine();
16404
+ this.toolsPanel.addSelect( "Filter", this.allowedTypes, this.filter ?? this.allowedTypes[ 0 ], v => {
16405
+ this._refreshContent( null, v );
16406
+ }, { width: "30%", minWidth: "128px" } );
16407
+ this.toolsPanel.addText( null, this.searchValue ?? "", v => this._refreshContent.call(this, v, null), { placeholder: "Search assets.." } );
16408
+ this.toolsPanel.addButton( null, "", _onSort.bind(this), { title: "Sort", tooltip: true, icon: ( this.sortMode === AssetView.CONTENT_SORT_ASC ) ? "SortAsc" : "SortDesc" } );
16409
+ this.toolsPanel.addButton( null, "", _onChangeView.bind(this), { title: "View", tooltip: true, icon: ( this.layout === AssetView.LAYOUT_GRID ) ? "LayoutGrid" : "LayoutList" } );
16410
+ // Content Pages
16411
+ this.toolsPanel.addButton( null, "", _onChangePage.bind(this, -1), { title: "Previous Page", icon: "ChevronsLeft", className: "ml-auto" } );
16412
+ this.toolsPanel.addButton( null, "", _onChangePage.bind(this, 1), { title: "Next Page", icon: "ChevronsRight" } );
16413
+ const textString = "Page " + this.contentPage + " / " + ((((this.currentData.length - 1) / AssetView.MAX_PAGE_ELEMENTS )|0) + 1);
16414
+ this.toolsPanel.addText(null, textString, null, {
16415
+ inputClass: "nobg", disabled: true, signal: "@on_page_change", maxWidth: "16ch" }
16416
+ );
15635
16417
 
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 );
16418
+ if( !this.skipBrowser )
16419
+ {
16420
+ this.toolsPanel.addComboButtons( null, [
16421
+ {
16422
+ value: "Left",
16423
+ icon: "ArrowLeft",
16424
+ callback: domEl => {
16425
+ if(!this.prevData.length) return;
16426
+ this.nextData.push( this.currentData );
16427
+ this.currentData = this.prevData.pop();
16428
+ this._refreshContent();
16429
+ this._updatePath( this.currentData );
16430
+ }
16431
+ },
16432
+ {
16433
+ value: "Right",
16434
+ icon: "ArrowRight",
16435
+ callback: domEl => {
16436
+ if(!this.nextData.length) return;
16437
+ this.prevData.push( this.currentData );
16438
+ this.currentData = this.nextData.pop();
16439
+ this._refreshContent();
16440
+ this._updatePath( this.currentData );
16441
+ }
16442
+ },
16443
+ {
16444
+ value: "Refresh",
16445
+ icon: "Refresh",
16446
+ callback: domEl => { this._refreshContent(); }
15660
16447
  }
15661
- },
15662
- {
15663
- value: "Refresh",
15664
- icon: "Refresh",
15665
- callback: domEl => { this._refreshContent(); }
15666
- }
15667
- ], { noSelection: true } );
16448
+ ], { noSelection: true } );
15668
16449
 
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
- });
16450
+ this.toolsPanel.addText(null, this.path.join('/'), null, {
16451
+ inputClass: "nobg", disabled: true, signal: "@on_folder_change",
16452
+ style: { fontWeight: "600", fontSize: "15px" }
16453
+ });
16454
+ }
15673
16455
 
15674
- this.rightPanel.endLine();
15675
- }
16456
+ this.toolsPanel.endLine();
16457
+ };
16458
+
16459
+ this.toolsPanel.refresh();
16460
+
16461
+ // Start content panel
15676
16462
 
15677
16463
  this.content = document.createElement('ul');
15678
16464
  this.content.className = "lexassetscontent";
15679
- this.rightPanel.root.appendChild(this.content);
16465
+ this.contentPanel.attach( this.content );
15680
16466
 
15681
16467
  this.content.addEventListener('dragenter', function( e ) {
15682
16468
  e.preventDefault();
@@ -15699,12 +16485,12 @@ class AssetView {
15699
16485
 
15700
16486
  _refreshContent( searchValue, filter ) {
15701
16487
 
15702
- const isContentLayout = ( this.layout == AssetView.LAYOUT_CONTENT ); // default
16488
+ const isGridLayout = ( this.layout == AssetView.LAYOUT_GRID ); // default
15703
16489
 
15704
16490
  this.filter = filter ?? ( this.filter ?? "None" );
15705
16491
  this.searchValue = searchValue ?? (this.searchValue ?? "");
15706
16492
  this.content.innerHTML = "";
15707
- this.content.className = (isContentLayout ? "lexassetscontent" : "lexassetscontent list");
16493
+ this.content.className = (isGridLayout ? "lexassetscontent" : "lexassetscontent list");
15708
16494
  let that = this;
15709
16495
 
15710
16496
  const _addItem = function(item) {
@@ -15727,7 +16513,7 @@ class AssetView {
15727
16513
 
15728
16514
  itemEl.addEventListener("mousemove", e => {
15729
16515
 
15730
- if( !isContentLayout )
16516
+ if( !isGridLayout )
15731
16517
  {
15732
16518
  return;
15733
16519
  }
@@ -15756,14 +16542,14 @@ class AssetView {
15756
16542
  });
15757
16543
 
15758
16544
  itemEl.addEventListener("mouseenter", () => {
15759
- if( isContentLayout )
16545
+ if( isGridLayout )
15760
16546
  {
15761
16547
  desc.style.display = "unset";
15762
16548
  }
15763
16549
  });
15764
16550
 
15765
16551
  itemEl.addEventListener("mouseleave", () => {
15766
- if( isContentLayout )
16552
+ if( isGridLayout )
15767
16553
  {
15768
16554
  setTimeout( () => {
15769
16555
  desc.style.display = "none";
@@ -15807,11 +16593,11 @@ class AssetView {
15807
16593
  let preview = null;
15808
16594
  const hasImage = item.src && (['png', 'jpg'].indexOf( LX.getExtension( item.src ) ) > -1 || item.src.includes("data:image/") ); // Support b64 image as src
15809
16595
 
15810
- if( hasImage || isFolder || !isContentLayout)
16596
+ if( hasImage || isFolder || !isGridLayout)
15811
16597
  {
15812
16598
  preview = document.createElement('img');
15813
16599
  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");
16600
+ preview.src = (isGridLayout || isFolder ? real_src : that.rootPath + "images/file.png");
15815
16601
  itemEl.appendChild( preview );
15816
16602
  }
15817
16603
  else
@@ -16072,16 +16858,20 @@ class AssetView {
16072
16858
  }
16073
16859
  }
16074
16860
 
16075
- _sortData( sort_by, sort_descending = false ) {
16861
+ _sortData( sortBy, sortMode ) {
16076
16862
 
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']);
16863
+ sortBy = sortBy ?? ( this._lastSortBy ?? 'id' );
16864
+ sortMode = sortMode ?? this.sortMode;
16865
+ const sortDesc = ( sortMode === AssetView.CONTENT_SORT_DESC );
16866
+ this.currentData = this.currentData.sort( ( a, b ) => {
16867
+ var r = sortDesc ? b[ sortBy ].localeCompare( a[ sortBy ] ) : a[ sortBy ].localeCompare( b[ sortBy ] );
16868
+ if( r == 0 ) r = sortDesc ? b['id'].localeCompare( a['id'] ) : a[ 'id' ].localeCompare( b[ 'id' ] );
16081
16869
  return r;
16082
16870
  } );
16083
16871
 
16084
- this._lastSortBy = sort_by;
16872
+ this._lastSortBy = sortBy;
16873
+ this.sortMode = sortMode;
16874
+ this.toolsPanel.refresh();
16085
16875
  this._refreshContent();
16086
16876
  }
16087
16877
 
@@ -16451,4 +17241,4 @@ class Tour {
16451
17241
  }
16452
17242
  LX.Tour = Tour;
16453
17243
 
16454
- export { ADD_CUSTOM_WIDGET, Area, AssetView, AssetViewEvent, Branch, LX, Menubar, Panel, Sidebar, Tour, Widget };
17244
+ export { ADD_CUSTOM_COMPONENT, Area, AssetView, AssetViewEvent, BaseComponent, Branch, LX, Menubar, Panel, Sidebar, Tour };