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
package/build/lexgui.js CHANGED
@@ -14,9 +14,9 @@ console.warn( 'Script _build/lexgui.js_ is depracated and will be removed soon.
14
14
  */
15
15
 
16
16
  const LX = {
17
- version: "0.6.12",
17
+ version: "0.7.1",
18
18
  ready: false,
19
- components: [], // Specific pre-build components
19
+ extensions: [], // Store extensions used
20
20
  signals: {}, // Events and triggers
21
21
  extraCommandbarEntries: [], // User specific entries for command bar
22
22
  activeDraggable: null // Watch for the current active draggable
@@ -210,7 +210,7 @@ function _createCommandbar( root )
210
210
  }
211
211
  else
212
212
  {
213
- for( let c of LX.components )
213
+ for( let c of LX.extensions )
214
214
  {
215
215
  if( !LX[ c ] || !LX[ c ].prototype.onKeyPressed )
216
216
  {
@@ -423,6 +423,7 @@ function _createCommandbar( root )
423
423
  * skipRoot: Skip adding LX root container
424
424
  * skipDefaultArea: Skip creation of main area
425
425
  * layoutMode: Sets page layout mode (document | app)
426
+ * spacingMode: Sets page layout spacing mode (default | compact)
426
427
  */
427
428
 
428
429
  async function init( options = { } )
@@ -474,6 +475,9 @@ async function init( options = { } )
474
475
  } );
475
476
  }
476
477
 
478
+ this.spacingMode = options.spacingMode ?? "default";
479
+ document.documentElement.setAttribute( "data-spacing", this.spacingMode );
480
+
477
481
  this.container.appendChild( this.modal );
478
482
 
479
483
  if( !options.skipRoot )
@@ -518,13 +522,13 @@ async function init( options = { } )
518
522
  this.DEFAULT_SPLITBAR_SIZE = 4;
519
523
  this.OPEN_CONTEXTMENU_ENTRY = 'click';
520
524
 
521
- this.widgetResizeObserver = new ResizeObserver( entries => {
525
+ this.componentResizeObserver = new ResizeObserver( entries => {
522
526
  for ( const entry of entries )
523
527
  {
524
- const widget = entry.target?.jsInstance;
525
- if( widget && widget.onResize )
528
+ const c = entry.target?.jsInstance;
529
+ if( c && c.onResize )
526
530
  {
527
- widget.onResize( entry.contentRect );
531
+ c.onResize( entry.contentRect );
528
532
  }
529
533
  }
530
534
  });
@@ -569,6 +573,19 @@ function setLayoutMode( mode )
569
573
 
570
574
  LX.setLayoutMode = setLayoutMode;
571
575
 
576
+ /**
577
+ * @method setSpacingMode
578
+ * @param {String} mode: default | compact
579
+ */
580
+
581
+ function setSpacingMode( mode )
582
+ {
583
+ this.spacingMode = mode;
584
+ document.documentElement.setAttribute( "data-spacing", this.spacingMode );
585
+ }
586
+
587
+ LX.setSpacingMode = setSpacingMode;
588
+
572
589
  /**
573
590
  * @method setCommandbarState
574
591
  * @param {Boolean} value
@@ -671,7 +688,7 @@ function emit( signalName, value, options = {} )
671
688
 
672
689
  for( let obj of data )
673
690
  {
674
- if( obj instanceof LX.Widget )
691
+ if( obj instanceof LX.BaseComponent )
675
692
  {
676
693
  obj.set( value, options.skipCallback ?? true );
677
694
  }
@@ -751,7 +768,19 @@ class Popover {
751
768
  this.root.dataset["side"] = this.side;
752
769
  this.root.tabIndex = "1";
753
770
  this.root.className = "lexpopover";
754
- LX.root.appendChild( this.root );
771
+
772
+ const refElement = trigger ?? this.reference;
773
+ const nestedDialog = refElement.closest( "dialog" );
774
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
775
+ {
776
+ this._parent = nestedDialog;
777
+ }
778
+ else
779
+ {
780
+ this._parent = LX.root;
781
+ }
782
+
783
+ this._parent.appendChild( this.root );
755
784
 
756
785
  this.root.addEventListener( "keydown", (e) => {
757
786
  if( e.key == "Escape" )
@@ -873,6 +902,13 @@ class Popover {
873
902
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
874
903
  }
875
904
 
905
+ if( this._parent instanceof HTMLDialogElement )
906
+ {
907
+ let parentRect = this._parent.getBoundingClientRect();
908
+ position[ 0 ] -= parentRect.x;
909
+ position[ 1 ] -= parentRect.y;
910
+ }
911
+
876
912
  this.root.style.left = `${ position[ 0 ] }px`;
877
913
  this.root.style.top = `${ position[ 1 ] }px`;
878
914
  }
@@ -946,7 +982,7 @@ class Sheet {
946
982
  this.root.tabIndex = "1";
947
983
  this.root.role = "dialog";
948
984
  this.root.className = "lexsheet fixed z-1000 bg-primary";
949
- LX.root.appendChild( this.root );
985
+ document.body.appendChild( this.root );
950
986
 
951
987
  this.root.addEventListener( "keydown", (e) => {
952
988
  if( e.key == "Escape" )
@@ -987,17 +1023,21 @@ class Sheet {
987
1023
  this.root.style.height = "100%";
988
1024
  break;
989
1025
  case "top":
1026
+ this.root.style.left = 0;
990
1027
  this.root.style.top = 0;
991
1028
  this.root.style.width = "100%";
992
1029
  this.root.style.height = size;
993
1030
  break;
994
1031
  case "bottom":
1032
+ this.root.style.left = 0;
995
1033
  this.root.style.bottom = 0;
996
1034
  this.root.style.width = "100%";
997
1035
  this.root.style.height = size;
998
1036
  break;
999
1037
  }
1000
1038
 
1039
+ document.documentElement.setAttribute( "data-scale", `sheet-${ this.side }` );
1040
+
1001
1041
  this.root.focus();
1002
1042
 
1003
1043
  this._onClick = e => {
@@ -1015,6 +1055,8 @@ class Sheet {
1015
1055
 
1016
1056
  destroy() {
1017
1057
 
1058
+ document.documentElement.setAttribute( "data-scale", "" );
1059
+
1018
1060
  document.body.removeEventListener( "mousedown", this._onClick, true );
1019
1061
  document.body.removeEventListener( "focusin", this._onClick, true );
1020
1062
 
@@ -1064,7 +1106,18 @@ class DropdownMenu {
1064
1106
  this.root.dataset["side"] = this.side;
1065
1107
  this.root.tabIndex = "1";
1066
1108
  this.root.className = "lexdropdownmenu";
1067
- LX.root.appendChild( this.root );
1109
+
1110
+ const nestedDialog = trigger.closest( "dialog" );
1111
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
1112
+ {
1113
+ this._parent = nestedDialog;
1114
+ }
1115
+ else
1116
+ {
1117
+ this._parent = LX.root;
1118
+ }
1119
+
1120
+ this._parent.appendChild( this.root );
1068
1121
 
1069
1122
  this._create( this._items );
1070
1123
 
@@ -1100,7 +1153,7 @@ class DropdownMenu {
1100
1153
  document.body.removeEventListener( "mousedown", this._onClick, true );
1101
1154
  document.body.removeEventListener( "focusin", this._onClick, true );
1102
1155
 
1103
- LX.root.querySelectorAll( ".lexdropdownmenu" ).forEach( m => { m.remove(); } );
1156
+ this._parent.querySelectorAll( ".lexdropdownmenu" ).forEach( m => { m.remove(); } );
1104
1157
 
1105
1158
  DropdownMenu.currentMenu = null;
1106
1159
 
@@ -1125,14 +1178,22 @@ class DropdownMenu {
1125
1178
  newParent.className = "lexdropdownmenu";
1126
1179
  newParent.dataset["id"] = parentDom.dataset["id"];
1127
1180
  newParent.dataset["side"] = "right"; // submenus always come from the right
1128
- LX.root.appendChild( newParent );
1181
+ this._parent.appendChild( newParent );
1129
1182
 
1130
1183
  newParent.currentParent = parentDom;
1131
1184
  parentDom = newParent;
1132
1185
 
1133
1186
  LX.doAsync( () => {
1187
+
1134
1188
  const position = [ parentRect.x + parentRect.width, parentRect.y ];
1135
1189
 
1190
+ if( this._parent instanceof HTMLDialogElement )
1191
+ {
1192
+ let rootParentRect = this._parent.getBoundingClientRect();
1193
+ position[ 0 ] -= rootParentRect.x;
1194
+ position[ 1 ] -= rootParentRect.y;
1195
+ }
1196
+
1136
1197
  if( this.avoidCollisions )
1137
1198
  {
1138
1199
  position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - newParent.offsetWidth - this._windowPadding );
@@ -1148,139 +1209,180 @@ class DropdownMenu {
1148
1209
 
1149
1210
  for( let item of items )
1150
1211
  {
1151
- if( !item )
1152
- {
1153
- this._addSeparator( parentDom );
1154
- continue;
1155
- }
1212
+ this._createItem( item, parentDom, applyIconPadding );
1213
+ }
1214
+ }
1156
1215
 
1157
- const key = item.name ?? item;
1158
- const pKey = LX.getSupportedDOMName( key );
1216
+ _createItem( item, parentDom, applyIconPadding ) {
1159
1217
 
1160
- // Item already created
1161
- if( parentDom.querySelector( "#" + pKey ) )
1162
- {
1163
- continue;
1164
- }
1218
+ if( !item )
1219
+ {
1220
+ this._addSeparator( parentDom );
1221
+ return;
1222
+ }
1165
1223
 
1166
- const menuItem = document.createElement('div');
1167
- menuItem.className = "lexdropdownmenuitem" + ( item.name ? "" : " label" ) + ( item.disabled ?? false ? " disabled" : "" ) + ( ` ${ item.className ?? "" }` );
1168
- menuItem.dataset["id"] = pKey;
1169
- menuItem.innerHTML = `<span>${ key }</span>`;
1170
- menuItem.tabIndex = "1";
1171
- parentDom.appendChild( menuItem );
1224
+ const key = item.name ?? item;
1225
+ const pKey = LX.getSupportedDOMName( key );
1172
1226
 
1173
- if( item.constructor === String ) // Label case
1174
- {
1175
- continue;
1176
- }
1227
+ // Item already created
1228
+ if( parentDom.querySelector( "#" + pKey ) )
1229
+ {
1230
+ return;
1231
+ }
1177
1232
 
1178
- if( item.submenu )
1179
- {
1180
- const submenuIcon = LX.makeIcon( "Right", { svgClass: "sm" } );
1181
- menuItem.appendChild( submenuIcon );
1182
- }
1183
- else if( item.kbd )
1184
- {
1185
- item.kbd = [].concat( item.kbd );
1233
+ const menuItem = document.createElement('div');
1234
+ menuItem.className = "lexdropdownmenuitem" + ( item.name ? "" : " label" ) + ( item.disabled ?? false ? " disabled" : "" ) + ( ` ${ item.className ?? "" }` );
1235
+ menuItem.dataset["id"] = pKey;
1236
+ menuItem.innerHTML = `<span>${ key }</span>`;
1237
+ menuItem.tabIndex = "1";
1238
+ parentDom.appendChild( menuItem );
1186
1239
 
1187
- const kbd = LX.makeKbd( item.kbd );
1188
- menuItem.appendChild( kbd );
1240
+ if( item.constructor === String ) // Label case
1241
+ {
1242
+ return;
1243
+ }
1189
1244
 
1190
- document.addEventListener( "keydown", e => {
1191
- if( !this._trigger.ddm ) return;
1192
- e.preventDefault();
1193
- // Check if it's a letter or other key
1194
- let kdbKey = item.kbd.join("");
1195
- kdbKey = kdbKey.length == 1 ? kdbKey.toLowerCase() : kdbKey;
1196
- if( kdbKey == e.key )
1197
- {
1198
- menuItem.click();
1199
- }
1200
- } );
1201
- }
1245
+ if( item.submenu )
1246
+ {
1247
+ const submenuIcon = LX.makeIcon( "Right", { svgClass: "sm" } );
1248
+ menuItem.appendChild( submenuIcon );
1249
+ }
1250
+ else if( item.kbd )
1251
+ {
1252
+ item.kbd = [].concat( item.kbd );
1202
1253
 
1203
- const disabled = item.disabled ?? false;
1254
+ const kbd = LX.makeKbd( item.kbd );
1255
+ menuItem.appendChild( kbd );
1204
1256
 
1205
- if( item.icon )
1206
- {
1207
- const icon = LX.makeIcon( item.icon, { svgClass: disabled ? "fg-tertiary" : item.className } );
1208
- menuItem.prepend( icon );
1209
- }
1210
- else if( item.checked == undefined && applyIconPadding ) // no checkbox, no icon, apply padding if there's checkbox or icon in other items
1211
- {
1212
- menuItem.classList.add( "pl-8" );
1213
- }
1257
+ document.addEventListener( "keydown", e => {
1258
+ if( !this._trigger.ddm ) return;
1259
+ e.preventDefault();
1260
+ // Check if it's a letter or other key
1261
+ let kdbKey = item.kbd.join("");
1262
+ kdbKey = kdbKey.length == 1 ? kdbKey.toLowerCase() : kdbKey;
1263
+ if( kdbKey == e.key )
1264
+ {
1265
+ menuItem.click();
1266
+ }
1267
+ } );
1268
+ }
1214
1269
 
1215
- if( disabled )
1216
- {
1217
- continue;
1218
- }
1270
+ const disabled = item.disabled ?? false;
1219
1271
 
1220
- if( item.checked != undefined )
1272
+ if( this._radioGroup !== undefined )
1273
+ {
1274
+ if( item.name === this._radioGroup.selected )
1221
1275
  {
1222
- const checkbox = new LX.Checkbox( pKey + "_entryChecked", item.checked, (v) => {
1223
- const f = item[ 'callback' ];
1224
- item.checked = v;
1225
- if( f )
1226
- {
1227
- f.call( this, key, v, menuItem );
1228
- }
1229
- }, { className: "accent" });
1230
- const input = checkbox.root.querySelector( "input" );
1231
- input.classList.add( "ml-auto" );
1232
- menuItem.appendChild( input );
1233
-
1234
- menuItem.addEventListener( "click", (e) => {
1235
- if( e.target.type == "checkbox" ) return;
1236
- input.checked = !input.checked;
1237
- checkbox.set( input.checked );
1238
- } );
1276
+ const icon = LX.makeIcon( "Circle", { svgClass: "xxs fill-current" } );
1277
+ menuItem.prepend( icon );
1239
1278
  }
1240
- else
1241
- {
1242
- menuItem.addEventListener( "click", () => {
1243
- const f = item[ 'callback' ];
1244
- if( f )
1245
- {
1246
- f.call( this, key, menuItem );
1247
- }
1248
1279
 
1249
- this.destroy( true );
1250
- } );
1251
- }
1280
+ menuItem.setAttribute( "data-radioname", this._radioGroup.name );
1281
+ }
1282
+ else if( item.icon )
1283
+ {
1284
+ const icon = LX.makeIcon( item.icon, { svgClass: disabled ? "fg-tertiary" : item.className } );
1285
+ menuItem.prepend( icon );
1286
+ }
1287
+ else if( item.checked == undefined && applyIconPadding ) // no checkbox, no icon, apply padding if there's checkbox or icon in other items
1288
+ {
1289
+ menuItem.classList.add( "pl-8" );
1290
+ }
1252
1291
 
1253
- menuItem.addEventListener("mouseover", e => {
1292
+ if( disabled )
1293
+ {
1294
+ return;
1295
+ }
1254
1296
 
1255
- let path = menuItem.dataset["id"];
1256
- let p = parentDom;
1297
+ if( item.checked != undefined )
1298
+ {
1299
+ const checkbox = new LX.Checkbox( pKey + "_entryChecked", item.checked, (v) => {
1300
+ const f = item[ 'callback' ];
1301
+ item.checked = v;
1302
+ if( f )
1303
+ {
1304
+ f.call( this, key, v, menuItem );
1305
+ }
1306
+ }, { className: "accent" });
1307
+ const input = checkbox.root.querySelector( "input" );
1308
+ input.classList.add( "ml-auto" );
1309
+ menuItem.appendChild( input );
1310
+
1311
+ menuItem.addEventListener( "click", (e) => {
1312
+ if( e.target.type == "checkbox" ) return;
1313
+ input.checked = !input.checked;
1314
+ checkbox.set( input.checked );
1315
+ } );
1316
+ }
1317
+ else
1318
+ {
1319
+ menuItem.addEventListener( "click", () => {
1320
+ const f = item[ 'callback' ];
1321
+ if( f )
1322
+ {
1323
+ f.call( this, key, menuItem );
1324
+ }
1257
1325
 
1258
- while( p )
1326
+ const radioName = menuItem.getAttribute( "data-radioname" );
1327
+ if( radioName )
1259
1328
  {
1260
- path += "/" + p.dataset["id"];
1261
- p = p.currentParent?.parentElement;
1329
+ this._trigger[ radioName ] = key;
1262
1330
  }
1263
1331
 
1264
- LX.root.querySelectorAll( ".lexdropdownmenu" ).forEach( m => {
1265
- if( !path.includes( m.dataset["id"] ) )
1266
- {
1267
- m.currentParent.built = false;
1268
- m.remove();
1269
- }
1270
- } );
1332
+ this.destroy( true );
1333
+ } );
1334
+ }
1335
+
1336
+ menuItem.addEventListener("mouseover", e => {
1337
+
1338
+ let path = menuItem.dataset["id"];
1339
+ let p = parentDom;
1340
+
1341
+ while( p )
1342
+ {
1343
+ path += "/" + p.dataset["id"];
1344
+ p = p.currentParent?.parentElement;
1345
+ }
1271
1346
 
1272
- if( item.submenu && this.inPlace )
1347
+ this._parent.querySelectorAll( ".lexdropdownmenu" ).forEach( m => {
1348
+ if( !path.includes( m.dataset["id"] ) )
1273
1349
  {
1274
- if( menuItem.built )
1275
- {
1276
- return;
1277
- }
1278
- menuItem.built = true;
1279
- this._create( item.submenu, menuItem );
1350
+ m.currentParent.built = false;
1351
+ m.remove();
1280
1352
  }
1353
+ } );
1281
1354
 
1282
- e.stopPropagation();
1283
- });
1355
+ if( item.submenu && this.inPlace )
1356
+ {
1357
+ if( menuItem.built )
1358
+ {
1359
+ return;
1360
+ }
1361
+ menuItem.built = true;
1362
+ this._create( item.submenu, menuItem );
1363
+ }
1364
+
1365
+ e.stopPropagation();
1366
+ });
1367
+
1368
+ if( item.options )
1369
+ {
1370
+ this._addSeparator();
1371
+
1372
+ console.assert( this._trigger[ item.name ] && "An item of the radio group must be selected!" );
1373
+ this._radioGroup = {
1374
+ name: item.name,
1375
+ selected: this._trigger[ item.name ]
1376
+ };
1377
+
1378
+ for( let o of item.options )
1379
+ {
1380
+ this._createItem( o, parentDom, applyIconPadding );
1381
+ }
1382
+
1383
+ delete this._radioGroup;
1384
+
1385
+ this._addSeparator();
1284
1386
  }
1285
1387
  }
1286
1388
 
@@ -1340,6 +1442,13 @@ class DropdownMenu {
1340
1442
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
1341
1443
  }
1342
1444
 
1445
+ if( this._parent instanceof HTMLDialogElement )
1446
+ {
1447
+ let parentRect = this._parent.getBoundingClientRect();
1448
+ position[ 0 ] -= parentRect.x;
1449
+ position[ 1 ] -= parentRect.y;
1450
+ }
1451
+
1343
1452
  this.root.style.left = `${ position[ 0 ] }px`;
1344
1453
  this.root.style.top = `${ position[ 1 ] }px`;
1345
1454
  this.inPlace = true;
@@ -1600,25 +1709,25 @@ class ColorPicker {
1600
1709
  this._updateColorValue( null, true );
1601
1710
  } ).root );
1602
1711
 
1603
- this.labelWidget = new LX.TextInput( null, "", null, { inputClass: "bg-none", fit: true, disabled: true } );
1604
- colorLabel.appendChild( this.labelWidget.root );
1712
+ this.labelComponent = new LX.TextInput( null, "", null, { inputClass: "bg-none", fit: true, disabled: true } );
1713
+ colorLabel.appendChild( this.labelComponent.root );
1605
1714
 
1606
1715
  // Copy button
1607
1716
  {
1608
- const copyButtonWidget = new LX.Button(null, "copy", async () => {
1609
- navigator.clipboard.writeText( this.labelWidget.value() );
1610
- copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "none";
1717
+ const copyButtonComponent = new LX.Button(null, "copy", async () => {
1718
+ navigator.clipboard.writeText( this.labelComponent.value() );
1719
+ copyButtonComponent.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "none";
1611
1720
 
1612
1721
  LX.doAsync( () => {
1613
- copyButtonWidget.swap( true );
1614
- copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "auto";
1722
+ copyButtonComponent.swap( true );
1723
+ copyButtonComponent.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "auto";
1615
1724
  }, 3000 );
1616
1725
 
1617
1726
  }, { swap: "Check", icon: "Copy", buttonClass: "bg-none", className: "ml-auto", title: "Copy" } );
1618
1727
 
1619
- copyButtonWidget.root.querySelector( ".swap-on svg" ).addClass( "fg-success" );
1728
+ copyButtonComponent.root.querySelector( ".swap-on svg" ).addClass( "fg-success" );
1620
1729
 
1621
- colorLabel.appendChild( copyButtonWidget.root );
1730
+ colorLabel.appendChild( copyButtonComponent.root );
1622
1731
  }
1623
1732
 
1624
1733
  this._updateColorValue( hexValue, true );
@@ -1673,25 +1782,25 @@ class ColorPicker {
1673
1782
  if( this.colorModel == "CSS" )
1674
1783
  {
1675
1784
  const { r, g, b, a } = this.currentColor.css;
1676
- this.labelWidget.set( `rgb${ this.useAlpha ? 'a' : '' }(${ r },${ g },${ b }${ this.useAlpha ? ',' + toFixed( a ) : '' })` );
1785
+ this.labelComponent.set( `rgb${ this.useAlpha ? 'a' : '' }(${ r },${ g },${ b }${ this.useAlpha ? ',' + toFixed( a ) : '' })` );
1677
1786
  }
1678
1787
  else if( this.colorModel == "Hex" )
1679
1788
  {
1680
- this.labelWidget.set( ( this.useAlpha ? this.currentColor.hex : this.currentColor.hex.substr( 0, 7 ) ).toUpperCase() );
1789
+ this.labelComponent.set( ( this.useAlpha ? this.currentColor.hex : this.currentColor.hex.substr( 0, 7 ) ).toUpperCase() );
1681
1790
  }
1682
1791
  else if( this.colorModel == "HSV" )
1683
1792
  {
1684
1793
  const { h, s, v, a } = this.currentColor.hsv;
1685
1794
  const components = [ Math.floor( h ) + 'º', Math.floor( s * 100 ) + '%', Math.floor( v * 100 ) + '%' ];
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
  else // RGB
1690
1799
  {
1691
1800
  const { r, g, b, a } = this.currentColor.rgb;
1692
1801
  const components = [ toFixed( r ), toFixed( g ), toFixed( b ) ];
1693
1802
  if( this.useAlpha ) components.push( toFixed( a ) );
1694
- this.labelWidget.set( components.join( ' ' ) );
1803
+ this.labelComponent.set( components.join( ' ' ) );
1695
1804
  }
1696
1805
  }
1697
1806
 
@@ -1855,8 +1964,6 @@ class Calendar {
1855
1964
  let fromRangeDate = this.range ? LX.dateFromDateString( this.range[ 0 ] ) : null;
1856
1965
  let toRangeDate = this.range ? LX.dateFromDateString( this.range[ 1 ] ) : null;
1857
1966
 
1858
- this.currentDate ? new Date( `${ this.currentDate.month }/${ this.currentDate.day }/${ this.currentDate.year }` ) : null;
1859
-
1860
1967
  for( let week = 0; week < 6; week++ )
1861
1968
  {
1862
1969
  const hrow = document.createElement( 'tr' );
@@ -2007,6 +2114,13 @@ class Calendar {
2007
2114
  this.refresh();
2008
2115
  }
2009
2116
 
2117
+ setMonth( month ) {
2118
+
2119
+ this.month = month;
2120
+
2121
+ this.fromMonthYear( this.month, this.year );
2122
+ }
2123
+
2010
2124
  _getOrdinalSuffix( day ) {
2011
2125
  if ( day > 3 && day < 21 ) return "th";
2012
2126
  switch ( day % 10 )
@@ -2035,7 +2149,8 @@ class CalendarRange {
2035
2149
 
2036
2150
  console.assert( range && range.constructor === Array, "Range cannot be empty and has to be an Array!" );
2037
2151
 
2038
- let mustAdvanceMonth = false;
2152
+ let mustSetMonth = null;
2153
+ let dateReversed = false;
2039
2154
 
2040
2155
  // Fix any issues with date range picking
2041
2156
  {
@@ -2047,9 +2162,10 @@ class CalendarRange {
2047
2162
  const tmp = range[ 0 ];
2048
2163
  range[ 0 ] = range[ 1 ];
2049
2164
  range[ 1 ] = tmp;
2165
+ dateReversed = true;
2050
2166
  }
2051
2167
 
2052
- mustAdvanceMonth = ( t0.getMonth() == t1.getMonth() ) && ( t0.getFullYear() == t1.getFullYear() );
2168
+ mustSetMonth = (dateReversed ? t1.getMonth() : t0.getMonth() ) + 2; // +1 to convert range, +1 to use next month
2053
2169
  }
2054
2170
 
2055
2171
  this.from = range[ 0 ];
@@ -2102,10 +2218,8 @@ class CalendarRange {
2102
2218
  range
2103
2219
  });
2104
2220
 
2105
- if( mustAdvanceMonth )
2106
- {
2107
- this.toCalendar._nextMonth( true );
2108
- }
2221
+ console.assert( mustSetMonth && "New Month must be valid" );
2222
+ this.toCalendar.setMonth( mustSetMonth );
2109
2223
 
2110
2224
  this.root.appendChild( this.fromCalendar.root );
2111
2225
  this.root.appendChild( this.toCalendar.root );
@@ -2329,6 +2443,19 @@ class Tabs {
2329
2443
  tabEl.instance = this;
2330
2444
  contentEl.id = tabEl.id + "_content";
2331
2445
 
2446
+ if( options.badge )
2447
+ {
2448
+ const asChild = options.badge.asChild ?? false;
2449
+ const badgeOptions = { };
2450
+
2451
+ if( asChild )
2452
+ {
2453
+ badgeOptions.parent = tabEl;
2454
+ }
2455
+
2456
+ tabEl.innerHTML += LX.badge( options.badge.content ?? "", options.badge.className, badgeOptions );
2457
+ }
2458
+
2332
2459
  if( tabEl.selected )
2333
2460
  {
2334
2461
  this.selected = name;
@@ -2601,7 +2728,7 @@ class Dialog {
2601
2728
 
2602
2729
  if( !callback )
2603
2730
  {
2604
- console.warn("Content is empty, add some widgets using 'callback' parameter!");
2731
+ console.warn("Content is empty, add some components using 'callback' parameter!");
2605
2732
  }
2606
2733
 
2607
2734
  this._oncreate = callback;
@@ -2616,6 +2743,7 @@ class Dialog {
2616
2743
  let root = document.createElement('dialog');
2617
2744
  root.className = "lexdialog " + (options.className ?? "");
2618
2745
  root.id = options.id ?? "dialog" + Dialog._last_id++;
2746
+ root.dataset["modal"] = modal;
2619
2747
  LX.root.appendChild( root );
2620
2748
 
2621
2749
  LX.doAsync( () => {
@@ -2669,9 +2797,9 @@ class Dialog {
2669
2797
  const panelChildCount = panel.root.childElementCount;
2670
2798
 
2671
2799
  const branch = panel.branch( data.name, { closed: data.closed } );
2672
- branch.widgets = data.widgets;
2800
+ branch.components = data.components;
2673
2801
 
2674
- for( let w of branch.widgets )
2802
+ for( let w of branch.components )
2675
2803
  {
2676
2804
  branch.content.appendChild( w.root );
2677
2805
  }
@@ -2915,9 +3043,6 @@ class ContextMenu {
2915
3043
 
2916
3044
  this.root = document.createElement( "div" );
2917
3045
  this.root.className = "lexcontextmenu";
2918
- this.root.style.left = ( event.x - 48 ) + "px";
2919
- this.root.style.top = ( event.y - 8 ) + "px";
2920
-
2921
3046
  this.root.addEventListener("mouseleave", function() {
2922
3047
  this.remove();
2923
3048
  });
@@ -2933,31 +3058,57 @@ class ContextMenu {
2933
3058
  item[ "icon" ] = options.icon;
2934
3059
  this.items.push( item );
2935
3060
  }
3061
+
3062
+ const nestedDialog = event.target.closest( "dialog" );
3063
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
3064
+ {
3065
+ this._parent = nestedDialog;
3066
+ }
3067
+ else
3068
+ {
3069
+ this._parent = LX.root;
3070
+ }
3071
+
3072
+ this._parent.appendChild( this.root );
3073
+
3074
+ // Set position based on parent
3075
+ const position = [ event.x - 48, event.y - 8 ];
3076
+ if( this._parent instanceof HTMLDialogElement )
3077
+ {
3078
+ let parentRect = this._parent.getBoundingClientRect();
3079
+ position[ 0 ] -= parentRect.x;
3080
+ position[ 1 ] -= parentRect.y;
3081
+ }
3082
+
3083
+ this.root.style.left = `${ position[ 0 ] }px`;
3084
+ this.root.style.top = `${ position[ 1 ] }px`;
2936
3085
  }
2937
3086
 
2938
3087
  _adjustPosition( div, margin, useAbsolute = false ) {
2939
3088
 
2940
3089
  let rect = div.getBoundingClientRect();
3090
+ let left = parseInt( div.style.left );
3091
+ let top = parseInt( div.style.top );
2941
3092
 
2942
3093
  if( !useAbsolute )
2943
3094
  {
2944
3095
  let width = rect.width;
2945
3096
  if( rect.left < 0 )
2946
3097
  {
2947
- div.style.left = margin + "px";
3098
+ left = margin;
2948
3099
  }
2949
3100
  else if( window.innerWidth - rect.right < 0 )
2950
3101
  {
2951
- div.style.left = (window.innerWidth - width - margin) + "px";
3102
+ left = (window.innerWidth - width - margin);
2952
3103
  }
2953
3104
 
2954
3105
  if( rect.top < 0 )
2955
3106
  {
2956
- div.style.top = margin + "px";
3107
+ top = margin;
2957
3108
  }
2958
3109
  else if( (rect.top + rect.height) > window.innerHeight )
2959
3110
  {
2960
- div.style.top = (window.innerHeight - rect.height - margin) + "px";
3111
+ top = (window.innerHeight - rect.height - margin);
2961
3112
  }
2962
3113
  }
2963
3114
  else
@@ -2965,15 +3116,18 @@ class ContextMenu {
2965
3116
  let dt = window.innerWidth - rect.right;
2966
3117
  if( dt < 0 )
2967
3118
  {
2968
- div.style.left = div.offsetLeft + (dt - margin) + "px";
3119
+ left = div.offsetLeft + (dt - margin);
2969
3120
  }
2970
3121
 
2971
3122
  dt = window.innerHeight - (rect.top + rect.height);
2972
3123
  if( dt < 0 )
2973
3124
  {
2974
- div.style.top = div.offsetTop + (dt - margin + 20 ) + "px";
3125
+ top = div.offsetTop + (dt - margin + 20 );
2975
3126
  }
2976
3127
  }
3128
+
3129
+ div.style.left = `${ left }px`;
3130
+ div.style.top = `${ top }px`;
2977
3131
  }
2978
3132
 
2979
3133
  _createSubmenu( o, k, c, d ) {
@@ -3195,7 +3349,6 @@ LX.ContextMenu = ContextMenu;
3195
3349
  function addContextMenu( title, event, callback, options )
3196
3350
  {
3197
3351
  const menu = new ContextMenu( event, title, options );
3198
- LX.root.appendChild( menu.root );
3199
3352
 
3200
3353
  if( callback )
3201
3354
  {
@@ -4311,6 +4464,43 @@ class CanvasMap2D {
4311
4464
 
4312
4465
  LX.CanvasMap2D = CanvasMap2D;
4313
4466
 
4467
+ class Skeleton {
4468
+
4469
+ constructor( elements ) {
4470
+
4471
+ this.root = LX.makeContainer( [ "auto", "auto" ], "flex flex-row lexskeleton" );
4472
+
4473
+ if( elements.constructor === String )
4474
+ {
4475
+ this.root.innerHTML = elements;
4476
+ }
4477
+ else
4478
+ {
4479
+ // Force array
4480
+ elements = [].concat( elements );
4481
+
4482
+ for( let e of elements )
4483
+ {
4484
+ this.root.appendChild( e );
4485
+ }
4486
+ }
4487
+ }
4488
+
4489
+ destroy() {
4490
+
4491
+ this.root.dataset[ "closed" ] = true;
4492
+
4493
+ LX.doAsync( () => {
4494
+ this.root.remove();
4495
+ this.root = null;
4496
+ }, 200 );
4497
+ }
4498
+ }
4499
+
4500
+ LX.Skeleton = Skeleton;
4501
+
4502
+ // Js native overrides
4503
+
4314
4504
  Object.defineProperty(String.prototype, 'lastChar', {
4315
4505
  get: function() { return this[ this.length - 1 ]; },
4316
4506
  enumerable: true,
@@ -4611,6 +4801,28 @@ function deleteElement( element )
4611
4801
 
4612
4802
  LX.deleteElement = deleteElement;
4613
4803
 
4804
+ /**
4805
+ * @method toCamelCase
4806
+ * @param {String} str
4807
+ */
4808
+ function toCamelCase( str )
4809
+ {
4810
+ return str.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase());
4811
+ }
4812
+
4813
+ LX.toCamelCase = toCamelCase;
4814
+
4815
+ /**
4816
+ * @method toTitleCase
4817
+ * @param {String} str
4818
+ */
4819
+ function toTitleCase( str )
4820
+ {
4821
+ return str.replace(/-/g, " ").toLowerCase().replace(/\b\w/g, char => char.toUpperCase());
4822
+ }
4823
+
4824
+ LX.toTitleCase = toTitleCase;
4825
+
4614
4826
  /**
4615
4827
  * @method getSupportedDOMName
4616
4828
  * @description Convert a text string to a valid DOM name
@@ -4639,12 +4851,12 @@ LX.getSupportedDOMName = getSupportedDOMName;
4639
4851
 
4640
4852
  /**
4641
4853
  * @method has
4642
- * @description Ask if LexGUI is using a specific component
4643
- * @param {String} componentName Name of the LexGUI component
4854
+ * @description Ask if LexGUI is using a specific extension
4855
+ * @param {String} extensionName Name of the LexGUI extension
4644
4856
  */
4645
- function has( componentName )
4857
+ function has( extensionName )
4646
4858
  {
4647
- return ( LX.components.indexOf( componentName ) > -1 );
4859
+ return ( LX.extensions.indexOf( extensionName ) > -1 );
4648
4860
  }
4649
4861
 
4650
4862
  LX.has = has;
@@ -4821,6 +5033,19 @@ function getThemeColor( colorName )
4821
5033
 
4822
5034
  LX.getThemeColor = getThemeColor;
4823
5035
 
5036
+ /**
5037
+ * @method switchSpacing
5038
+ * @description Toggles between "default" and "compact" spacing layouts
5039
+ */
5040
+ function switchSpacing()
5041
+ {
5042
+ const currentSpacing = document.documentElement.getAttribute( "data-spacing" ) ?? "default";
5043
+ document.documentElement.setAttribute( "data-spacing", ( currentSpacing == "default" ) ? "compact" : "default" );
5044
+ LX.emit( "@on_new_spacing_layout", currentSpacing );
5045
+ }
5046
+
5047
+ LX.switchSpacing = switchSpacing;
5048
+
4824
5049
  /**
4825
5050
  * @method getBase64Image
4826
5051
  * @description Convert an image to a base64 string
@@ -5362,6 +5587,80 @@ function makeKbd( keys, useSpecialKeys = true, extraClass = "" )
5362
5587
 
5363
5588
  LX.makeKbd = makeKbd;
5364
5589
 
5590
+ /**
5591
+ * @method makeBreadcrumb
5592
+ * @description Displays the path to the current resource using a hierarchy
5593
+ * @param {Array} items
5594
+ * @param {Object} options
5595
+ * maxItems: Max items until ellipsis is used to overflow
5596
+ * separatorIcon: Customize separator icon
5597
+ */
5598
+ function makeBreadcrumb( items, options = {} )
5599
+ {
5600
+ const breadcrumb = LX.makeContainer( ["auto", "auto"], "flex flex-row gap-1" );
5601
+
5602
+ const separatorIcon = options.separatorIcon ?? "ChevronRight";
5603
+ const maxItems = options.maxItems ?? 4;
5604
+ const eraseNum = items.length - maxItems;
5605
+ if( eraseNum > 0 )
5606
+ {
5607
+ const erased = items.splice( 1, eraseNum + 1 );
5608
+ const ellipsisItem = { title: "...", ellipsis: erased.map( v => v.title ).join( "/" ) };
5609
+ items.splice( 1, 0, ellipsisItem );
5610
+ }
5611
+
5612
+ for( let i = 0; i < items.length; ++i )
5613
+ {
5614
+ const item = items[ i ];
5615
+ console.assert( item.title, "Breadcrumb item must have a title!" );
5616
+
5617
+ if( i != 0 )
5618
+ {
5619
+ const icon = LX.makeIcon( separatorIcon, { svgClass: "sm fg-secondary separator" } );
5620
+ breadcrumb.appendChild( icon );
5621
+ }
5622
+
5623
+ const lastElement = ( i == items.length - 1 );
5624
+ const breadcrumbItem = LX.makeContainer( ["auto", "auto"], `p-1 flex flex-row gap-1 items-center ${ lastElement ? "" : "fg-secondary" }` );
5625
+ breadcrumb.appendChild( breadcrumbItem );
5626
+
5627
+ let itemTitle = LX.makeElement( "p", "", item.title );
5628
+ if( item.icon )
5629
+ {
5630
+ breadcrumbItem.appendChild( LX.makeIcon( item.icon, { svgClass: "sm" } ) );
5631
+ }
5632
+
5633
+ if( item.items !== undefined )
5634
+ {
5635
+ const bDropdownTrigger = LX.makeContainer( ["auto", "auto"], `${ lastElement ? "" : "fg-secondary" }` );
5636
+ bDropdownTrigger.listen( "click", (e) => {
5637
+ new LX.DropdownMenu( e.target, item.items, { side: "bottom", align: "start" });
5638
+ } );
5639
+ bDropdownTrigger.append( itemTitle );
5640
+ breadcrumbItem.appendChild( bDropdownTrigger );
5641
+ }
5642
+ else if( item.url !== undefined )
5643
+ {
5644
+ let itemUrl = LX.makeElement( "a", `decoration-none fg-${ lastElement ? "primary" : "secondary" }`, "", breadcrumbItem );
5645
+ itemUrl.href = item.url;
5646
+ itemUrl.appendChild( itemTitle );
5647
+ }
5648
+ else
5649
+ {
5650
+ breadcrumbItem.appendChild( itemTitle );
5651
+ }
5652
+
5653
+ if( item.ellipsis )
5654
+ {
5655
+ LX.asTooltip( breadcrumbItem, item.ellipsis, { side: "bottom", offset: 4 } );
5656
+ }
5657
+ }
5658
+
5659
+ return breadcrumb;
5660
+ }
5661
+
5662
+ LX.makeBreadcrumb = makeBreadcrumb;
5663
+
5365
5664
  /**
5366
5665
  * @method makeIcon
5367
5666
  * @description Gets an SVG element using one of LX.ICONS
@@ -5678,6 +5977,7 @@ LX.prompt = prompt;
5678
5977
  * @param {String} title
5679
5978
  * @param {String} description (Optional)
5680
5979
  * @param {Object} options
5980
+ * position: Set new position for the toasts ("top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right")
5681
5981
  * action: Data of the custom action { name, callback }
5682
5982
  * closable: Allow closing the toast
5683
5983
  * timeout: Time in which the toast closed automatically, in ms. -1 means persistent. [3000]
@@ -5694,9 +5994,48 @@ function toast( title, description, options = {} )
5694
5994
 
5695
5995
  const toast = document.createElement( "li" );
5696
5996
  toast.className = "lextoast";
5697
- toast.style.translate = "0 calc(100% + 30px)";
5698
5997
  this.notifications.prepend( toast );
5699
5998
 
5999
+ const [ positionVertical, positionHorizontal ] = options.position ? options.position.split( "-" ) : [ "bottom", "right" ];
6000
+
6001
+ // Reset style
6002
+ this.notifications.style.right = "unset";
6003
+ this.notifications.style.left = "unset";
6004
+ this.notifications.style.top = "unset";
6005
+ this.notifications.style.bottom = "unset";
6006
+ this.notifications.style.placeSelf = "unset";
6007
+
6008
+ switch( positionVertical )
6009
+ {
6010
+ case "top":
6011
+ toast.style.translate = "0 -30px";
6012
+ this.notifications.style.top = "1rem";
6013
+ this.notifications.style.flexDirection = "column";
6014
+ break;
6015
+ case "bottom":
6016
+ toast.style.translate = "0 calc(100% + 30px)";
6017
+ this.notifications.style.top = "auto";
6018
+ this.notifications.style.bottom = "1rem";
6019
+ this.notifications.style.flexDirection = "column-reverse";
6020
+ break;
6021
+ }
6022
+
6023
+ switch( positionHorizontal )
6024
+ {
6025
+ case "left":
6026
+ this.notifications.style.left = "1rem";
6027
+ break;
6028
+ case "center":
6029
+ this.notifications.style.placeSelf = "center";
6030
+ break;
6031
+ case "right":
6032
+ this.notifications.style.right = "1rem";
6033
+ break;
6034
+ }
6035
+
6036
+ toast.classList.add( positionVertical );
6037
+ toast.classList.add( positionHorizontal );
6038
+
5700
6039
  LX.doAsync( () => {
5701
6040
 
5702
6041
  if( this.notifications.offsetWidth > this.notifications.iWidth )
@@ -5735,7 +6074,7 @@ function toast( title, description, options = {} )
5735
6074
  const that = this;
5736
6075
 
5737
6076
  toast.close = function() {
5738
- this.dataset[ "closed" ] = true;
6077
+ this.dataset[ "open" ] = false;
5739
6078
  LX.doAsync( () => {
5740
6079
  this.remove();
5741
6080
  if( !that.notifications.childElementCount )
@@ -5781,7 +6120,32 @@ function badge( text, className, options = {} )
5781
6120
  const container = document.createElement( "div" );
5782
6121
  container.innerHTML = text;
5783
6122
  container.className = "lexbadge " + ( className ?? "" );
6123
+
6124
+ if( options.chip )
6125
+ {
6126
+ container.classList.add( "chip" );
6127
+ }
6128
+
5784
6129
  Object.assign( container.style, options.style ?? {} );
6130
+
6131
+ if( options.callback )
6132
+ {
6133
+ const arrowIcon = LX.makeIcon( "ArrowUpRight", { svgClass: "xs fg-contrast" } );
6134
+ arrowIcon.querySelector("svg").style.marginLeft = "-0.25rem";
6135
+ container.innerHTML += arrowIcon.innerHTML;
6136
+ container.addEventListener( "click", e => {
6137
+ e.preventDefault();
6138
+ e.stopPropagation();
6139
+ options.callback();
6140
+ } );
6141
+ }
6142
+
6143
+ if( options.parent )
6144
+ {
6145
+ options.parent.classList.add( "lexbadge-parent" );
6146
+ options.parent.appendChild( container );
6147
+ }
6148
+
5785
6149
  return ( options.asElement ?? false ) ? container : container.outerHTML;
5786
6150
  }
5787
6151
 
@@ -5846,7 +6210,10 @@ LX.makeContainer = makeContainer;
5846
6210
  * @param {Object} options
5847
6211
  * side: Side of the tooltip
5848
6212
  * offset: Tooltip margin offset
6213
+ * offsetX: Tooltip margin horizontal offset
6214
+ * offsetY: Tooltip margin vertical offset
5849
6215
  * active: Tooltip active by default [true]
6216
+ * callback: Callback function to execute when the tooltip is shown
5850
6217
  */
5851
6218
 
5852
6219
  function asTooltip( trigger, content, options = {} )
@@ -5856,6 +6223,11 @@ function asTooltip( trigger, content, options = {} )
5856
6223
  trigger.dataset[ "disableTooltip" ] = !( options.active ?? true );
5857
6224
 
5858
6225
  let tooltipDom = null;
6226
+ let tooltipParent = LX.root;
6227
+
6228
+ const _offset = options.offset ?? 6;
6229
+ const _offsetX = options.offsetX ?? _offset;
6230
+ const _offsetY = options.offsetY ?? _offset;
5859
6231
 
5860
6232
  trigger.addEventListener( "mouseenter", function(e) {
5861
6233
 
@@ -5864,51 +6236,72 @@ function asTooltip( trigger, content, options = {} )
5864
6236
  return;
5865
6237
  }
5866
6238
 
5867
- LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
5868
-
5869
6239
  tooltipDom = document.createElement( "div" );
5870
6240
  tooltipDom.className = "lextooltip";
5871
- tooltipDom.innerHTML = content;
6241
+ tooltipDom.innerHTML = trigger.dataset[ "tooltipContent" ] ?? content;
6242
+
6243
+ const nestedDialog = trigger.closest( "dialog" );
6244
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
6245
+ {
6246
+ tooltipParent = nestedDialog;
6247
+ }
6248
+
6249
+ // Remove other first
6250
+ LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
6251
+
6252
+ // Append new tooltip
6253
+ tooltipParent.appendChild( tooltipDom );
5872
6254
 
5873
6255
  LX.doAsync( () => {
5874
6256
 
5875
6257
  const position = [ 0, 0 ];
6258
+ const offsetX = parseFloat( trigger.dataset[ "tooltipOffsetX" ] ?? _offsetX );
6259
+ const offsetY = parseFloat( trigger.dataset[ "tooltipOffsetY" ] ?? _offsetY );
5876
6260
  const rect = this.getBoundingClientRect();
5877
- const offset = options.offset ?? 6;
5878
6261
  let alignWidth = true;
5879
6262
 
5880
6263
  switch( options.side ?? "top" )
5881
6264
  {
5882
6265
  case "left":
5883
- position[ 0 ] += ( rect.x - tooltipDom.offsetWidth - offset );
6266
+ position[ 0 ] += ( rect.x - tooltipDom.offsetWidth - offsetX );
5884
6267
  alignWidth = false;
5885
6268
  break;
5886
6269
  case "right":
5887
- position[ 0 ] += ( rect.x + rect.width + offset );
6270
+ position[ 0 ] += ( rect.x + rect.width + offsetX );
5888
6271
  alignWidth = false;
5889
6272
  break;
5890
6273
  case "top":
5891
- position[ 1 ] += ( rect.y - tooltipDom.offsetHeight - offset );
6274
+ position[ 1 ] += ( rect.y - tooltipDom.offsetHeight - offsetY );
5892
6275
  alignWidth = true;
5893
6276
  break;
5894
6277
  case "bottom":
5895
- position[ 1 ] += ( rect.y + rect.height + offset );
6278
+ position[ 1 ] += ( rect.y + rect.height + offsetY );
5896
6279
  alignWidth = true;
5897
6280
  break;
5898
6281
  }
5899
6282
 
5900
- if( alignWidth ) { position[ 0 ] += ( rect.x + rect.width * 0.5 ) - tooltipDom.offsetWidth * 0.5; }
5901
- else { position[ 1 ] += ( rect.y + rect.height * 0.5 ) - tooltipDom.offsetHeight * 0.5; }
6283
+ if( alignWidth ) { position[ 0 ] += ( rect.x + rect.width * 0.5 ) - tooltipDom.offsetWidth * 0.5 + offsetX; }
6284
+ else { position[ 1 ] += ( rect.y + rect.height * 0.5 ) - tooltipDom.offsetHeight * 0.5 + offsetY; }
5902
6285
 
5903
6286
  // Avoid collisions
5904
6287
  position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - tooltipDom.offsetWidth - 4 );
5905
6288
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - tooltipDom.offsetHeight - 4 );
5906
6289
 
6290
+ if( tooltipParent instanceof HTMLDialogElement )
6291
+ {
6292
+ let parentRect = tooltipParent.getBoundingClientRect();
6293
+ position[ 0 ] -= parentRect.x;
6294
+ position[ 1 ] -= parentRect.y;
6295
+ }
6296
+
5907
6297
  tooltipDom.style.left = `${ position[ 0 ] }px`;
5908
6298
  tooltipDom.style.top = `${ position[ 1 ] }px`;
5909
- } );
5910
6299
 
5911
- LX.root.appendChild( tooltipDom );
6300
+ if( options.callback )
6301
+ {
6302
+ options.callback( tooltipDom, trigger );
6303
+ }
6304
+ } );
5912
6305
  } );
5913
6306
 
5914
6307
  trigger.addEventListener( "mouseleave", function(e) {
@@ -6354,15 +6747,15 @@ class AreaOverlayButtons {
6354
6747
  }
6355
6748
 
6356
6749
  let callback = b.callback;
6357
- let widget = null;
6750
+ let component = null;
6358
6751
 
6359
6752
  if( b.options )
6360
6753
  {
6361
- widget = overlayPanel.addSelect( null, b.options, b.value ?? b.name, callback, _options );
6754
+ component = overlayPanel.addSelect( null, b.options, b.value ?? b.name, callback, _options );
6362
6755
  }
6363
6756
  else
6364
6757
  {
6365
- widget = overlayPanel.addButton( null, b.name, function( value, event ) {
6758
+ component = overlayPanel.addButton( null, b.name, function( value, event ) {
6366
6759
  if( b.selectable )
6367
6760
  {
6368
6761
  if( b.group )
@@ -6379,13 +6772,13 @@ class AreaOverlayButtons {
6379
6772
 
6380
6773
  if( callback )
6381
6774
  {
6382
- callback( value, event, widget.root );
6775
+ callback( value, event, component.root );
6383
6776
  }
6384
6777
 
6385
6778
  }, _options );
6386
6779
  }
6387
6780
 
6388
- this.buttons[ b.name ] = widget;
6781
+ this.buttons[ b.name ] = component;
6389
6782
 
6390
6783
  // ends the group
6391
6784
  if( overlayGroup && last )
@@ -6459,6 +6852,7 @@ class Area {
6459
6852
  * minHeight: Minimum height to be applied when resizing
6460
6853
  * maxWidth: Maximum width to be applied when resizing
6461
6854
  * maxHeight: Maximum height to be applied when resizing
6855
+ * layout: Layout to automatically split the area
6462
6856
  */
6463
6857
 
6464
6858
  constructor( options = {} ) {
@@ -6504,6 +6898,11 @@ class Area {
6504
6898
  lexroot.appendChild( this.root );
6505
6899
  }
6506
6900
 
6901
+ if( options.layout )
6902
+ {
6903
+ this.setLayout( options.layout );
6904
+ }
6905
+
6507
6906
  let overlay = options.overlay;
6508
6907
 
6509
6908
  if( overlay )
@@ -6664,6 +7063,47 @@ class Area {
6664
7063
  this.root.appendChild( element );
6665
7064
  }
6666
7065
 
7066
+ /**
7067
+ * @method setLayout
7068
+ * @description Automatically split the area to generate the desired layout
7069
+ * @param {Array} layout
7070
+ */
7071
+
7072
+ setLayout( layout ) {
7073
+
7074
+ this.layout = LX.deepCopy( layout );
7075
+
7076
+ if( !layout.splits )
7077
+ {
7078
+ console.warn( "Area layout has no splits!" );
7079
+ return;
7080
+ }
7081
+
7082
+ const _splitArea = ( area, layout ) => {
7083
+
7084
+ if( layout.className )
7085
+ {
7086
+ area.root.className += ` ${ layout.className }`;
7087
+ }
7088
+
7089
+ if( !layout.splits )
7090
+ {
7091
+ return;
7092
+ }
7093
+
7094
+ const type = layout.type ?? "horizontal";
7095
+ const resize = layout.resize ?? true;
7096
+ const minimizable = layout.minimizable ?? false;
7097
+ const [ splitA, splitB ] = area.split( { type, resize, minimizable, sizes: [ layout.splits[ 0 ].size, layout.splits[ 1 ].size ] } );
7098
+
7099
+ _splitArea( splitA, layout.splits[ 0 ] );
7100
+ _splitArea( splitB, layout.splits[ 1 ] );
7101
+
7102
+ };
7103
+
7104
+ _splitArea( this, layout );
7105
+ }
7106
+
6667
7107
  /**
6668
7108
  * @method split
6669
7109
  * @param {Object} options
@@ -6922,12 +7362,17 @@ class Area {
6922
7362
  doc.addEventListener( 'mouseup', innerMouseUp );
6923
7363
  e.stopPropagation();
6924
7364
  e.preventDefault();
6925
- document.body.classList.add( 'nocursor' );
6926
- that.splitBar.classList.add( 'nocursor' );
6927
7365
  }
6928
7366
 
6929
7367
  function innerMouseMove( e )
6930
7368
  {
7369
+ const rect = that.root.getBoundingClientRect();
7370
+ if( ( ( e.x ) < rect.x || ( e.x ) > ( rect.x + rect.width ) ) ||
7371
+ ( ( e.y ) < rect.y || ( e.y ) > ( rect.y + rect.height ) ) )
7372
+ {
7373
+ return;
7374
+ }
7375
+
6931
7376
  if( that.type == "horizontal" )
6932
7377
  {
6933
7378
  that._moveSplit( -e.movementX );
@@ -6946,8 +7391,6 @@ class Area {
6946
7391
  const doc = that.root.ownerDocument;
6947
7392
  doc.removeEventListener( 'mousemove', innerMouseMove );
6948
7393
  doc.removeEventListener( 'mouseup', innerMouseUp );
6949
- document.body.classList.remove( 'nocursor' );
6950
- that.splitBar.classList.remove( 'nocursor' );
6951
7394
  }
6952
7395
 
6953
7396
  return this.sections;
@@ -7306,10 +7749,11 @@ class Area {
7306
7749
  }
7307
7750
  else
7308
7751
  {
7752
+ const parentHeight = this.size[ 1 ];
7309
7753
  var size = Math.max( ( a2Root.offsetHeight + dt ) + a2.offset, parseInt( a2.minHeight ) );
7754
+ size = Math.min( parentHeight - LX.DEFAULT_SPLITBAR_SIZE, size );
7310
7755
  if( forceWidth ) size = forceWidth;
7311
7756
 
7312
- const parentHeight = this.size[ 1 ];
7313
7757
  const bottomPercent = ( size / parentHeight ) * 100;
7314
7758
  const topPercent = Math.max( 0, 100 - bottomPercent );
7315
7759
 
@@ -7368,13 +7812,13 @@ class Area {
7368
7812
  }
7369
7813
  LX.Area = Area;
7370
7814
 
7371
- // widget.js @jxarco
7815
+ // base_component.js @jxarco
7372
7816
 
7373
7817
  /**
7374
- * @class Widget
7818
+ * @class BaseComponent
7375
7819
  */
7376
7820
 
7377
- class Widget {
7821
+ class BaseComponent {
7378
7822
 
7379
7823
  static NONE = 0;
7380
7824
  static TEXT = 1;
@@ -7414,14 +7858,15 @@ class Widget {
7414
7858
  static TABS = 35;
7415
7859
  static DATE = 36;
7416
7860
  static MAP2D = 37;
7417
- static LABEL = 38;
7418
- static BLANK = 39;
7861
+ static LABEL = 39;
7862
+ static BLANK = 40;
7863
+ static RATE = 41;
7419
7864
 
7420
7865
  static NO_CONTEXT_TYPES = [
7421
- Widget.BUTTON,
7422
- Widget.LIST,
7423
- Widget.FILE,
7424
- Widget.PROGRESS
7866
+ BaseComponent.BUTTON,
7867
+ BaseComponent.LIST,
7868
+ BaseComponent.FILE,
7869
+ BaseComponent.PROGRESS
7425
7870
  ];
7426
7871
 
7427
7872
  constructor( type, name, value, options = {} ) {
@@ -7432,7 +7877,7 @@ class Widget {
7432
7877
  this._initialValue = value;
7433
7878
 
7434
7879
  const root = document.createElement( 'div' );
7435
- root.className = "lexwidget";
7880
+ root.className = "lexcomponent";
7436
7881
 
7437
7882
  if( options.id )
7438
7883
  {
@@ -7449,7 +7894,7 @@ class Widget {
7449
7894
  root.className += " " + options.className;
7450
7895
  }
7451
7896
 
7452
- if( type != Widget.TITLE )
7897
+ if( type != BaseComponent.TITLE )
7453
7898
  {
7454
7899
  if( options.width )
7455
7900
  {
@@ -7468,7 +7913,7 @@ class Widget {
7468
7913
  root.style.height = root.style.minHeight = options.height;
7469
7914
  }
7470
7915
 
7471
- LX.widgetResizeObserver.observe( root );
7916
+ LX.componentResizeObserver.observe( root );
7472
7917
  }
7473
7918
 
7474
7919
  if( name != undefined )
@@ -7476,7 +7921,7 @@ class Widget {
7476
7921
  if( !( options.hideName ?? false ) )
7477
7922
  {
7478
7923
  let domName = document.createElement( 'div' );
7479
- domName.className = "lexwidgetname";
7924
+ domName.className = "lexcomponentname";
7480
7925
 
7481
7926
  if( options.justifyName )
7482
7927
  {
@@ -7538,7 +7983,7 @@ class Widget {
7538
7983
  }
7539
7984
 
7540
7985
  _canPaste() {
7541
- let pasteAllowed = this.type === Widget.CUSTOM ?
7986
+ let pasteAllowed = this.type === BaseComponent.CUSTOM ?
7542
7987
  ( navigator.clipboard.customIdx !== undefined && this.customIdx == navigator.clipboard.customIdx ) : navigator.clipboard.type === this.type;
7543
7988
 
7544
7989
  pasteAllowed &= ( this.disabled !== true );
@@ -7575,7 +8020,7 @@ class Widget {
7575
8020
 
7576
8021
  if( this.onSetValue )
7577
8022
  {
7578
- let resetButton = this.root.querySelector( ".lexwidgetname .lexicon" );
8023
+ let resetButton = this.root.querySelector( ".lexcomponentname .lexicon" );
7579
8024
  if( resetButton )
7580
8025
  {
7581
8026
  resetButton.style.display = ( value != this.value() ? "block" : "none" );
@@ -7601,7 +8046,7 @@ class Widget {
7601
8046
 
7602
8047
  oncontextmenu( e ) {
7603
8048
 
7604
- if( Widget.NO_CONTEXT_TYPES.includes( this.type ) )
8049
+ if( BaseComponent.NO_CONTEXT_TYPES.includes( this.type ) )
7605
8050
  {
7606
8051
  return;
7607
8052
  }
@@ -7632,41 +8077,42 @@ class Widget {
7632
8077
 
7633
8078
  switch( this.type )
7634
8079
  {
7635
- case Widget.TEXT: return "Text";
7636
- case Widget.TEXTAREA: return "TextArea";
7637
- case Widget.BUTTON: return "Button";
7638
- case Widget.SELECT: return "Select";
7639
- case Widget.CHECKBOX: return "Checkbox";
7640
- case Widget.TOGGLE: return "Toggle";
7641
- case Widget.RADIO: return "Radio";
7642
- case Widget.COLOR: return "Color";
7643
- case Widget.RANGE: return "Range";
7644
- case Widget.NUMBER: return "Number";
7645
- case Widget.VECTOR: return "Vector";
7646
- case Widget.TREE: return "Tree";
7647
- case Widget.PROGRESS: return "Progress";
7648
- case Widget.FILE: return "File";
7649
- case Widget.LAYERS: return "Layers";
7650
- case Widget.ARRAY: return "Array";
7651
- case Widget.LIST: return "List";
7652
- case Widget.TAGS: return "Tags";
7653
- case Widget.CURVE: return "Curve";
7654
- case Widget.KNOB: return "Knob";
7655
- case Widget.SIZE: return "Size";
7656
- case Widget.PAD: return "Pad";
7657
- case Widget.FORM: return "Form";
7658
- case Widget.DIAL: return "Dial";
7659
- case Widget.COUNTER: return "Counter";
7660
- case Widget.TABLE: return "Table";
7661
- case Widget.TABS: return "Tabs";
7662
- case Widget.DATE: return "Date";
7663
- case Widget.MAP2D: return "Map2D";
7664
- case Widget.LABEL: return "Label";
7665
- case Widget.BLANK: return "Blank";
7666
- case Widget.CUSTOM: return this.customName;
7667
- }
7668
-
7669
- console.error( `Unknown Widget type: ${ this.type }` );
8080
+ case BaseComponent.TEXT: return "Text";
8081
+ case BaseComponent.TEXTAREA: return "TextArea";
8082
+ case BaseComponent.BUTTON: return "Button";
8083
+ case BaseComponent.SELECT: return "Select";
8084
+ case BaseComponent.CHECKBOX: return "Checkbox";
8085
+ case BaseComponent.TOGGLE: return "Toggle";
8086
+ case BaseComponent.RADIO: return "Radio";
8087
+ case BaseComponent.COLOR: return "Color";
8088
+ case BaseComponent.RANGE: return "Range";
8089
+ case BaseComponent.NUMBER: return "Number";
8090
+ case BaseComponent.VECTOR: return "Vector";
8091
+ case BaseComponent.TREE: return "Tree";
8092
+ case BaseComponent.PROGRESS: return "Progress";
8093
+ case BaseComponent.FILE: return "File";
8094
+ case BaseComponent.LAYERS: return "Layers";
8095
+ case BaseComponent.ARRAY: return "Array";
8096
+ case BaseComponent.LIST: return "List";
8097
+ case BaseComponent.TAGS: return "Tags";
8098
+ case BaseComponent.CURVE: return "Curve";
8099
+ case BaseComponent.KNOB: return "Knob";
8100
+ case BaseComponent.SIZE: return "Size";
8101
+ case BaseComponent.PAD: return "Pad";
8102
+ case BaseComponent.FORM: return "Form";
8103
+ case BaseComponent.DIAL: return "Dial";
8104
+ case BaseComponent.COUNTER: return "Counter";
8105
+ case BaseComponent.TABLE: return "Table";
8106
+ case BaseComponent.TABS: return "Tabs";
8107
+ case BaseComponent.DATE: return "Date";
8108
+ case BaseComponent.MAP2D: return "Map2D";
8109
+ case BaseComponent.RATE: return "Rate";
8110
+ case BaseComponent.LABEL: return "Label";
8111
+ case BaseComponent.BLANK: return "Blank";
8112
+ case BaseComponent.CUSTOM: return this.customName;
8113
+ }
8114
+
8115
+ console.error( `Unknown Component type: ${ this.type }` );
7670
8116
  }
7671
8117
 
7672
8118
  refresh() {
@@ -7674,52 +8120,52 @@ class Widget {
7674
8120
  }
7675
8121
  }
7676
8122
 
7677
- LX.Widget = Widget;
8123
+ LX.BaseComponent = BaseComponent;
7678
8124
 
7679
- function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
8125
+ function ADD_CUSTOM_COMPONENT( customComponentName, options = {} )
7680
8126
  {
7681
8127
  let customIdx = LX.guidGenerator();
7682
8128
 
7683
- LX.Panel.prototype[ 'add' + customWidgetName ] = function( name, instance, callback ) {
8129
+ LX.Panel.prototype[ 'add' + customComponentName ] = function( name, instance, callback ) {
7684
8130
 
7685
8131
  const userParams = Array.from( arguments ).slice( 3 );
7686
8132
 
7687
- let widget = new Widget( Widget.CUSTOM, name, null, options );
7688
- this._attachWidget( widget );
8133
+ let component = new BaseComponent( BaseComponent.CUSTOM, name, null, options );
8134
+ this._attachComponent( component );
7689
8135
 
7690
- widget.customName = customWidgetName;
7691
- widget.customIdx = customIdx;
8136
+ component.customName = customComponentName;
8137
+ component.customIdx = customIdx;
7692
8138
 
7693
- widget.onGetValue = () => {
8139
+ component.onGetValue = () => {
7694
8140
  return instance;
7695
8141
  };
7696
8142
 
7697
- widget.onSetValue = ( newValue, skipCallback, event ) => {
8143
+ component.onSetValue = ( newValue, skipCallback, event ) => {
7698
8144
  instance = newValue;
7699
- refresh_widget();
8145
+ _refreshComponent();
7700
8146
  element.querySelector( ".lexcustomitems" ).toggleAttribute( 'hidden', false );
7701
8147
  if( !skipCallback )
7702
8148
  {
7703
- widget._trigger( new LX.IEvent( name, instance, event ), callback );
8149
+ component._trigger( new LX.IEvent( name, instance, event ), callback );
7704
8150
  }
7705
8151
  };
7706
8152
 
7707
- widget.onResize = ( rect ) => {
7708
- const realNameWidth = ( widget.root.domName?.style.width ?? "0px" );
8153
+ component.onResize = ( rect ) => {
8154
+ const realNameWidth = ( component.root.domName?.style.width ?? "0px" );
7709
8155
  container.style.width = `calc( 100% - ${ realNameWidth })`;
7710
8156
  };
7711
8157
 
7712
- const element = widget.root;
8158
+ const element = component.root;
7713
8159
 
7714
- let container, customWidgetsDom;
8160
+ let container, customComponentsDom;
7715
8161
  let defaultInstance = options.default ?? {};
7716
8162
 
7717
8163
  // Add instance button
7718
8164
 
7719
- const refresh_widget = () => {
8165
+ const _refreshComponent = () => {
7720
8166
 
7721
8167
  if( container ) container.remove();
7722
- if( customWidgetsDom ) customWidgetsDom.remove();
8168
+ if( customComponentsDom ) customComponentsDom.remove();
7723
8169
 
7724
8170
  container = document.createElement('div');
7725
8171
  container.className = "lexcustomcontainer w-full";
@@ -7729,7 +8175,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7729
8175
  const customIcon = LX.makeIcon( options.icon ?? "Box" );
7730
8176
  const menuIcon = LX.makeIcon( "Menu" );
7731
8177
 
7732
- let buttonName = customWidgetName + (!instance ? " [empty]" : "");
8178
+ let buttonName = customComponentName + (!instance ? " [empty]" : "");
7733
8179
  let buttonEl = this.addButton(null, buttonName, (value, event) => {
7734
8180
  if( instance )
7735
8181
  {
@@ -7739,9 +8185,9 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7739
8185
  else
7740
8186
  {
7741
8187
  LX.addContextMenu(null, event, c => {
7742
- c.add("New " + customWidgetName, () => {
8188
+ c.add("New " + customComponentName, () => {
7743
8189
  instance = {};
7744
- refresh_widget();
8190
+ _refreshComponent();
7745
8191
  element.querySelector(".lexcustomitems").toggleAttribute('hidden', false);
7746
8192
  element.dataset["opened"] = !element.querySelector(".lexcustomitems").hasAttribute("hidden");
7747
8193
  });
@@ -7763,7 +8209,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7763
8209
  LX.addContextMenu(null, e, c => {
7764
8210
  c.add("Clear", () => {
7765
8211
  instance = null;
7766
- refresh_widget();
8212
+ _refreshComponent();
7767
8213
  });
7768
8214
  });
7769
8215
  });
@@ -7771,14 +8217,14 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7771
8217
 
7772
8218
  // Show elements
7773
8219
 
7774
- customWidgetsDom = document.createElement('div');
7775
- customWidgetsDom.className = "lexcustomitems";
7776
- customWidgetsDom.toggleAttribute('hidden', true);
7777
- element.appendChild( customWidgetsDom );
8220
+ customComponentsDom = document.createElement('div');
8221
+ customComponentsDom.className = "lexcustomitems";
8222
+ customComponentsDom.toggleAttribute('hidden', true);
8223
+ element.appendChild( customComponentsDom );
7778
8224
 
7779
8225
  if( instance )
7780
8226
  {
7781
- this.queue( customWidgetsDom );
8227
+ this.queue( customComponentsDom );
7782
8228
 
7783
8229
  const on_instance_changed = ( key, value, event ) => {
7784
8230
  const setter = options[ `_set_${ key }` ];
@@ -7790,7 +8236,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7790
8236
  {
7791
8237
  instance[ key ] = value;
7792
8238
  }
7793
- widget._trigger( new LX.IEvent( name, instance, event ), callback );
8239
+ component._trigger( new LX.IEvent( name, instance, event ), callback );
7794
8240
  };
7795
8241
 
7796
8242
  for( let key in defaultInstance )
@@ -7855,11 +8301,11 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7855
8301
  }
7856
8302
  };
7857
8303
 
7858
- refresh_widget();
8304
+ _refreshComponent();
7859
8305
  };
7860
8306
  }
7861
8307
 
7862
- LX.ADD_CUSTOM_WIDGET = ADD_CUSTOM_WIDGET;
8308
+ LX.ADD_CUSTOM_COMPONENT = ADD_CUSTOM_COMPONENT;
7863
8309
 
7864
8310
  /**
7865
8311
  * @class NodeTree
@@ -8431,14 +8877,14 @@ LX.NodeTree = NodeTree;
8431
8877
 
8432
8878
  /**
8433
8879
  * @class Blank
8434
- * @description Blank Widget
8880
+ * @description Blank Component
8435
8881
  */
8436
8882
 
8437
- class Blank extends Widget {
8883
+ class Blank extends BaseComponent {
8438
8884
 
8439
8885
  constructor( width, height ) {
8440
8886
 
8441
- super( Widget.BLANK );
8887
+ super( BaseComponent.BLANK );
8442
8888
 
8443
8889
  this.root.style.width = width ?? "auto";
8444
8890
  this.root.style.height = height ?? "8px";
@@ -8449,17 +8895,17 @@ LX.Blank = Blank;
8449
8895
 
8450
8896
  /**
8451
8897
  * @class Title
8452
- * @description Title Widget
8898
+ * @description Title Component
8453
8899
  */
8454
8900
 
8455
- class Title extends Widget {
8901
+ class Title extends BaseComponent {
8456
8902
 
8457
8903
  constructor( name, options = {} ) {
8458
8904
 
8459
- console.assert( name, "Can't create Title Widget without text!" );
8905
+ console.assert( name, "Can't create Title Component without text!" );
8460
8906
 
8461
- // Note: Titles are not registered in Panel.widgets by now
8462
- super( Widget.TITLE, null, null, options );
8907
+ // Note: Titles are not registered in Panel.components by now
8908
+ super( BaseComponent.TITLE, null, null, options );
8463
8909
 
8464
8910
  this.root.className = `lextitle ${ this.root.className }`;
8465
8911
 
@@ -8493,14 +8939,14 @@ LX.Title = Title;
8493
8939
 
8494
8940
  /**
8495
8941
  * @class TextInput
8496
- * @description TextInput Widget
8942
+ * @description TextInput Component
8497
8943
  */
8498
8944
 
8499
- class TextInput extends Widget {
8945
+ class TextInput extends BaseComponent {
8500
8946
 
8501
8947
  constructor( name, value, callback, options = {} ) {
8502
8948
 
8503
- super( Widget.TEXT, name, String( value ), options );
8949
+ super( BaseComponent.TEXT, name, String( value ), options );
8504
8950
 
8505
8951
  this.onGetValue = () => {
8506
8952
  return value;
@@ -8633,14 +9079,14 @@ LX.TextInput = TextInput;
8633
9079
 
8634
9080
  /**
8635
9081
  * @class TextArea
8636
- * @description TextArea Widget
9082
+ * @description TextArea Component
8637
9083
  */
8638
9084
 
8639
- class TextArea extends Widget {
9085
+ class TextArea extends BaseComponent {
8640
9086
 
8641
9087
  constructor( name, value, callback, options = {} ) {
8642
9088
 
8643
- super( Widget.TEXTAREA, name, value, options );
9089
+ super( BaseComponent.TEXTAREA, name, value, options );
8644
9090
 
8645
9091
  this.onGetValue = () => {
8646
9092
  return value;
@@ -8734,14 +9180,14 @@ LX.TextArea = TextArea;
8734
9180
 
8735
9181
  /**
8736
9182
  * @class Button
8737
- * @description Button Widget
9183
+ * @description Button Component
8738
9184
  */
8739
9185
 
8740
- class Button extends Widget {
9186
+ class Button extends BaseComponent {
8741
9187
 
8742
9188
  constructor( name, value, callback, options = {} ) {
8743
9189
 
8744
- super( Widget.BUTTON, name, null, options );
9190
+ super( BaseComponent.BUTTON, name, null, options );
8745
9191
 
8746
9192
  this.onGetValue = () => {
8747
9193
  const swapInput = wValue.querySelector( "input" );
@@ -8956,10 +9402,10 @@ LX.Button = Button;
8956
9402
 
8957
9403
  /**
8958
9404
  * @class ComboButtons
8959
- * @description ComboButtons Widget
9405
+ * @description ComboButtons Component
8960
9406
  */
8961
9407
 
8962
- class ComboButtons extends Widget {
9408
+ class ComboButtons extends BaseComponent {
8963
9409
 
8964
9410
  constructor( name, values, options = {} ) {
8965
9411
 
@@ -9074,7 +9520,7 @@ class ComboButtons extends Widget {
9074
9520
  currentValue = currentValue[ 0 ];
9075
9521
  }
9076
9522
 
9077
- super( Widget.BUTTONS, name, null, options );
9523
+ super( BaseComponent.BUTTONS, name, null, options );
9078
9524
 
9079
9525
  this.onGetValue = () => {
9080
9526
  return currentValue;
@@ -9117,16 +9563,16 @@ LX.ComboButtons = ComboButtons;
9117
9563
 
9118
9564
  /**
9119
9565
  * @class Card
9120
- * @description Card Widget
9566
+ * @description Card Component
9121
9567
  */
9122
9568
 
9123
- class Card extends Widget {
9569
+ class Card extends BaseComponent {
9124
9570
 
9125
9571
  constructor( name, options = {} ) {
9126
9572
 
9127
9573
  options.hideName = true;
9128
9574
 
9129
- super( Widget.CARD, name, null, options );
9575
+ super( BaseComponent.CARD, name, null, options );
9130
9576
 
9131
9577
  let container = document.createElement('div');
9132
9578
  container.className = "lexcard";
@@ -9180,10 +9626,10 @@ LX.Card = Card;
9180
9626
 
9181
9627
  /**
9182
9628
  * @class Form
9183
- * @description Form Widget
9629
+ * @description Form Component
9184
9630
  */
9185
9631
 
9186
- class Form extends Widget {
9632
+ class Form extends BaseComponent {
9187
9633
 
9188
9634
  constructor( name, data, callback, options = {} ) {
9189
9635
 
@@ -9196,7 +9642,7 @@ class Form extends Widget {
9196
9642
  // Always hide name for this one
9197
9643
  options.hideName = true;
9198
9644
 
9199
- super( Widget.FORM, name, null, options );
9645
+ super( BaseComponent.FORM, name, null, options );
9200
9646
 
9201
9647
  this.onGetValue = () => {
9202
9648
  return container.formData;
@@ -9204,18 +9650,18 @@ class Form extends Widget {
9204
9650
 
9205
9651
  this.onSetValue = ( newValue, skipCallback, event ) => {
9206
9652
  container.formData = newValue;
9207
- const entries = container.querySelectorAll( ".lexwidget" );
9653
+ const entries = container.querySelectorAll( ".lexcomponent" );
9208
9654
  for( let i = 0; i < entries.length; ++i )
9209
9655
  {
9210
9656
  const entry = entries[ i ];
9211
- if( entry.jsInstance.type != LX.Widget.TEXT )
9657
+ if( entry.jsInstance.type != BaseComponent.TEXT )
9212
9658
  {
9213
9659
  continue;
9214
9660
  }
9215
- let entryName = entries[ i ].querySelector( ".lexwidgetname" ).innerText;
9661
+ let entryName = entries[ i ].querySelector( ".lexcomponentname" ).innerText;
9216
9662
  let entryInput = entries[ i ].querySelector( ".lextext input" );
9217
9663
  entryInput.value = newValue[ entryName ] ?? "";
9218
- Widget._dispatchEvent( entryInput, "focusout", skipCallback );
9664
+ BaseComponent._dispatchEvent( entryInput, "focusout", skipCallback );
9219
9665
  }
9220
9666
  };
9221
9667
 
@@ -9245,10 +9691,10 @@ class Form extends Widget {
9245
9691
  container.appendChild( label.root );
9246
9692
  }
9247
9693
 
9248
- entryData.textWidget = new LX.TextInput( null, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
9694
+ entryData.textComponent = new LX.TextInput( null, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
9249
9695
  container.formData[ entry ] = value;
9250
9696
  }, entryData );
9251
- container.appendChild( entryData.textWidget.root );
9697
+ container.appendChild( entryData.textComponent.root );
9252
9698
 
9253
9699
  container.formData[ entry ] = entryData.constructor == Object ? entryData.value : entryData;
9254
9700
  }
@@ -9273,7 +9719,7 @@ class Form extends Widget {
9273
9719
  {
9274
9720
  let entryData = data[ entry ];
9275
9721
 
9276
- if( !entryData.textWidget.valid() )
9722
+ if( !entryData.textComponent.valid() )
9277
9723
  {
9278
9724
  return;
9279
9725
  }
@@ -9293,14 +9739,14 @@ LX.Form = Form;
9293
9739
 
9294
9740
  /**
9295
9741
  * @class Select
9296
- * @description Select Widget
9742
+ * @description Select Component
9297
9743
  */
9298
9744
 
9299
- class Select extends Widget {
9745
+ class Select extends BaseComponent {
9300
9746
 
9301
9747
  constructor( name, values, value, callback, options = {} ) {
9302
9748
 
9303
- super( Widget.SELECT, name, value, options );
9749
+ super( BaseComponent.SELECT, name, value, options );
9304
9750
 
9305
9751
  this.onGetValue = () => {
9306
9752
  return value;
@@ -9373,7 +9819,7 @@ class Select extends Widget {
9373
9819
  options.overflowContainerX = options.overflowContainerY = options.overflowContainer;
9374
9820
  }
9375
9821
 
9376
- const _placeOptions = ( parent ) => {
9822
+ const _placeOptions = ( parent, forceLastPlacement ) => {
9377
9823
 
9378
9824
  const selectRoot = selectedOption.root;
9379
9825
  const rect = selectRoot.getBoundingClientRect();
@@ -9403,8 +9849,8 @@ class Select extends Widget {
9403
9849
  parent.style.top = ( topPosition + selectRoot.offsetHeight ) + 'px';
9404
9850
  list.style.height = ""; // set auto height by default
9405
9851
 
9406
- const failBelow = ( topPosition + listHeight ) > maxY;
9407
- const failAbove = ( topPosition - listHeight ) < 0;
9852
+ const failAbove = forceLastPlacement ? this._lastPlacement[ 0 ] : ( topPosition - listHeight ) < 0;
9853
+ const failBelow = forceLastPlacement ? this._lastPlacement[ 1 ] : ( topPosition + listHeight ) > maxY;
9408
9854
  if( failBelow && !failAbove )
9409
9855
  {
9410
9856
  parent.style.top = ( topPosition - listHeight ) + 'px';
@@ -9415,6 +9861,8 @@ class Select extends Widget {
9415
9861
  {
9416
9862
  list.style.height = `${ maxY - topPosition - 32 }px`; // 32px margin
9417
9863
  }
9864
+
9865
+ this._lastPlacement = [ failAbove, failBelow ];
9418
9866
  }
9419
9867
 
9420
9868
  // Manage horizontal aspect
@@ -9528,7 +9976,7 @@ class Select extends Widget {
9528
9976
  {
9529
9977
  const filterOptions = LX.deepCopy( options );
9530
9978
  filterOptions.placeholder = filterOptions.placeholder ?? "Search...";
9531
- filterOptions.skipWidget = filterOptions.skipWidget ?? true;
9979
+ filterOptions.skipComponent = filterOptions.skipComponent ?? true;
9532
9980
  filterOptions.trigger = "input";
9533
9981
  filterOptions.icon = "Search";
9534
9982
  filterOptions.className = "lexfilter";
@@ -9537,6 +9985,7 @@ class Select extends Widget {
9537
9985
  filter = new LX.TextInput(null, options.filterValue ?? "", ( v ) => {
9538
9986
  const filteredOptions = this._filterOptions( values, v );
9539
9987
  list.refresh( filteredOptions );
9988
+ _placeOptions( listDialog, true );
9540
9989
  }, filterOptions );
9541
9990
  filter.root.querySelector( ".lextext" ).style.border = "1px solid transparent";
9542
9991
 
@@ -9570,7 +10019,7 @@ class Select extends Widget {
9570
10019
 
9571
10020
  let option = document.createElement( "div" );
9572
10021
  option.className = "option";
9573
- option.innerHTML = iValue;
10022
+ option.innerHTML = ( LX.makeIcon( "Inbox", { svgClass: "mr-2" } ).innerHTML + iValue );
9574
10023
 
9575
10024
  let li = document.createElement( "li" );
9576
10025
  li.className = "lexselectitem empty";
@@ -9679,7 +10128,7 @@ class Select extends Widget {
9679
10128
  const emptyFilter = !value.length;
9680
10129
  let filteredOptions = [];
9681
10130
 
9682
- // Add widgets
10131
+ // Add components
9683
10132
  for( let i = 0; i < options.length; i++ )
9684
10133
  {
9685
10134
  let o = options[ i ];
@@ -9702,16 +10151,16 @@ LX.Select = Select;
9702
10151
 
9703
10152
  /**
9704
10153
  * @class Curve
9705
- * @description Curve Widget
10154
+ * @description Curve Component
9706
10155
  */
9707
10156
 
9708
- class Curve extends Widget {
10157
+ class Curve extends BaseComponent {
9709
10158
 
9710
10159
  constructor( name, values, callback, options = {} ) {
9711
10160
 
9712
10161
  let defaultValues = JSON.parse( JSON.stringify( values ) );
9713
10162
 
9714
- super( Widget.CURVE, name, defaultValues, options );
10163
+ super( BaseComponent.CURVE, name, defaultValues, options );
9715
10164
 
9716
10165
  this.onGetValue = () => {
9717
10166
  return JSON.parse(JSON.stringify( curveInstance.element.value ));
@@ -9763,16 +10212,16 @@ LX.Curve = Curve;
9763
10212
 
9764
10213
  /**
9765
10214
  * @class Dial
9766
- * @description Dial Widget
10215
+ * @description Dial Component
9767
10216
  */
9768
10217
 
9769
- class Dial extends Widget {
10218
+ class Dial extends BaseComponent {
9770
10219
 
9771
10220
  constructor( name, values, callback, options = {} ) {
9772
10221
 
9773
10222
  let defaultValues = JSON.parse( JSON.stringify( values ) );
9774
10223
 
9775
- super( Widget.DIAL, name, defaultValues, options );
10224
+ super( BaseComponent.DIAL, name, defaultValues, options );
9776
10225
 
9777
10226
  this.onGetValue = () => {
9778
10227
  return JSON.parse( JSON.stringify( dialInstance.element.value ) );
@@ -9820,14 +10269,14 @@ LX.Dial = Dial;
9820
10269
 
9821
10270
  /**
9822
10271
  * @class Layers
9823
- * @description Layers Widget
10272
+ * @description Layers Component
9824
10273
  */
9825
10274
 
9826
- class Layers extends Widget {
10275
+ class Layers extends BaseComponent {
9827
10276
 
9828
10277
  constructor( name, value, callback, options = {} ) {
9829
10278
 
9830
- super( Widget.LAYERS, name, value, options );
10279
+ super( BaseComponent.LAYERS, name, value, options );
9831
10280
 
9832
10281
  this.onGetValue = () => {
9833
10282
  return value;
@@ -9904,16 +10353,16 @@ LX.Layers = Layers;
9904
10353
 
9905
10354
  /**
9906
10355
  * @class ItemArray
9907
- * @description ItemArray Widget
10356
+ * @description ItemArray Component
9908
10357
  */
9909
10358
 
9910
- class ItemArray extends Widget {
10359
+ class ItemArray extends BaseComponent {
9911
10360
 
9912
10361
  constructor( name, values = [], callback, options = {} ) {
9913
10362
 
9914
10363
  options.nameWidth = "100%";
9915
10364
 
9916
- super( Widget.ARRAY, name, null, options );
10365
+ super( BaseComponent.ARRAY, name, null, options );
9917
10366
 
9918
10367
  this.onGetValue = () => {
9919
10368
  return values;
@@ -9968,41 +10417,41 @@ class ItemArray extends Widget {
9968
10417
  {
9969
10418
  const value = values[ i ];
9970
10419
  let baseclass = options.innerValues ? 'select' : value.constructor;
9971
- let widget = null;
10420
+ let component = null;
9972
10421
 
9973
10422
  switch( baseclass )
9974
10423
  {
9975
10424
  case String:
9976
- widget = new LX.TextInput(i + "", value, function(value, event) {
10425
+ component = new LX.TextInput(i + "", value, function(value, event) {
9977
10426
  values[ i ] = value;
9978
10427
  callback( values );
9979
10428
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9980
10429
  break;
9981
10430
  case Number:
9982
- widget = new NumberInput(i + "", value, function(value, event) {
10431
+ component = new NumberInput(i + "", value, function(value, event) {
9983
10432
  values[ i ] = value;
9984
10433
  callback( values );
9985
10434
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9986
10435
  break;
9987
10436
  case 'select':
9988
- widget = new Select(i + "", options.innerValues, value, function(value, event) {
10437
+ component = new Select(i + "", options.innerValues, value, function(value, event) {
9989
10438
  values[ i ] = value;
9990
10439
  callback( values );
9991
10440
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9992
10441
  break;
9993
10442
  }
9994
10443
 
9995
- console.assert( widget, `Value of type ${ baseclass } cannot be modified in ItemArray` );
10444
+ console.assert( component, `Value of type ${ baseclass } cannot be modified in ItemArray` );
9996
10445
 
9997
- arrayItems.appendChild( widget.root );
10446
+ arrayItems.appendChild( component.root );
9998
10447
 
9999
- const removeWidget = new LX.Button( null, "", ( v, event) => {
10448
+ const removeComponent = new LX.Button( null, "", ( v, event) => {
10000
10449
  values.splice( values.indexOf( value ), 1 );
10001
10450
  this._updateItems();
10002
10451
  this._trigger( new LX.IEvent(name, values, event), callback );
10003
10452
  }, { title: "Remove item", icon: "Trash3"} );
10004
10453
 
10005
- widget.root.appendChild( removeWidget.root );
10454
+ component.root.appendChild( removeComponent.root );
10006
10455
  }
10007
10456
 
10008
10457
  const addButton = new LX.Button(null, LX.makeIcon( "Plus", { svgClass: "sm" } ).innerHTML + "Add item", (v, event) => {
@@ -10022,14 +10471,14 @@ LX.ItemArray = ItemArray;
10022
10471
 
10023
10472
  /**
10024
10473
  * @class List
10025
- * @description List Widget
10474
+ * @description List Component
10026
10475
  */
10027
10476
 
10028
- class List extends Widget {
10477
+ class List extends BaseComponent {
10029
10478
 
10030
10479
  constructor( name, values, value, callback, options = {} ) {
10031
10480
 
10032
- super( Widget.LIST, name, value, options );
10481
+ super( BaseComponent.LIST, name, value, options );
10033
10482
 
10034
10483
  this.onGetValue = () => {
10035
10484
  return value;
@@ -10122,17 +10571,17 @@ LX.List = List;
10122
10571
 
10123
10572
  /**
10124
10573
  * @class Tags
10125
- * @description Tags Widget
10574
+ * @description Tags Component
10126
10575
  */
10127
10576
 
10128
- class Tags extends Widget {
10577
+ class Tags extends BaseComponent {
10129
10578
 
10130
10579
  constructor( name, value, callback, options = {} ) {
10131
10580
 
10132
10581
  value = value.replace( /\s/g, '' ).split( ',' );
10133
10582
 
10134
10583
  let defaultValue = [].concat( value );
10135
- super( Widget.TAGS, name, defaultValue, options );
10584
+ super( BaseComponent.TAGS, name, defaultValue, options );
10136
10585
 
10137
10586
  this.onGetValue = () => {
10138
10587
  return [].concat( value );
@@ -10211,19 +10660,19 @@ LX.Tags = Tags;
10211
10660
 
10212
10661
  /**
10213
10662
  * @class Checkbox
10214
- * @description Checkbox Widget
10663
+ * @description Checkbox Component
10215
10664
  */
10216
10665
 
10217
- class Checkbox extends Widget {
10666
+ class Checkbox extends BaseComponent {
10218
10667
 
10219
10668
  constructor( name, value, callback, options = {} ) {
10220
10669
 
10221
10670
  if( !name && !options.label )
10222
10671
  {
10223
- throw( "Set Widget Name or at least a label!" );
10672
+ throw( "Set Component Name or at least a label!" );
10224
10673
  }
10225
10674
 
10226
- super( Widget.CHECKBOX, name, value, options );
10675
+ super( BaseComponent.CHECKBOX, name, value, options );
10227
10676
 
10228
10677
  this.onGetValue = () => {
10229
10678
  return value;
@@ -10294,19 +10743,19 @@ LX.Checkbox = Checkbox;
10294
10743
 
10295
10744
  /**
10296
10745
  * @class Toggle
10297
- * @description Toggle Widget
10746
+ * @description Toggle Component
10298
10747
  */
10299
10748
 
10300
- class Toggle extends Widget {
10749
+ class Toggle extends BaseComponent {
10301
10750
 
10302
10751
  constructor( name, value, callback, options = {} ) {
10303
10752
 
10304
10753
  if( !name && !options.label )
10305
10754
  {
10306
- throw( "Set Widget Name or at least a label!" );
10755
+ throw( "Set Component Name or at least a label!" );
10307
10756
  }
10308
10757
 
10309
- super( Widget.TOGGLE, name, value, options );
10758
+ super( BaseComponent.TOGGLE, name, value, options );
10310
10759
 
10311
10760
  this.onGetValue = () => {
10312
10761
  return toggle.checked;
@@ -10378,14 +10827,14 @@ LX.Toggle = Toggle;
10378
10827
 
10379
10828
  /**
10380
10829
  * @class RadioGroup
10381
- * @description RadioGroup Widget
10830
+ * @description RadioGroup Component
10382
10831
  */
10383
10832
 
10384
- class RadioGroup extends Widget {
10833
+ class RadioGroup extends BaseComponent {
10385
10834
 
10386
10835
  constructor( name, label, values, callback, options = {} ) {
10387
10836
 
10388
- super( Widget.RADIO, name, null, options );
10837
+ super( BaseComponent.RADIO, name, null, options );
10389
10838
 
10390
10839
  let currentIndex = null;
10391
10840
 
@@ -10457,10 +10906,10 @@ LX.RadioGroup = RadioGroup;
10457
10906
 
10458
10907
  /**
10459
10908
  * @class ColorInput
10460
- * @description ColorInput Widget
10909
+ * @description ColorInput Component
10461
10910
  */
10462
10911
 
10463
- class ColorInput extends Widget {
10912
+ class ColorInput extends BaseComponent {
10464
10913
 
10465
10914
  constructor( name, value, callback, options = {} ) {
10466
10915
 
@@ -10469,12 +10918,12 @@ class ColorInput extends Widget {
10469
10918
  const useAlpha = options.useAlpha ??
10470
10919
  ( ( value.constructor === Object && 'a' in value ) || ( value.constructor === String && [ 5, 9 ].includes( value.length ) ) );
10471
10920
 
10472
- const widgetColor = new LX.Color( value );
10921
+ const componentColor = new LX.Color( value );
10473
10922
 
10474
10923
  // Force always hex internally
10475
- value = useAlpha ? widgetColor.hex : widgetColor.hex.substr( 0, 7 );
10924
+ value = useAlpha ? componentColor.hex : componentColor.hex.substr( 0, 7 );
10476
10925
 
10477
- super( Widget.COLOR, name, value, options );
10926
+ super( BaseComponent.COLOR, name, value, options );
10478
10927
 
10479
10928
  this.onGetValue = () => {
10480
10929
  const currentColor = new LX.Color( value );
@@ -10494,7 +10943,7 @@ class ColorInput extends Widget {
10494
10943
 
10495
10944
  if( !this._skipTextUpdate )
10496
10945
  {
10497
- textWidget.set( value, true, event );
10946
+ textComponent.set( value, true, event );
10498
10947
  }
10499
10948
 
10500
10949
  if( !skipCallback )
@@ -10562,15 +11011,15 @@ class ColorInput extends Widget {
10562
11011
  colorSampleRGB.style.width = "18px";
10563
11012
  }
10564
11013
 
10565
- const textWidget = new LX.TextInput( null, value, v => {
11014
+ const textComponent = new LX.TextInput( null, value, v => {
10566
11015
  this._skipTextUpdate = true;
10567
11016
  this.set( v );
10568
11017
  delete this._skipTextUpdate;
10569
11018
  this.picker.fromHexColor( v );
10570
11019
  }, { width: "calc( 100% - 24px )", disabled: options.disabled });
10571
11020
 
10572
- textWidget.root.style.marginLeft = "6px";
10573
- container.appendChild( textWidget.root );
11021
+ textComponent.root.style.marginLeft = "6px";
11022
+ container.appendChild( textComponent.root );
10574
11023
 
10575
11024
  LX.doAsync( this.onResize.bind( this ) );
10576
11025
  }
@@ -10580,45 +11029,117 @@ LX.ColorInput = ColorInput;
10580
11029
 
10581
11030
  /**
10582
11031
  * @class RangeInput
10583
- * @description RangeInput Widget
11032
+ * @description RangeInput Component
10584
11033
  */
10585
11034
 
10586
- class RangeInput extends Widget {
11035
+ class RangeInput extends BaseComponent {
10587
11036
 
10588
11037
  constructor( name, value, callback, options = {} ) {
10589
11038
 
10590
- super( Widget.RANGE, name, value, options );
11039
+ const ogValue = LX.deepCopy( value );
11040
+
11041
+ super( BaseComponent.RANGE, name, LX.deepCopy( ogValue ), options );
11042
+
11043
+ const isRangeValue = ( value.constructor == Array && value.length == 2 );
11044
+ if( isRangeValue )
11045
+ {
11046
+ value = ogValue[ 0 ];
11047
+ options.fill = false; // Range inputs do not fill by default
11048
+ }
10591
11049
 
10592
11050
  this.onGetValue = () => {
10593
- return value;
11051
+ let finalValue = value;
11052
+ if( isRangeValue )
11053
+ {
11054
+ finalValue = [ value, ogValue[ 1 ] ];
11055
+ }
11056
+ else if( options.left )
11057
+ {
11058
+ finalValue = ( ( +slider.max ) - value + ( +slider.min ) );
11059
+ }
11060
+ return finalValue;
10594
11061
  };
10595
11062
 
10596
11063
  this.onSetValue = ( newValue, skipCallback, event ) => {
10597
11064
 
10598
- if( isNaN( newValue ) )
11065
+ let newTpContent = "";
11066
+
11067
+ const diff = ( options.max - options.min );
11068
+
11069
+ if( isRangeValue )
10599
11070
  {
10600
- return;
11071
+ slider.value = value = LX.clamp( +newValue[ 0 ], +slider.min, +slider.max );
11072
+ this._maxSlider.value = ogValue[ 1 ] = LX.clamp( +newValue[ 1 ], +slider.min, +slider.max );
11073
+
11074
+ // Update the range slider
11075
+ const diffOffset = ( value / diff ) - 0.5;
11076
+ const diffMaxOffset = ( ogValue[ 1 ] / diff ) - 0.5;
11077
+ const remappedMin = LX.remapRange( value, options.min, options.max, 0, 1 );
11078
+ const remappedMax = LX.remapRange( ogValue[ 1 ], options.min, options.max, 0, 1 );
11079
+ slider.style.setProperty("--range-min-value", `${ remappedMin * 100 }%`);
11080
+ slider.style.setProperty("--range-max-value", `${ remappedMax * 100 }%`);
11081
+ slider.style.setProperty("--range-fix-min-offset", `${ -diffOffset }rem`);
11082
+ slider.style.setProperty("--range-fix-max-offset", `${ diffMaxOffset }rem`);
11083
+
11084
+ container.dataset[ "tooltipOffsetX" ] = container.offsetWidth * remappedMin + container.offsetWidth * ( remappedMax - remappedMin ) * 0.5 - ( container.offsetWidth * 0.5 );
11085
+ newTpContent = `${ value } - ${ ogValue[ 1 ] }`;
11086
+ }
11087
+ else
11088
+ {
11089
+ if( isNaN( newValue ) )
11090
+ {
11091
+ return;
11092
+ }
11093
+
11094
+ slider.value = value = LX.clamp( +newValue, +slider.min, +slider.max );
11095
+ const remapped = LX.remapRange( value, options.min, options.max, 0, 1 ) * 0.5;
11096
+ container.dataset[ "tooltipOffsetX" ] = container.offsetWidth * remapped - ( container.offsetWidth * 0.5 );
11097
+ newTpContent = `${ value }`;
10601
11098
  }
10602
11099
 
10603
- slider.value = value = LX.clamp( +newValue, +slider.min, +slider.max );
11100
+ container.dataset[ "tooltipContent" ] = newTpContent;
11101
+ if( this._labelTooltip )
11102
+ {
11103
+ this._labelTooltip.innerHTML = newTpContent;
11104
+ }
10604
11105
 
10605
11106
  if( !skipCallback )
10606
11107
  {
10607
- this._trigger( new LX.IEvent( name, options.left ? ( ( +slider.max ) - value + ( +slider.min ) ) : value, event ), callback );
11108
+ let finalValue = value;
11109
+ if( isRangeValue )
11110
+ {
11111
+ finalValue = [ value, ogValue[ 1 ] ];
11112
+ }
11113
+ else if( options.left )
11114
+ {
11115
+ finalValue = ( ( +slider.max ) - value + ( +slider.min ) );
11116
+ }
11117
+
11118
+ this._trigger( new LX.IEvent( name, finalValue, event ), callback );
10608
11119
  }
10609
11120
  };
10610
11121
 
10611
11122
  this.onResize = ( rect ) => {
10612
11123
  const realNameWidth = ( this.root.domName?.style.width ?? "0px" );
10613
11124
  container.style.width = options.inputWidth ?? `calc( 100% - ${ realNameWidth })`;
11125
+ if( isRangeValue )
11126
+ {
11127
+ const diff = ( options.max - options.min );
11128
+ const diffOffset = ( value / diff ) - 0.5;
11129
+ const diffMaxOffset = ( ogValue[ 1 ] / diff ) - 0.5;
11130
+ slider.style.setProperty("--range-min-value", `${ LX.remapRange( value, options.min, options.max, 0, 1 ) * 100 }%`);
11131
+ slider.style.setProperty("--range-max-value", `${ LX.remapRange( ogValue[ 1 ], options.min, options.max, 0, 1 ) * 100 }%`);
11132
+ slider.style.setProperty("--range-fix-min-offset", `${ -diffOffset }rem`);
11133
+ slider.style.setProperty("--range-fix-max-offset", `${ diffMaxOffset }rem`);
11134
+ }
10614
11135
  };
10615
11136
 
10616
11137
  const container = document.createElement( 'div' );
10617
- container.className = "lexrange";
11138
+ container.className = "lexrange relative";
10618
11139
  this.root.appendChild( container );
10619
11140
 
10620
11141
  let slider = document.createElement( 'input' );
10621
- slider.className = "lexrangeslider " + ( options.className ?? "" );
11142
+ slider.className = "lexrangeslider " + ( isRangeValue ? "pointer-events-none " : "" ) + ( options.className ?? "" );
10622
11143
  slider.min = options.min ?? 0;
10623
11144
  slider.max = options.max ?? 100;
10624
11145
  slider.step = options.step ?? 1;
@@ -10630,16 +11151,9 @@ class RangeInput extends Widget {
10630
11151
  value = LX.clamp( value, +slider.min, +slider.max );
10631
11152
  }
10632
11153
 
10633
- if( options.left )
10634
- {
10635
- value = ( ( +slider.max ) - value + ( +slider.min ) );
10636
- }
10637
-
10638
- slider.value = value;
10639
- container.appendChild( slider );
10640
-
10641
11154
  if( options.left ?? false )
10642
11155
  {
11156
+ value = ( ( +slider.max ) - value + ( +slider.min ) );
10643
11157
  slider.classList.add( "left" );
10644
11158
  }
10645
11159
 
@@ -10648,33 +11162,80 @@ class RangeInput extends Widget {
10648
11162
  slider.classList.add( "no-fill" );
10649
11163
  }
10650
11164
 
11165
+ slider.value = value;
11166
+ container.appendChild( slider );
11167
+
10651
11168
  slider.addEventListener( "input", e => {
10652
- this.set( e.target.valueAsNumber, false, e );
11169
+ this.set( isRangeValue ? [ e.target.valueAsNumber, ogValue[ 1 ] ] : e.target.valueAsNumber, false, e );
10653
11170
  }, { passive: false });
10654
11171
 
10655
- slider.addEventListener( "mousedown", function( e ) {
10656
- if( options.onPress )
10657
- {
10658
- options.onPress.bind( slider )( e, slider );
10659
- }
10660
- }, false );
11172
+ // If its a range value, we need to update the slider using the thumbs
11173
+ if( !isRangeValue )
11174
+ {
11175
+ slider.addEventListener( "mousedown", function( e ) {
11176
+ if( options.onPress )
11177
+ {
11178
+ options.onPress.bind( slider )( e, slider );
11179
+ }
11180
+ }, false );
10661
11181
 
10662
- slider.addEventListener( "mouseup", function( e ) {
10663
- if( options.onRelease )
10664
- {
10665
- options.onRelease.bind( slider )( e, slider );
10666
- }
10667
- }, false );
11182
+ slider.addEventListener( "mouseup", function( e ) {
11183
+ if( options.onRelease )
11184
+ {
11185
+ options.onRelease.bind( slider )( e, slider );
11186
+ }
11187
+ }, false );
11188
+ }
10668
11189
 
10669
11190
  // Method to change min, max, step parameters
10670
11191
  this.setLimits = ( newMin, newMax, newStep ) => {
10671
11192
  slider.min = newMin ?? slider.min;
10672
11193
  slider.max = newMax ?? slider.max;
10673
11194
  slider.step = newStep ?? slider.step;
10674
- Widget._dispatchEvent( slider, "input", true );
11195
+ BaseComponent._dispatchEvent( slider, "input", true );
10675
11196
  };
10676
11197
 
10677
- LX.doAsync( this.onResize.bind( this ) );
11198
+ LX.doAsync( () => {
11199
+
11200
+ this.onResize();
11201
+
11202
+ let offsetX = 0;
11203
+ if( isRangeValue )
11204
+ {
11205
+ const remappedMin = LX.remapRange( value, options.min, options.max, 0, 1 );
11206
+ const remappedMax = LX.remapRange( ogValue[ 1 ], options.min, options.max, 0, 1 );
11207
+ offsetX = container.offsetWidth * remappedMin + container.offsetWidth * ( remappedMax - remappedMin ) * 0.5 - ( container.offsetWidth * 0.5 );
11208
+ }
11209
+ else
11210
+ {
11211
+ const remapped = LX.remapRange( value, options.min, options.max, 0, 1 ) * 0.5;
11212
+ offsetX = container.offsetWidth * remapped - ( container.offsetWidth * 0.5 );
11213
+ }
11214
+ LX.asTooltip( container, `${ value }${ isRangeValue ? `- ${ ogValue[ 1 ] }` : `` }`, { offsetX, callback: ( tpDom ) => {
11215
+ this._labelTooltip = tpDom;
11216
+ } } );
11217
+ } );
11218
+
11219
+ if( ogValue.constructor == Array ) // Its a range value
11220
+ {
11221
+ let maxSlider = document.createElement( 'input' );
11222
+ maxSlider.className = "lexrangeslider no-fill pointer-events-none overlap absolute top-0 left-0 " + ( options.className ?? "" );
11223
+ maxSlider.min = options.min ?? 0;
11224
+ maxSlider.max = options.max ?? 100;
11225
+ maxSlider.step = options.step ?? 1;
11226
+ maxSlider.type = "range";
11227
+ maxSlider.disabled = options.disabled ?? false;
11228
+ this._maxSlider = maxSlider;
11229
+
11230
+ let maxRangeValue = ogValue[ 1 ];
11231
+ maxSlider.value = maxRangeValue = LX.clamp( maxRangeValue, +maxSlider.min, +maxSlider.max );
11232
+ container.appendChild( maxSlider );
11233
+
11234
+ maxSlider.addEventListener( "input", e => {
11235
+ ogValue[ 1 ] = +e.target.valueAsNumber;
11236
+ this.set( [ value, ogValue[ 1 ] ], false, e );
11237
+ }, { passive: false });
11238
+ }
10678
11239
  }
10679
11240
  }
10680
11241
 
@@ -10682,14 +11243,14 @@ LX.RangeInput = RangeInput;
10682
11243
 
10683
11244
  /**
10684
11245
  * @class NumberInput
10685
- * @description NumberInput Widget
11246
+ * @description NumberInput Component
10686
11247
  */
10687
11248
 
10688
- class NumberInput extends Widget {
11249
+ class NumberInput extends BaseComponent {
10689
11250
 
10690
11251
  constructor( name, value, callback, options = {} ) {
10691
11252
 
10692
- super( Widget.NUMBER, name, value, options );
11253
+ super( BaseComponent.NUMBER, name, value, options );
10693
11254
 
10694
11255
  this.onGetValue = () => {
10695
11256
  return value;
@@ -10766,7 +11327,7 @@ class NumberInput extends Widget {
10766
11327
  // Add slider below
10767
11328
  if( !options.skipSlider && options.min !== undefined && options.max !== undefined )
10768
11329
  {
10769
- let sliderBox = LX.makeContainer( [ "100%", "auto" ], "", "", box );
11330
+ let sliderBox = LX.makeContainer( [ "100%", "auto" ], "z-1 input-box", "", box );
10770
11331
  let slider = document.createElement( 'input' );
10771
11332
  slider.className = "lexinputslider";
10772
11333
  slider.min = options.min;
@@ -10904,17 +11465,17 @@ LX.NumberInput = NumberInput;
10904
11465
 
10905
11466
  /**
10906
11467
  * @class Vector
10907
- * @description Vector Widget
11468
+ * @description Vector Component
10908
11469
  */
10909
11470
 
10910
- class Vector extends Widget {
11471
+ class Vector extends BaseComponent {
10911
11472
 
10912
11473
  constructor( numComponents, name, value, callback, options = {} ) {
10913
11474
 
10914
11475
  numComponents = LX.clamp( numComponents, 2, 4 );
10915
11476
  value = value ?? new Array( numComponents ).fill( 0 );
10916
11477
 
10917
- super( Widget.VECTOR, name, [].concat( value ), options );
11478
+ super( BaseComponent.VECTOR, name, [].concat( value ), options );
10918
11479
 
10919
11480
  this.onGetValue = () => {
10920
11481
  let inputs = this.root.querySelectorAll( "input[type='number']" );
@@ -11009,13 +11570,13 @@ class Vector extends Widget {
11009
11570
  for( let v of that.querySelectorAll(".vecinput") )
11010
11571
  {
11011
11572
  v.value = LX.round( +v.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ), options.precision );
11012
- Widget._dispatchEvent( v, "change" );
11573
+ BaseComponent._dispatchEvent( v, "change" );
11013
11574
  }
11014
11575
  }
11015
11576
  else
11016
11577
  {
11017
11578
  this.value = LX.round( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ), options.precision );
11018
- Widget._dispatchEvent( vecinput, "change" );
11579
+ BaseComponent._dispatchEvent( vecinput, "change" );
11019
11580
  }
11020
11581
  }, { passive: false } );
11021
11582
 
@@ -11089,13 +11650,13 @@ class Vector extends Widget {
11089
11650
  for( let v of that.root.querySelectorAll( ".vecinput" ) )
11090
11651
  {
11091
11652
  v.value = LX.round( +v.valueAsNumber + mult * dt, options.precision );
11092
- Widget._dispatchEvent( v, "change" );
11653
+ BaseComponent._dispatchEvent( v, "change" );
11093
11654
  }
11094
11655
  }
11095
11656
  else
11096
11657
  {
11097
11658
  vecinput.value = LX.round( +vecinput.valueAsNumber + mult * dt, options.precision );
11098
- Widget._dispatchEvent( vecinput, "change" );
11659
+ BaseComponent._dispatchEvent( vecinput, "change" );
11099
11660
  }
11100
11661
  }
11101
11662
 
@@ -11154,14 +11715,14 @@ LX.Vector = Vector;
11154
11715
 
11155
11716
  /**
11156
11717
  * @class SizeInput
11157
- * @description SizeInput Widget
11718
+ * @description SizeInput Component
11158
11719
  */
11159
11720
 
11160
- class SizeInput extends Widget {
11721
+ class SizeInput extends BaseComponent {
11161
11722
 
11162
11723
  constructor( name, value, callback, options = {} ) {
11163
11724
 
11164
- super( Widget.SIZE, name, value, options );
11725
+ super( BaseComponent.SIZE, name, value, options );
11165
11726
 
11166
11727
  this.onGetValue = () => {
11167
11728
  const value = [];
@@ -11242,10 +11803,10 @@ LX.SizeInput = SizeInput;
11242
11803
 
11243
11804
  /**
11244
11805
  * @class OTPInput
11245
- * @description OTPInput Widget
11806
+ * @description OTPInput Component
11246
11807
  */
11247
11808
 
11248
- class OTPInput extends Widget {
11809
+ class OTPInput extends BaseComponent {
11249
11810
 
11250
11811
  constructor( name, value, callback, options = {} ) {
11251
11812
 
@@ -11258,7 +11819,7 @@ class OTPInput extends Widget {
11258
11819
  value = "x".repeat( patternSize );
11259
11820
  }
11260
11821
 
11261
- super( Widget.OTP, name, value, options );
11822
+ super( BaseComponent.OTP, name, value, options );
11262
11823
 
11263
11824
  this.onGetValue = () => {
11264
11825
  return +value;
@@ -11400,14 +11961,14 @@ LX.OTPInput = OTPInput;
11400
11961
 
11401
11962
  /**
11402
11963
  * @class Pad
11403
- * @description Pad Widget
11964
+ * @description Pad Component
11404
11965
  */
11405
11966
 
11406
- class Pad extends Widget {
11967
+ class Pad extends BaseComponent {
11407
11968
 
11408
11969
  constructor( name, value, callback, options = {} ) {
11409
11970
 
11410
- super( Widget.PAD, name, null, options );
11971
+ super( BaseComponent.PAD, name, null, options );
11411
11972
 
11412
11973
  this.onGetValue = () => {
11413
11974
  return thumb.value.xy;
@@ -11520,14 +12081,14 @@ LX.Pad = Pad;
11520
12081
 
11521
12082
  /**
11522
12083
  * @class Progress
11523
- * @description Progress Widget
12084
+ * @description Progress Component
11524
12085
  */
11525
12086
 
11526
- class Progress extends Widget {
12087
+ class Progress extends BaseComponent {
11527
12088
 
11528
12089
  constructor( name, value, options = {} ) {
11529
12090
 
11530
- super( Widget.PROGRESS, name, value, options );
12091
+ super( BaseComponent.PROGRESS, name, value, options );
11531
12092
 
11532
12093
  this.onGetValue = () => {
11533
12094
  return progress.value;
@@ -11657,14 +12218,14 @@ LX.Progress = Progress;
11657
12218
 
11658
12219
  /**
11659
12220
  * @class FileInput
11660
- * @description FileInput Widget
12221
+ * @description FileInput Component
11661
12222
  */
11662
12223
 
11663
- class FileInput extends Widget {
12224
+ class FileInput extends BaseComponent {
11664
12225
 
11665
12226
  constructor( name, callback, options = { } ) {
11666
12227
 
11667
- super( Widget.FILE, name, null, options );
12228
+ super( BaseComponent.FILE, name, null, options );
11668
12229
 
11669
12230
  let local = options.local ?? true;
11670
12231
  let type = options.type ?? 'text';
@@ -11742,16 +12303,16 @@ LX.FileInput = FileInput;
11742
12303
 
11743
12304
  /**
11744
12305
  * @class Tree
11745
- * @description Tree Widget
12306
+ * @description Tree Component
11746
12307
  */
11747
12308
 
11748
- class Tree extends Widget {
12309
+ class Tree extends BaseComponent {
11749
12310
 
11750
12311
  constructor( name, data, options = {} ) {
11751
12312
 
11752
12313
  options.hideName = true;
11753
12314
 
11754
- super( Widget.TREE, name, null, options );
12315
+ super( BaseComponent.TREE, name, null, options );
11755
12316
 
11756
12317
  let container = document.createElement('div');
11757
12318
  container.className = "lextree";
@@ -11824,16 +12385,16 @@ LX.Tree = Tree;
11824
12385
 
11825
12386
  /**
11826
12387
  * @class TabSections
11827
- * @description TabSections Widget
12388
+ * @description TabSections Component
11828
12389
  */
11829
12390
 
11830
- class TabSections extends Widget {
12391
+ class TabSections extends BaseComponent {
11831
12392
 
11832
12393
  constructor( name, tabs, options = {} ) {
11833
12394
 
11834
12395
  options.hideName = true;
11835
12396
 
11836
- super( Widget.TABS, name, null, options );
12397
+ super( BaseComponent.TABS, name, null, options );
11837
12398
 
11838
12399
  if( tabs.constructor != Array )
11839
12400
  {
@@ -11879,7 +12440,7 @@ class TabSections extends Widget {
11879
12440
 
11880
12441
  let infoContainer = document.createElement( "div" );
11881
12442
  infoContainer.id = tab.name.replace( /\s/g, '' );
11882
- infoContainer.className = "widgets";
12443
+ infoContainer.className = "components";
11883
12444
  infoContainer.toggleAttribute( "hidden", !( tab.selected ?? false ) );
11884
12445
  container.appendChild( infoContainer );
11885
12446
 
@@ -11888,7 +12449,7 @@ class TabSections extends Widget {
11888
12449
  tabContainer.querySelectorAll( ".lextab" ).forEach( e => { e.classList.remove( "selected" ); } );
11889
12450
  tabEl.classList.add( "selected" );
11890
12451
  // Hide all tabs content
11891
- container.querySelectorAll(".widgets").forEach( e => { e.toggleAttribute( "hidden", true ); } );
12452
+ container.querySelectorAll(".components").forEach( e => { e.toggleAttribute( "hidden", true ); } );
11892
12453
  // Show tab content
11893
12454
  const el = container.querySelector( '#' + infoContainer.id );
11894
12455
  el.toggleAttribute( "hidden" );
@@ -11931,14 +12492,14 @@ LX.TabSections = TabSections;
11931
12492
 
11932
12493
  /**
11933
12494
  * @class Counter
11934
- * @description Counter Widget
12495
+ * @description Counter Component
11935
12496
  */
11936
12497
 
11937
- class Counter extends Widget {
12498
+ class Counter extends BaseComponent {
11938
12499
 
11939
12500
  constructor( name, value, callback, options = { } ) {
11940
12501
 
11941
- super( Widget.COUNTER, name, value, options );
12502
+ super( BaseComponent.COUNTER, name, value, options );
11942
12503
 
11943
12504
  this.onGetValue = () => {
11944
12505
  return counterText.count;
@@ -12001,10 +12562,10 @@ LX.Counter = Counter;
12001
12562
 
12002
12563
  /**
12003
12564
  * @class Table
12004
- * @description Table Widget
12565
+ * @description Table Component
12005
12566
  */
12006
12567
 
12007
- class Table extends Widget {
12568
+ class Table extends BaseComponent {
12008
12569
 
12009
12570
  constructor( name, data, options = { } ) {
12010
12571
 
@@ -12013,7 +12574,7 @@ class Table extends Widget {
12013
12574
  throw( "Data is needed to create a table!" );
12014
12575
  }
12015
12576
 
12016
- super( Widget.TABLE, name, null, options );
12577
+ super( BaseComponent.TABLE, name, null, options );
12017
12578
 
12018
12579
  this.onResize = ( rect ) => {
12019
12580
  const realNameWidth = ( this.root.domName?.style.width ?? "0px" );
@@ -12063,7 +12624,7 @@ class Table extends Widget {
12063
12624
  {
12064
12625
  const filterOptions = LX.deepCopy( options );
12065
12626
  filterOptions.placeholder = `Filter ${ this.filter }...`;
12066
- filterOptions.skipWidget = true;
12627
+ filterOptions.skipComponent = true;
12067
12628
  filterOptions.trigger = "input";
12068
12629
  filterOptions.inputClass = "outline";
12069
12630
 
@@ -12082,9 +12643,9 @@ class Table extends Widget {
12082
12643
 
12083
12644
  for( let f of this.customFilters )
12084
12645
  {
12085
- f.widget = new LX.Button(null, icon.innerHTML + f.name, ( v ) => {
12646
+ f.component = new LX.Button(null, icon.innerHTML + f.name, ( v ) => {
12086
12647
 
12087
- const spanName = f.widget.root.querySelector( "span" );
12648
+ const spanName = f.component.root.querySelector( "span" );
12088
12649
 
12089
12650
  if( f.options )
12090
12651
  {
@@ -12105,7 +12666,7 @@ class Table extends Widget {
12105
12666
  };
12106
12667
  return item;
12107
12668
  } );
12108
- new LX.DropdownMenu( f.widget.root, menuOptions, { side: "bottom", align: "start" });
12669
+ new LX.DropdownMenu( f.component.root, menuOptions, { side: "bottom", align: "start" });
12109
12670
  }
12110
12671
  else if( f.type == "range" )
12111
12672
  {
@@ -12124,12 +12685,14 @@ class Table extends Widget {
12124
12685
  f.start = v;
12125
12686
  const inUse = ( f.start != f.min || f.end != f.max );
12126
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" );
12127
12689
  this.refresh();
12128
12690
  }, { skipSlider: true, min: f.min, max: f.max, step: f.step, units: f.units } );
12129
12691
  panel.addNumber( null, f.end, (v) => {
12130
12692
  f.end = v;
12131
12693
  const inUse = ( f.start != f.min || f.end != f.max );
12132
12694
  spanName.innerHTML = icon.innerHTML + f.name + ( inUse ? separatorHtml + LX.badge( `${ f.start } - ${ f.end } ${ f.units ?? "" }`, "bg-tertiary fg-secondary text-sm border-0" ) : "" );
12695
+ if( inUse ) this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
12133
12696
  this.refresh();
12134
12697
  }, { skipSlider: true, min: f.min, max: f.max, step: f.step, units: f.units } );
12135
12698
  panel.addButton( null, "Reset", () => {
@@ -12142,11 +12705,43 @@ class Table extends Widget {
12142
12705
  };
12143
12706
  panel.refresh();
12144
12707
  container.appendChild( panel.root );
12145
- new LX.Popover( f.widget.root, [ container ], { side: "bottom" } );
12708
+ new LX.Popover( f.component.root, [ container ], { side: "bottom" } );
12709
+ }
12710
+ else if( f.type == "date" )
12711
+ {
12712
+ const container = LX.makeContainer( ["auto", "auto"], "text-md" );
12713
+ const panel = new LX.Panel();
12714
+ LX.makeContainer( ["100%", "auto"], "px-3 p-2 pb-0 text-md font-medium", f.name, container );
12715
+
12716
+ panel.refresh = () => {
12717
+ panel.clear();
12718
+
12719
+ // Generate default value once the filter is used
12720
+ if( !f.default )
12721
+ {
12722
+ const date = new Date();
12723
+ const todayStringDate = `${ date.getDate() }/${ date.getMonth() + 1 }/${ date.getFullYear() }`;
12724
+ f.default = [ todayStringDate, todayStringDate ];
12725
+ }
12726
+
12727
+ const calendar = new LX.CalendarRange( f.value, {
12728
+ onChange: ( dateRange ) => {
12729
+ f.value = dateRange;
12730
+ spanName.innerHTML = icon.innerHTML + f.name + ( separatorHtml + LX.badge( `${ calendar.getFullDate() }`, "bg-tertiary fg-secondary text-sm border-0" ) );
12731
+ this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
12732
+ this.refresh();
12733
+ }
12734
+ });
12735
+
12736
+ panel.attach( calendar );
12737
+ };
12738
+ panel.refresh();
12739
+ container.appendChild( panel.root );
12740
+ new LX.Popover( f.component.root, [ container ], { side: "bottom" } );
12146
12741
  }
12147
12742
 
12148
12743
  }, { buttonClass: "px-2 primary dashed" } );
12149
- headerContainer.appendChild( f.widget.root );
12744
+ headerContainer.appendChild( f.component.root );
12150
12745
  }
12151
12746
 
12152
12747
  this._resetCustomFiltersBtn = new LX.Button(null, "resetButton", ( v ) => {
@@ -12154,12 +12749,16 @@ class Table extends Widget {
12154
12749
  this._resetCustomFiltersBtn.root.classList.add( "hidden" );
12155
12750
  for( let f of this.customFilters )
12156
12751
  {
12157
- f.widget.root.querySelector( "span" ).innerHTML = ( icon.innerHTML + f.name );
12752
+ f.component.root.querySelector( "span" ).innerHTML = ( icon.innerHTML + f.name );
12158
12753
  if( f.type == "range" )
12159
12754
  {
12160
12755
  f.start = f.min;
12161
12756
  f.end = f.max;
12162
12757
  }
12758
+ else if( f.type == "date" )
12759
+ {
12760
+ delete f.default;
12761
+ }
12163
12762
  }
12164
12763
  this.refresh();
12165
12764
  }, { title: "Reset filters", tooltip: true, icon: "X" } );
@@ -12441,7 +13040,30 @@ class Table extends Widget {
12441
13040
  }
12442
13041
  }
12443
13042
  else if( f.type == "date" )
12444
- ;
13043
+ {
13044
+ acfMap[ acfName ] = acfMap[ acfName ] ?? false;
13045
+
13046
+ const filterColIndex = data.head.indexOf( acfName );
13047
+ if( filterColIndex > -1 )
13048
+ {
13049
+ if( !f.default )
13050
+ {
13051
+ const date = new Date();
13052
+ const todayStringDate = `${ date.getDate() }/${ date.getMonth() + 1 }/${ date.getFullYear() }`;
13053
+ f.value = [ todayStringDate, todayStringDate ];
13054
+ acfMap[ acfName ] |= true;
13055
+ continue;
13056
+ }
13057
+
13058
+ f.value = f.value ?? f.default;
13059
+
13060
+ const dateString = bodyData[ filterColIndex ];
13061
+ const date = LX.dateFromDateString( dateString );
13062
+ const minDate = LX.dateFromDateString( f.value[ 0 ] );
13063
+ const maxDate = LX.dateFromDateString( f.value[ 1 ] );
13064
+ acfMap[ acfName ] |= ( date >= minDate ) && ( date <= maxDate );
13065
+ }
13066
+ }
12445
13067
  }
12446
13068
 
12447
13069
  const show = Object.values( acfMap ).reduce( ( e, acc ) => acc *= e, true );
@@ -12686,14 +13308,14 @@ LX.Table = Table;
12686
13308
 
12687
13309
  /**
12688
13310
  * @class DatePicker
12689
- * @description DatePicker Widget
13311
+ * @description DatePicker Component
12690
13312
  */
12691
13313
 
12692
- class DatePicker extends Widget {
13314
+ class DatePicker extends BaseComponent {
12693
13315
 
12694
13316
  constructor( name, dateValue, callback, options = { } ) {
12695
13317
 
12696
- super( Widget.DATE, name, null, options );
13318
+ super( BaseComponent.DATE, name, null, options );
12697
13319
 
12698
13320
  const dateAsRange = ( dateValue?.constructor === Array );
12699
13321
 
@@ -12766,10 +13388,6 @@ class DatePicker extends Widget {
12766
13388
  const calendarIcon = LX.makeIcon( "Calendar" );
12767
13389
  const calendarButton = new LX.Button( null, d0, () => {
12768
13390
  this._popover = new LX.Popover( calendarButton.root, [ this.calendar ] );
12769
- if( dateAsRange )
12770
- {
12771
- Object.assign( this._popover.root.style, { display: "flex", width: "auto" } );
12772
- }
12773
13391
  }, { buttonClass: `flex flex-row px-3 ${ emptyDate ? "" : "fg-tertiary" } justify-between` } );
12774
13392
  calendarButton.root.querySelector( "button" ).appendChild( calendarIcon );
12775
13393
  calendarButton.root.style.width = "100%";
@@ -12784,10 +13402,6 @@ class DatePicker extends Widget {
12784
13402
  const calendarIcon = LX.makeIcon( "Calendar" );
12785
13403
  const calendarButton = new LX.Button( null, d1, () => {
12786
13404
  this._popover = new LX.Popover( calendarButton.root, [ this.calendar ] );
12787
- if( dateAsRange )
12788
- {
12789
- Object.assign( this._popover.root.style, { display: "flex", width: "auto" } );
12790
- }
12791
13405
  }, { buttonClass: `flex flex-row px-3 ${ emptyDate ? "" : "fg-tertiary" } justify-between` } );
12792
13406
  calendarButton.root.querySelector( "button" ).appendChild( calendarIcon );
12793
13407
  calendarButton.root.style.width = "100%";
@@ -12812,14 +13426,14 @@ LX.DatePicker = DatePicker;
12812
13426
 
12813
13427
  /**
12814
13428
  * @class Map2D
12815
- * @description Map2D Widget
13429
+ * @description Map2D Component
12816
13430
  */
12817
13431
 
12818
- class Map2D extends Widget {
13432
+ class Map2D extends BaseComponent {
12819
13433
 
12820
13434
  constructor( name, points, callback, options = {} ) {
12821
13435
 
12822
- super( Widget.MAP2D, name, null, options );
13436
+ super( BaseComponent.MAP2D, name, null, options );
12823
13437
 
12824
13438
  this.onGetValue = () => {
12825
13439
  return this.map2d.weightsObj;
@@ -12857,6 +13471,127 @@ class Map2D extends Widget {
12857
13471
 
12858
13472
  LX.Map2D = Map2D;
12859
13473
 
13474
+ /**
13475
+ * @class Rate
13476
+ * @description Rate Component
13477
+ */
13478
+
13479
+ class Rate extends BaseComponent {
13480
+
13481
+ constructor( name, value, callback, options = {} ) {
13482
+
13483
+ const allowHalf = options.allowHalf ?? false;
13484
+
13485
+ if( !allowHalf)
13486
+ {
13487
+ value = Math.floor( value );
13488
+ }
13489
+
13490
+ super( BaseComponent.RATE, name, value, options );
13491
+
13492
+ this.onGetValue = () => {
13493
+ return value;
13494
+ };
13495
+
13496
+ this.onSetValue = ( newValue, skipCallback, event ) => {
13497
+
13498
+ value = newValue;
13499
+
13500
+ _updateStars( value );
13501
+
13502
+ if( !skipCallback )
13503
+ {
13504
+ this._trigger( new LX.IEvent( name, newValue, event ), callback );
13505
+ }
13506
+ };
13507
+
13508
+ this.onResize = ( rect ) => {
13509
+ const realNameWidth = ( this.root.domName?.style.width ?? "0px" );
13510
+ container.style.width = `calc( 100% - ${ realNameWidth })`;
13511
+ };
13512
+
13513
+ const container = document.createElement('div');
13514
+ container.className = "lexrate relative";
13515
+ this.root.appendChild( container );
13516
+
13517
+ const starsContainer = LX.makeContainer( ["fit-content", "auto"], "flex flex-row gap-1", "", container );
13518
+ const filledStarsContainer = LX.makeContainer( ["fit-content", "auto"], "absolute top-0 flex flex-row gap-1 pointer-events-none", "", container );
13519
+ const halfStarsContainer = LX.makeContainer( ["fit-content", "auto"], "absolute top-0 flex flex-row gap-1 pointer-events-none", "", container );
13520
+
13521
+ starsContainer.addEventListener("mousemove", e => {
13522
+ const star = e.target;
13523
+ const idx = star.dataset["idx"];
13524
+
13525
+ if( idx !== undefined )
13526
+ {
13527
+ const rect = star.getBoundingClientRect();
13528
+ const half = allowHalf && e.offsetX < ( rect.width * 0.5 );
13529
+ _updateStars( idx - ( half ? 0.5 : 0.0 ) );
13530
+ }
13531
+ }, false );
13532
+
13533
+ starsContainer.addEventListener("mouseleave", e => {
13534
+ _updateStars( value );
13535
+ }, false );
13536
+
13537
+ // Create all layers of stars
13538
+
13539
+ for( let i = 0; i < 5; ++i )
13540
+ {
13541
+ const starIcon = LX.makeIcon( "Star", { svgClass: `lg fill-current fg-secondary` } );
13542
+ starIcon.dataset["idx"] = ( i + 1 );
13543
+ starsContainer.appendChild( starIcon );
13544
+
13545
+ starIcon.addEventListener("click", e => {
13546
+ const rect = e.target.getBoundingClientRect();
13547
+ const half = allowHalf && e.offsetX < ( rect.width * 0.5 );
13548
+ this.set( parseFloat( e.target.dataset["idx"] ) - ( half ? 0.5 : 0.0 ) );
13549
+ }, false );
13550
+
13551
+ const filledStarIcon = LX.makeIcon( "Star", { svgClass: `lg fill-current metallicyellow` } );
13552
+ filledStarsContainer.appendChild( filledStarIcon );
13553
+
13554
+ const halfStarIcon = LX.makeIcon( "StarHalf", { svgClass: `lg fill-current metallicyellow` } );
13555
+ halfStarsContainer.appendChild( halfStarIcon );
13556
+ }
13557
+
13558
+ const _updateStars = ( v ) => {
13559
+
13560
+ for( let i = 0; i < 5; ++i )
13561
+ {
13562
+ const filled = ( v > ( i + 0.5 ) );
13563
+ const starIcon = filledStarsContainer.childNodes[ i ];
13564
+ const halfStarIcon = halfStarsContainer.childNodes[ i ];
13565
+ if( filled )
13566
+ {
13567
+ starIcon.style.opacity = 1;
13568
+ }
13569
+ else
13570
+ {
13571
+ starIcon.style.opacity = 0;
13572
+
13573
+ const halfFilled = allowHalf && ( v > i );
13574
+ if( halfFilled )
13575
+ {
13576
+ halfStarIcon.style.opacity = 1;
13577
+ }
13578
+ else
13579
+ {
13580
+ halfStarIcon.style.opacity = 0;
13581
+ }
13582
+
13583
+ }
13584
+ }
13585
+ };
13586
+
13587
+ _updateStars( value );
13588
+
13589
+ LX.doAsync( this.onResize.bind( this ) );
13590
+ }
13591
+ }
13592
+
13593
+ LX.Rate = Rate;
13594
+
12860
13595
  // panel.js @jxarco
12861
13596
 
12862
13597
  /**
@@ -12895,42 +13630,42 @@ class Panel {
12895
13630
 
12896
13631
  this.root = root;
12897
13632
  this.branches = [];
12898
- this.widgets = {};
13633
+ this.components = {};
12899
13634
 
12900
13635
  this._branchOpen = false;
12901
13636
  this._currentBranch = null;
12902
- this._queue = []; // Append widgets in other locations
12903
- this._inlineWidgetsLeft = -1;
12904
- this._inline_queued_container = null;
13637
+ this._queue = []; // Append components in other locations
13638
+ this._inlineComponentsLeft = -1;
13639
+ this._inlineQueuedContainer = null;
12905
13640
  }
12906
13641
 
12907
13642
  get( name ) {
12908
13643
 
12909
- return this.widgets[ name ];
13644
+ return this.components[ name ];
12910
13645
  }
12911
13646
 
12912
13647
  getValue( name ) {
12913
13648
 
12914
- let widget = this.widgets[ name ];
13649
+ let component = this.components[ name ];
12915
13650
 
12916
- if( !widget )
13651
+ if( !component )
12917
13652
  {
12918
- throw( "No widget called " + name );
13653
+ throw( "No component called " + name );
12919
13654
  }
12920
13655
 
12921
- return widget.value();
13656
+ return component.value();
12922
13657
  }
12923
13658
 
12924
13659
  setValue( name, value, skipCallback ) {
12925
13660
 
12926
- let widget = this.widgets[ name ];
13661
+ let component = this.components[ name ];
12927
13662
 
12928
- if( !widget )
13663
+ if( !component )
12929
13664
  {
12930
- throw( "No widget called " + name );
13665
+ throw( "No component called " + name );
12931
13666
  }
12932
13667
 
12933
- return widget.set( value, skipCallback );
13668
+ return component.set( value, skipCallback );
12934
13669
  }
12935
13670
 
12936
13671
  /**
@@ -12951,18 +13686,18 @@ class Panel {
12951
13686
 
12952
13687
  clear() {
12953
13688
 
12954
- this._branchOpen = false;
12955
13689
  this.branches = [];
13690
+ this._branchOpen = false;
12956
13691
  this._currentBranch = null;
12957
13692
 
12958
- for( let w in this.widgets )
13693
+ for( let w in this.components )
12959
13694
  {
12960
- if( this.widgets[ w ].options && this.widgets[ w ].options.signal )
13695
+ if( this.components[ w ].options && this.components[ w ].options.signal )
12961
13696
  {
12962
- const signal = this.widgets[ w ].options.signal;
13697
+ const signal = this.components[ w ].options.signal;
12963
13698
  for( let i = 0; i < LX.signals[signal].length; i++ )
12964
13699
  {
12965
- if( LX.signals[signal][ i ] == this.widgets[ w ] )
13700
+ if( LX.signals[signal][ i ] == this.components[ w ] )
12966
13701
  {
12967
13702
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
12968
13703
  }
@@ -12974,11 +13709,11 @@ class Panel {
12974
13709
  {
12975
13710
  for( let w = 0; w < this.signals.length; w++ )
12976
13711
  {
12977
- let widget = Object.values(this.signals[ w ])[ 0 ];
12978
- let signal = widget.options.signal;
13712
+ let c = Object.values(this.signals[ w ])[ 0 ];
13713
+ let signal = c.options.signal;
12979
13714
  for( let i = 0; i < LX.signals[signal].length; i++ )
12980
13715
  {
12981
- if( LX.signals[signal][ i ] == widget )
13716
+ if( LX.signals[signal][ i ] == c )
12982
13717
  {
12983
13718
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
12984
13719
  }
@@ -12986,46 +13721,46 @@ class Panel {
12986
13721
  }
12987
13722
  }
12988
13723
 
12989
- this.widgets = {};
13724
+ this.components = {};
12990
13725
  this.root.innerHTML = "";
12991
13726
  }
12992
13727
 
12993
13728
  /**
12994
13729
  * @method sameLine
12995
- * @param {Number} number Of widgets that will be placed in the same line
12996
- * @param {String} className Extra class to customize inline widgets parent container
12997
- * @description Next N widgets will be in the same line. If no number, it will inline all until calling nextLine()
13730
+ * @param {Number} numberOfComponents Of components that will be placed in the same line
13731
+ * @param {String} className Extra class to customize inline components parent container
13732
+ * @description Next N components will be in the same line. If no number, it will inline all until calling nextLine()
12998
13733
  */
12999
13734
 
13000
- sameLine( number, className ) {
13735
+ sameLine( numberOfComponents, className ) {
13001
13736
 
13002
- this._inline_queued_container = this.queuedContainer;
13003
- this._inlineWidgetsLeft = ( number ?? Infinity );
13737
+ this._inlineQueuedContainer = this.queuedContainer;
13738
+ this._inlineComponentsLeft = ( numberOfComponents ?? Infinity );
13004
13739
  this._inlineExtraClass = className ?? null;
13005
13740
  }
13006
13741
 
13007
13742
  /**
13008
13743
  * @method endLine
13009
- * @param {String} className Extra class to customize inline widgets parent container
13010
- * @description Stop inlining widgets. Use it only if the number of widgets to be inlined is NOT specified.
13744
+ * @param {String} className Extra class to customize inline components parent container
13745
+ * @description Stop inlining components. Use it only if the number of components to be inlined is NOT specified.
13011
13746
  */
13012
13747
 
13013
13748
  endLine( className ) {
13014
13749
 
13015
13750
  className = className ?? this._inlineExtraClass;
13016
13751
 
13017
- if( this._inlineWidgetsLeft == -1 )
13752
+ if( this._inlineComponentsLeft == -1 )
13018
13753
  {
13019
- console.warn("No pending widgets to be inlined!");
13754
+ console.warn("No pending components to be inlined!");
13020
13755
  return;
13021
13756
  }
13022
13757
 
13023
- this._inlineWidgetsLeft = -1;
13758
+ this._inlineComponentsLeft = -1;
13024
13759
 
13025
13760
  if( !this._inlineContainer )
13026
13761
  {
13027
13762
  this._inlineContainer = document.createElement('div');
13028
- this._inlineContainer.className = "lexinlinewidgets";
13763
+ this._inlineContainer.className = "lexinlinecomponents";
13029
13764
 
13030
13765
  if( className )
13031
13766
  {
@@ -13034,14 +13769,14 @@ class Panel {
13034
13769
  }
13035
13770
 
13036
13771
  // Push all elements single element or Array[element, container]
13037
- for( let item of this._inlineWidgets )
13772
+ for( let item of this._inlineComponents )
13038
13773
  {
13039
13774
  const isPair = ( item.constructor == Array );
13040
13775
 
13041
13776
  if( isPair )
13042
13777
  {
13043
13778
  // eg. an array, inline items appended later to
13044
- if( this._inline_queued_container )
13779
+ if( this._inlineQueuedContainer )
13045
13780
  {
13046
13781
  this._inlineContainer.appendChild( item[ 0 ] );
13047
13782
  }
@@ -13057,7 +13792,7 @@ class Panel {
13057
13792
  }
13058
13793
  }
13059
13794
 
13060
- if( !this._inline_queued_container )
13795
+ if( !this._inlineQueuedContainer )
13061
13796
  {
13062
13797
  if( this._currentBranch )
13063
13798
  {
@@ -13070,10 +13805,10 @@ class Panel {
13070
13805
  }
13071
13806
  else
13072
13807
  {
13073
- this._inline_queued_container.appendChild( this._inlineContainer );
13808
+ this._inlineQueuedContainer.appendChild( this._inlineContainer );
13074
13809
  }
13075
13810
 
13076
- delete this._inlineWidgets;
13811
+ delete this._inlineComponents;
13077
13812
  delete this._inlineContainer;
13078
13813
  delete this._inlineExtraClass;
13079
13814
  }
@@ -13086,7 +13821,7 @@ class Panel {
13086
13821
  * className: Add class to the branch
13087
13822
  * closed: Set branch collapsed/opened [false]
13088
13823
  * icon: Set branch icon (LX.ICONS)
13089
- * filter: Allow filter widgets in branch by name [false]
13824
+ * filter: Allow filter components in branch by name [false]
13090
13825
  */
13091
13826
 
13092
13827
  branch( name, options = {} ) {
@@ -13107,10 +13842,10 @@ class Panel {
13107
13842
  this.branches.push( branch );
13108
13843
  this.root.appendChild( branch.root );
13109
13844
 
13110
- // Add widget filter
13845
+ // Add component filter
13111
13846
  if( options.filter )
13112
13847
  {
13113
- this._addFilter( options.filter, { callback: this._searchWidgets.bind( this, branch.name ) } );
13848
+ this._addFilter( options.filter, { callback: this._searchComponents.bind( this, branch.name ) } );
13114
13849
  }
13115
13850
 
13116
13851
  return branch;
@@ -13126,27 +13861,27 @@ class Panel {
13126
13861
  }
13127
13862
 
13128
13863
  /*
13129
- Panel Widgets
13864
+ Panel Components
13130
13865
  */
13131
13866
 
13132
- _attachWidget( widget, options = {} ) {
13867
+ _attachComponent( component, options = {} ) {
13133
13868
 
13134
- if( widget.name != undefined )
13869
+ if( component.name != undefined )
13135
13870
  {
13136
- this.widgets[ widget.name ] = widget;
13871
+ this.components[ component.name ] = component;
13137
13872
  }
13138
13873
 
13139
- if( widget.options.signal && !widget.name )
13874
+ if( component.options.signal && !component.name )
13140
13875
  {
13141
13876
  if( !this.signals )
13142
13877
  {
13143
13878
  this.signals = [];
13144
13879
  }
13145
13880
 
13146
- this.signals.push( { [ widget.options.signal ]: widget } );
13881
+ this.signals.push( { [ component.options.signal ]: component } );
13147
13882
  }
13148
13883
 
13149
- const _insertWidget = el => {
13884
+ const _insertComponent = el => {
13150
13885
  if( options.container )
13151
13886
  {
13152
13887
  options.container.appendChild( el );
@@ -13155,9 +13890,9 @@ class Panel {
13155
13890
  {
13156
13891
  if( this._currentBranch )
13157
13892
  {
13158
- if( !options.skipWidget )
13893
+ if( !options.skipComponent )
13159
13894
  {
13160
- this._currentBranch.widgets.push( widget );
13895
+ this._currentBranch.components.push( component );
13161
13896
  }
13162
13897
  this._currentBranch.content.appendChild( el );
13163
13898
  }
@@ -13174,54 +13909,54 @@ class Panel {
13174
13909
  }
13175
13910
  };
13176
13911
 
13177
- const _storeWidget = el => {
13912
+ const _storeComponent = el => {
13178
13913
 
13179
13914
  if( !this.queuedContainer )
13180
13915
  {
13181
- this._inlineWidgets.push( el );
13916
+ this._inlineComponents.push( el );
13182
13917
  }
13183
13918
  // Append content to queued tab container
13184
13919
  else
13185
13920
  {
13186
- this._inlineWidgets.push( [ el, this.queuedContainer ] );
13921
+ this._inlineComponents.push( [ el, this.queuedContainer ] );
13187
13922
  }
13188
13923
  };
13189
13924
 
13190
- // Process inline widgets
13191
- if( this._inlineWidgetsLeft > 0 && !options.skipInlineCount )
13925
+ // Process inline components
13926
+ if( this._inlineComponentsLeft > 0 && !options.skipInlineCount )
13192
13927
  {
13193
- if( !this._inlineWidgets )
13928
+ if( !this._inlineComponents )
13194
13929
  {
13195
- this._inlineWidgets = [];
13930
+ this._inlineComponents = [];
13196
13931
  }
13197
13932
 
13198
- // Store widget and its container
13199
- _storeWidget( widget.root );
13933
+ // Store component and its container
13934
+ _storeComponent( component.root );
13200
13935
 
13201
- this._inlineWidgetsLeft--;
13936
+ this._inlineComponentsLeft--;
13202
13937
 
13203
- // Last widget
13204
- if( !this._inlineWidgetsLeft )
13938
+ // Last component
13939
+ if( !this._inlineComponentsLeft )
13205
13940
  {
13206
13941
  this.endLine();
13207
13942
  }
13208
13943
  }
13209
13944
  else
13210
13945
  {
13211
- _insertWidget( widget.root );
13946
+ _insertComponent( component.root );
13212
13947
  }
13213
13948
 
13214
- return widget;
13949
+ return component;
13215
13950
  }
13216
13951
 
13217
13952
  _addFilter( placeholder, options = {} ) {
13218
13953
 
13219
13954
  options.placeholder = placeholder.constructor == String ? placeholder : "Filter properties..";
13220
- options.skipWidget = options.skipWidget ?? true;
13955
+ options.skipComponent = options.skipComponent ?? true;
13221
13956
  options.skipInlineCount = true;
13222
13957
 
13223
- let widget = new LX.TextInput( null, null, null, options );
13224
- const element = widget.root;
13958
+ let component = new LX.TextInput( null, null, null, options );
13959
+ const element = component.root;
13225
13960
  element.className += " lexfilter";
13226
13961
 
13227
13962
  let input = document.createElement('input');
@@ -13244,7 +13979,7 @@ class Panel {
13244
13979
  return element;
13245
13980
  }
13246
13981
 
13247
- _searchWidgets( branchName, value ) {
13982
+ _searchComponents( branchName, value ) {
13248
13983
 
13249
13984
  for( let b of this.branches )
13250
13985
  {
@@ -13253,8 +13988,8 @@ class Panel {
13253
13988
  continue;
13254
13989
  }
13255
13990
 
13256
- // remove all widgets
13257
- for( let w of b.widgets )
13991
+ // remove all components
13992
+ for( let w of b.components )
13258
13993
  {
13259
13994
  if( w.domEl.classList.contains('lexfilter') )
13260
13995
  {
@@ -13268,8 +14003,8 @@ class Panel {
13268
14003
 
13269
14004
  const emptyFilter = !value.length;
13270
14005
 
13271
- // add widgets
13272
- for( let w of b.widgets )
14006
+ // add components
14007
+ for( let w of b.components )
13273
14008
  {
13274
14009
  if( !emptyFilter )
13275
14010
  {
@@ -13279,7 +14014,7 @@ class Panel {
13279
14014
  if(!name.includes(value)) continue;
13280
14015
  }
13281
14016
 
13282
- // insert filtered widget
14017
+ // insert filtered component
13283
14018
  this.queuedContainer.appendChild( w.domEl );
13284
14019
  }
13285
14020
 
@@ -13350,13 +14085,13 @@ class Panel {
13350
14085
  var element = document.createElement('div');
13351
14086
  element.className = "lexseparator";
13352
14087
 
13353
- let widget = new LX.Widget( LX.Widget.SEPARATOR );
13354
- widget.root = element;
14088
+ let component = new LX.BaseComponent( LX.BaseComponent.SEPARATOR );
14089
+ component.root = element;
13355
14090
 
13356
14091
  if( this._currentBranch )
13357
14092
  {
13358
14093
  this._currentBranch.content.appendChild( element );
13359
- this._currentBranch.widgets.push( widget );
14094
+ this._currentBranch.components.push( component );
13360
14095
  }
13361
14096
  else
13362
14097
  {
@@ -13371,8 +14106,8 @@ class Panel {
13371
14106
  */
13372
14107
 
13373
14108
  addBlank( width, height ) {
13374
- const widget = new LX.Blank( width, height );
13375
- return this._attachWidget( widget );
14109
+ const component = new LX.Blank( width, height );
14110
+ return this._attachComponent( component );
13376
14111
  }
13377
14112
 
13378
14113
  /**
@@ -13387,18 +14122,18 @@ class Panel {
13387
14122
  */
13388
14123
 
13389
14124
  addTitle( name, options = {} ) {
13390
- const widget = new LX.Title( name, options );
13391
- return this._attachWidget( widget );
14125
+ const component = new LX.Title( name, options );
14126
+ return this._attachComponent( component );
13392
14127
  }
13393
14128
 
13394
14129
  /**
13395
14130
  * @method addText
13396
- * @param {String} name Widget name
14131
+ * @param {String} name Component name
13397
14132
  * @param {String} value Text value
13398
14133
  * @param {Function} callback Callback function on change
13399
14134
  * @param {Object} options:
13400
14135
  * hideName: Don't use name as label [false]
13401
- * disabled: Make the widget disabled [false]
14136
+ * disabled: Make the component disabled [false]
13402
14137
  * required: Make the input required
13403
14138
  * placeholder: Add input placeholder
13404
14139
  * icon: Icon (if any) to append at the input start
@@ -13413,18 +14148,18 @@ class Panel {
13413
14148
  */
13414
14149
 
13415
14150
  addText( name, value, callback, options = {} ) {
13416
- const widget = new LX.TextInput( name, value, callback, options );
13417
- return this._attachWidget( widget );
14151
+ const component = new LX.TextInput( name, value, callback, options );
14152
+ return this._attachComponent( component );
13418
14153
  }
13419
14154
 
13420
14155
  /**
13421
14156
  * @method addTextArea
13422
- * @param {String} name Widget name
14157
+ * @param {String} name Component name
13423
14158
  * @param {String} value Text Area value
13424
14159
  * @param {Function} callback Callback function on change
13425
14160
  * @param {Object} options:
13426
14161
  * hideName: Don't use name as label [false]
13427
- * disabled: Make the widget disabled [false]
14162
+ * disabled: Make the component disabled [false]
13428
14163
  * placeholder: Add input placeholder
13429
14164
  * resize: Allow resize [true]
13430
14165
  * trigger: Choose onchange trigger (default, input) [default]
@@ -13435,8 +14170,8 @@ class Panel {
13435
14170
  */
13436
14171
 
13437
14172
  addTextArea( name, value, callback, options = {} ) {
13438
- const widget = new LX.TextArea( name, value, callback, options );
13439
- return this._attachWidget( widget );
14173
+ const component = new LX.TextArea( name, value, callback, options );
14174
+ return this._attachComponent( component );
13440
14175
  }
13441
14176
 
13442
14177
  /**
@@ -13448,19 +14183,19 @@ class Panel {
13448
14183
  addLabel( value, options = {} ) {
13449
14184
  options.disabled = true;
13450
14185
  options.inputClass = ( options.inputClass ?? "" ) + " nobg";
13451
- const widget = this.addText( null, value, null, options );
13452
- widget.type = LX.Widget.LABEL;
13453
- return widget;
14186
+ const component = this.addText( null, value, null, options );
14187
+ component.type = LX.BaseComponent.LABEL;
14188
+ return component;
13454
14189
  }
13455
14190
 
13456
14191
  /**
13457
14192
  * @method addButton
13458
- * @param {String} name Widget name
14193
+ * @param {String} name Component name
13459
14194
  * @param {String} value Button name
13460
14195
  * @param {Function} callback Callback function on click
13461
14196
  * @param {Object} options:
13462
14197
  * hideName: Don't use name as label [false]
13463
- * disabled: Make the widget disabled [false]
14198
+ * disabled: Make the component disabled [false]
13464
14199
  * icon: Icon class to show as button value
13465
14200
  * iconPosition: Icon position (cover|start|end)
13466
14201
  * fileInput: Button click requests a file
@@ -13472,13 +14207,13 @@ class Panel {
13472
14207
  */
13473
14208
 
13474
14209
  addButton( name, value, callback, options = {} ) {
13475
- const widget = new LX.Button( name, value, callback, options );
13476
- return this._attachWidget( widget );
14210
+ const component = new LX.Button( name, value, callback, options );
14211
+ return this._attachComponent( component );
13477
14212
  }
13478
14213
 
13479
14214
  /**
13480
14215
  * @method addComboButtons
13481
- * @param {String} name Widget name
14216
+ * @param {String} name Component name
13482
14217
  * @param {Array} values Each of the {value, callback, selected, disabled} items
13483
14218
  * @param {Object} options:
13484
14219
  * hideName: Don't use name as label [false]
@@ -13489,8 +14224,8 @@ class Panel {
13489
14224
  */
13490
14225
 
13491
14226
  addComboButtons( name, values, options = {} ) {
13492
- const widget = new LX.ComboButtons( name, values, options );
13493
- return this._attachWidget( widget );
14227
+ const component = new LX.ComboButtons( name, values, options );
14228
+ return this._attachComponent( component );
13494
14229
  }
13495
14230
 
13496
14231
  /**
@@ -13505,13 +14240,13 @@ class Panel {
13505
14240
  */
13506
14241
 
13507
14242
  addCard( name, options = {} ) {
13508
- const widget = new LX.Card( name, options );
13509
- return this._attachWidget( widget );
14243
+ const component = new LX.Card( name, options );
14244
+ return this._attachComponent( component );
13510
14245
  }
13511
14246
 
13512
14247
  /**
13513
14248
  * @method addForm
13514
- * @param {String} name Widget name
14249
+ * @param {String} name Component name
13515
14250
  * @param {Object} data Form data
13516
14251
  * @param {Function} callback Callback function on submit form
13517
14252
  * @param {Object} options:
@@ -13524,13 +14259,13 @@ class Panel {
13524
14259
  */
13525
14260
 
13526
14261
  addForm( name, data, callback, options = {} ) {
13527
- const widget = new LX.Form( name, data, callback, options );
13528
- return this._attachWidget( widget );
14262
+ const component = new LX.Form( name, data, callback, options );
14263
+ return this._attachComponent( component );
13529
14264
  }
13530
14265
 
13531
14266
  /**
13532
14267
  * @method addContent
13533
- * @param {String} name Widget name
14268
+ * @param {String} name Component name
13534
14269
  * @param {HTMLElement/String} element
13535
14270
  * @param {Object} options
13536
14271
  */
@@ -13556,15 +14291,15 @@ class Panel {
13556
14291
 
13557
14292
  options.hideName = true;
13558
14293
 
13559
- let widget = new LX.Widget( LX.Widget.CONTENT, name, null, options );
13560
- widget.root.appendChild( element );
14294
+ let component = new LX.BaseComponent( LX.BaseComponent.CONTENT, name, null, options );
14295
+ component.root.appendChild( element );
13561
14296
 
13562
- return this._attachWidget( widget );
14297
+ return this._attachComponent( component );
13563
14298
  }
13564
14299
 
13565
14300
  /**
13566
14301
  * @method addImage
13567
- * @param {String} name Widget name
14302
+ * @param {String} name Component name
13568
14303
  * @param {String} url Image Url
13569
14304
  * @param {Object} options
13570
14305
  * hideName: Don't use name as label [false]
@@ -13583,43 +14318,43 @@ class Panel {
13583
14318
  Object.assign( img.style, options.style ?? {} );
13584
14319
  container.appendChild( img );
13585
14320
 
13586
- let widget = new LX.Widget( LX.Widget.IMAGE, name, null, options );
13587
- widget.root.appendChild( container );
14321
+ let component = new LX.BaseComponent( LX.BaseComponent.IMAGE, name, null, options );
14322
+ component.root.appendChild( container );
13588
14323
 
13589
14324
  // await img.decode();
13590
14325
  img.decode();
13591
14326
 
13592
- return this._attachWidget( widget );
14327
+ return this._attachComponent( component );
13593
14328
  }
13594
14329
 
13595
14330
  /**
13596
14331
  * @method addSelect
13597
- * @param {String} name Widget name
13598
- * @param {Array} values Posible options of the select widget -> String (for default select) or Object = {value, url} (for images, gifs..)
14332
+ * @param {String} name Component name
14333
+ * @param {Array} values Posible options of the select component -> String (for default select) or Object = {value, url} (for images, gifs..)
13599
14334
  * @param {String} value Select by default option
13600
14335
  * @param {Function} callback Callback function on change
13601
14336
  * @param {Object} options:
13602
14337
  * hideName: Don't use name as label [false]
13603
- * filter: Add a search bar to the widget [false]
13604
- * disabled: Make the widget disabled [false]
14338
+ * filter: Add a search bar to the component [false]
14339
+ * disabled: Make the component disabled [false]
13605
14340
  * skipReset: Don't add the reset value button when value changes
13606
14341
  * placeholder: Placeholder for the filter input
13607
14342
  * emptyMsg: Custom message to show when no filtered results
13608
14343
  */
13609
14344
 
13610
14345
  addSelect( name, values, value, callback, options = {} ) {
13611
- const widget = new LX.Select( name, values, value, callback, options );
13612
- return this._attachWidget( widget );
14346
+ const component = new LX.Select( name, values, value, callback, options );
14347
+ return this._attachComponent( component );
13613
14348
  }
13614
14349
 
13615
14350
  /**
13616
14351
  * @method addCurve
13617
- * @param {String} name Widget name
14352
+ * @param {String} name Component name
13618
14353
  * @param {Array of Array} values Array of 2N Arrays of each value of the curve
13619
14354
  * @param {Function} callback Callback function on change
13620
14355
  * @param {Object} options:
13621
14356
  * skipReset: Don't add the reset value button when value changes
13622
- * bgColor: Widget background color
14357
+ * bgColor: Component background color
13623
14358
  * pointsColor: Curve points color
13624
14359
  * lineColor: Curve line color
13625
14360
  * noOverlap: Points do not overlap, replacing themselves if necessary
@@ -13629,18 +14364,18 @@ class Panel {
13629
14364
  */
13630
14365
 
13631
14366
  addCurve( name, values, callback, options = {} ) {
13632
- const widget = new LX.Curve( name, values, callback, options );
13633
- return this._attachWidget( widget );
14367
+ const component = new LX.Curve( name, values, callback, options );
14368
+ return this._attachComponent( component );
13634
14369
  }
13635
14370
 
13636
14371
  /**
13637
14372
  * @method addDial
13638
- * @param {String} name Widget name
14373
+ * @param {String} name Component name
13639
14374
  * @param {Array of Array} values Array of 2N Arrays of each value of the dial
13640
14375
  * @param {Function} callback Callback function on change
13641
14376
  * @param {Object} options:
13642
14377
  * skipReset: Don't add the reset value button when value changes
13643
- * bgColor: Widget background color
14378
+ * bgColor: Component background color
13644
14379
  * pointsColor: Curve points color
13645
14380
  * lineColor: Curve line color
13646
14381
  * noOverlap: Points do not overlap, replacing themselves if necessary
@@ -13650,26 +14385,26 @@ class Panel {
13650
14385
  */
13651
14386
 
13652
14387
  addDial( name, values, callback, options = {} ) {
13653
- const widget = new LX.Dial( name, values, callback, options );
13654
- return this._attachWidget( widget );
14388
+ const component = new LX.Dial( name, values, callback, options );
14389
+ return this._attachComponent( component );
13655
14390
  }
13656
14391
 
13657
14392
  /**
13658
14393
  * @method addLayers
13659
- * @param {String} name Widget name
14394
+ * @param {String} name Component name
13660
14395
  * @param {Number} value Flag value by default option
13661
14396
  * @param {Function} callback Callback function on change
13662
14397
  * @param {Object} options:
13663
14398
  */
13664
14399
 
13665
14400
  addLayers( name, value, callback, options = {} ) {
13666
- const widget = new LX.Layers( name, value, callback, options );
13667
- return this._attachWidget( widget );
14401
+ const component = new LX.Layers( name, value, callback, options );
14402
+ return this._attachComponent( component );
13668
14403
  }
13669
14404
 
13670
14405
  /**
13671
14406
  * @method addArray
13672
- * @param {String} name Widget name
14407
+ * @param {String} name Component name
13673
14408
  * @param {Array} values By default values in the array
13674
14409
  * @param {Function} callback Callback function on change
13675
14410
  * @param {Object} options:
@@ -13677,13 +14412,13 @@ class Panel {
13677
14412
  */
13678
14413
 
13679
14414
  addArray( name, values = [], callback, options = {} ) {
13680
- const widget = new LX.ItemArray( name, values, callback, options );
13681
- return this._attachWidget( widget );
14415
+ const component = new LX.ItemArray( name, values, callback, options );
14416
+ return this._attachComponent( component );
13682
14417
  }
13683
14418
 
13684
14419
  /**
13685
14420
  * @method addList
13686
- * @param {String} name Widget name
14421
+ * @param {String} name Component name
13687
14422
  * @param {Array} values List values
13688
14423
  * @param {String} value Selected list value
13689
14424
  * @param {Function} callback Callback function on change
@@ -13692,13 +14427,13 @@ class Panel {
13692
14427
  */
13693
14428
 
13694
14429
  addList( name, values, value, callback, options = {} ) {
13695
- const widget = new LX.List( name, values, value, callback, options );
13696
- return this._attachWidget( widget );
14430
+ const component = new LX.List( name, values, value, callback, options );
14431
+ return this._attachComponent( component );
13697
14432
  }
13698
14433
 
13699
14434
  /**
13700
14435
  * @method addTags
13701
- * @param {String} name Widget name
14436
+ * @param {String} name Component name
13702
14437
  * @param {String} value Comma separated tags
13703
14438
  * @param {Function} callback Callback function on change
13704
14439
  * @param {Object} options:
@@ -13706,85 +14441,85 @@ class Panel {
13706
14441
  */
13707
14442
 
13708
14443
  addTags( name, value, callback, options = {} ) {
13709
- const widget = new LX.Tags( name, value, callback, options );
13710
- return this._attachWidget( widget );
14444
+ const component = new LX.Tags( name, value, callback, options );
14445
+ return this._attachComponent( component );
13711
14446
  }
13712
14447
 
13713
14448
  /**
13714
14449
  * @method addCheckbox
13715
- * @param {String} name Widget name
14450
+ * @param {String} name Component name
13716
14451
  * @param {Boolean} value Value of the checkbox
13717
14452
  * @param {Function} callback Callback function on change
13718
14453
  * @param {Object} options:
13719
- * disabled: Make the widget disabled [false]
14454
+ * disabled: Make the component disabled [false]
13720
14455
  * label: Checkbox label
13721
- * suboptions: Callback to add widgets in case of TRUE value
14456
+ * suboptions: Callback to add components in case of TRUE value
13722
14457
  * className: Extra classes to customize style
13723
14458
  */
13724
14459
 
13725
14460
  addCheckbox( name, value, callback, options = {} ) {
13726
- const widget = new LX.Checkbox( name, value, callback, options );
13727
- return this._attachWidget( widget );
14461
+ const component = new LX.Checkbox( name, value, callback, options );
14462
+ return this._attachComponent( component );
13728
14463
  }
13729
14464
 
13730
14465
  /**
13731
14466
  * @method addToggle
13732
- * @param {String} name Widget name
14467
+ * @param {String} name Component name
13733
14468
  * @param {Boolean} value Value of the checkbox
13734
14469
  * @param {Function} callback Callback function on change
13735
14470
  * @param {Object} options:
13736
- * disabled: Make the widget disabled [false]
14471
+ * disabled: Make the component disabled [false]
13737
14472
  * label: Toggle label
13738
- * suboptions: Callback to add widgets in case of TRUE value
14473
+ * suboptions: Callback to add components in case of TRUE value
13739
14474
  * className: Customize colors
13740
14475
  */
13741
14476
 
13742
14477
  addToggle( name, value, callback, options = {} ) {
13743
- const widget = new LX.Toggle( name, value, callback, options );
13744
- return this._attachWidget( widget );
14478
+ const component = new LX.Toggle( name, value, callback, options );
14479
+ return this._attachComponent( component );
13745
14480
  }
13746
14481
 
13747
14482
  /**
13748
14483
  * @method addRadioGroup
13749
- * @param {String} name Widget name
14484
+ * @param {String} name Component name
13750
14485
  * @param {String} label Radio label
13751
14486
  * @param {Array} values Radio options
13752
14487
  * @param {Function} callback Callback function on change
13753
14488
  * @param {Object} options:
13754
- * disabled: Make the widget disabled [false]
14489
+ * disabled: Make the component disabled [false]
13755
14490
  * className: Customize colors
13756
14491
  * selected: Index of the default selected option
13757
14492
  */
13758
14493
 
13759
14494
  addRadioGroup( name, label, values, callback, options = {} ) {
13760
- const widget = new LX.RadioGroup( name, label, values, callback, options );
13761
- return this._attachWidget( widget );
14495
+ const component = new LX.RadioGroup( name, label, values, callback, options );
14496
+ return this._attachComponent( component );
13762
14497
  }
13763
14498
 
13764
14499
  /**
13765
14500
  * @method addColor
13766
- * @param {String} name Widget name
14501
+ * @param {String} name Component name
13767
14502
  * @param {String} value Default color (hex)
13768
14503
  * @param {Function} callback Callback function on change
13769
14504
  * @param {Object} options:
13770
- * disabled: Make the widget disabled [false]
14505
+ * disabled: Make the component disabled [false]
13771
14506
  * useRGB: The callback returns color as Array (r, g, b) and not hex [false]
13772
14507
  */
13773
14508
 
13774
14509
  addColor( name, value, callback, options = {} ) {
13775
- const widget = new LX.ColorInput( name, value, callback, options );
13776
- return this._attachWidget( widget );
14510
+ const component = new LX.ColorInput( name, value, callback, options );
14511
+ return this._attachComponent( component );
13777
14512
  }
13778
14513
 
13779
14514
  /**
13780
14515
  * @method addRange
13781
- * @param {String} name Widget name
14516
+ * @param {String} name Component name
13782
14517
  * @param {Number} value Default number value
13783
14518
  * @param {Function} callback Callback function on change
13784
14519
  * @param {Object} options:
13785
14520
  * hideName: Don't use name as label [false]
13786
14521
  * className: Extra classes to customize style
13787
- * disabled: Make the widget disabled [false]
14522
+ * disabled: Make the component disabled [false]
13788
14523
  * left: The slider goes to the left instead of the right
13789
14524
  * fill: Fill slider progress [true]
13790
14525
  * step: Step of the input
@@ -13792,18 +14527,18 @@ class Panel {
13792
14527
  */
13793
14528
 
13794
14529
  addRange( name, value, callback, options = {} ) {
13795
- const widget = new LX.RangeInput( name, value, callback, options );
13796
- return this._attachWidget( widget );
14530
+ const component = new LX.RangeInput( name, value, callback, options );
14531
+ return this._attachComponent( component );
13797
14532
  }
13798
14533
 
13799
14534
  /**
13800
14535
  * @method addNumber
13801
- * @param {String} name Widget name
14536
+ * @param {String} name Component name
13802
14537
  * @param {Number} value Default number value
13803
14538
  * @param {Function} callback Callback function on change
13804
14539
  * @param {Object} options:
13805
14540
  * hideName: Don't use name as label [false]
13806
- * disabled: Make the widget disabled [false]
14541
+ * disabled: Make the component disabled [false]
13807
14542
  * step: Step of the input
13808
14543
  * precision: The number of digits to appear after the decimal point
13809
14544
  * min, max: Min and Max values for the input
@@ -13814,24 +14549,24 @@ class Panel {
13814
14549
  */
13815
14550
 
13816
14551
  addNumber( name, value, callback, options = {} ) {
13817
- const widget = new LX.NumberInput( name, value, callback, options );
13818
- return this._attachWidget( widget );
14552
+ const component = new LX.NumberInput( name, value, callback, options );
14553
+ return this._attachComponent( component );
13819
14554
  }
13820
14555
 
13821
14556
  static VECTOR_COMPONENTS = { 0: 'x', 1: 'y', 2: 'z', 3: 'w' };
13822
14557
 
13823
14558
  _addVector( numComponents, name, value, callback, options = {} ) {
13824
- const widget = new LX.Vector( numComponents, name, value, callback, options );
13825
- return this._attachWidget( widget );
14559
+ const component = new LX.Vector( numComponents, name, value, callback, options );
14560
+ return this._attachComponent( component );
13826
14561
  }
13827
14562
 
13828
14563
  /**
13829
14564
  * @method addVector N (2, 3, 4)
13830
- * @param {String} name Widget name
14565
+ * @param {String} name Component name
13831
14566
  * @param {Array} value Array of N components
13832
14567
  * @param {Function} callback Callback function on change
13833
14568
  * @param {Object} options:
13834
- * disabled: Make the widget disabled [false]
14569
+ * disabled: Make the component disabled [false]
13835
14570
  * step: Step of the inputs
13836
14571
  * min, max: Min and Max values for the inputs
13837
14572
  * onPress: Callback function on mouse down
@@ -13852,43 +14587,43 @@ class Panel {
13852
14587
 
13853
14588
  /**
13854
14589
  * @method addSize
13855
- * @param {String} name Widget name
14590
+ * @param {String} name Component name
13856
14591
  * @param {Number} value Default number value
13857
14592
  * @param {Function} callback Callback function on change
13858
14593
  * @param {Object} options:
13859
14594
  * hideName: Don't use name as label [false]
13860
- * disabled: Make the widget disabled [false]
14595
+ * disabled: Make the component disabled [false]
13861
14596
  * units: Unit as string added to the end of the value
13862
14597
  */
13863
14598
 
13864
14599
  addSize( name, value, callback, options = {} ) {
13865
- const widget = new LX.SizeInput( name, value, callback, options );
13866
- return this._attachWidget( widget );
14600
+ const component = new LX.SizeInput( name, value, callback, options );
14601
+ return this._attachComponent( component );
13867
14602
  }
13868
14603
 
13869
14604
  /**
13870
14605
  * @method addOTP
13871
- * @param {String} name Widget name
14606
+ * @param {String} name Component name
13872
14607
  * @param {String} value Default numeric value in string format
13873
14608
  * @param {Function} callback Callback function on change
13874
14609
  * @param {Object} options:
13875
14610
  * hideName: Don't use name as label [false]
13876
- * disabled: Make the widget disabled [false]
14611
+ * disabled: Make the component disabled [false]
13877
14612
  * pattern: OTP numeric pattern
13878
14613
  */
13879
14614
 
13880
14615
  addOTP( name, value, callback, options = {} ) {
13881
- const widget = new LX.OTPInput( name, value, callback, options );
13882
- return this._attachWidget( widget );
14616
+ const component = new LX.OTPInput( name, value, callback, options );
14617
+ return this._attachComponent( component );
13883
14618
  }
13884
14619
 
13885
14620
  /**
13886
14621
  * @method addPad
13887
- * @param {String} name Widget name
14622
+ * @param {String} name Component name
13888
14623
  * @param {Array} value Pad value
13889
14624
  * @param {Function} callback Callback function on change
13890
14625
  * @param {Object} options:
13891
- * disabled: Make the widget disabled [false]
14626
+ * disabled: Make the component disabled [false]
13892
14627
  * min, max: Min and Max values
13893
14628
  * padSize: Size of the pad (css)
13894
14629
  * onPress: Callback function on mouse down
@@ -13896,13 +14631,13 @@ class Panel {
13896
14631
  */
13897
14632
 
13898
14633
  addPad( name, value, callback, options = {} ) {
13899
- const widget = new LX.Pad( name, value, callback, options );
13900
- return this._attachWidget( widget );
14634
+ const component = new LX.Pad( name, value, callback, options );
14635
+ return this._attachComponent( component );
13901
14636
  }
13902
14637
 
13903
14638
  /**
13904
14639
  * @method addProgress
13905
- * @param {String} name Widget name
14640
+ * @param {String} name Component name
13906
14641
  * @param {Number} value Progress value
13907
14642
  * @param {Object} options:
13908
14643
  * min, max: Min and Max values
@@ -13913,29 +14648,29 @@ class Panel {
13913
14648
  */
13914
14649
 
13915
14650
  addProgress( name, value, options = {} ) {
13916
- const widget = new LX.Progress( name, value, options );
13917
- return this._attachWidget( widget );
14651
+ const component = new LX.Progress( name, value, options );
14652
+ return this._attachComponent( component );
13918
14653
  }
13919
14654
 
13920
14655
  /**
13921
14656
  * @method addFile
13922
- * @param {String} name Widget name
14657
+ * @param {String} name Component name
13923
14658
  * @param {Function} callback Callback function on change
13924
14659
  * @param {Object} options:
13925
14660
  * local: Ask for local file
13926
- * disabled: Make the widget disabled [false]
14661
+ * disabled: Make the component disabled [false]
13927
14662
  * read: Return the file itself (False) or the contents (True)
13928
14663
  * type: type to read as [text (Default), buffer, bin, url]
13929
14664
  */
13930
14665
 
13931
14666
  addFile( name, callback, options = { } ) {
13932
- const widget = new LX.FileInput( name, callback, options );
13933
- return this._attachWidget( widget );
14667
+ const component = new LX.FileInput( name, callback, options );
14668
+ return this._attachComponent( component );
13934
14669
  }
13935
14670
 
13936
14671
  /**
13937
14672
  * @method addTree
13938
- * @param {String} name Widget name
14673
+ * @param {String} name Component name
13939
14674
  * @param {Object} data Data of the tree
13940
14675
  * @param {Object} options:
13941
14676
  * icons: Array of objects with icon button information {name, icon, callback}
@@ -13945,13 +14680,13 @@ class Panel {
13945
14680
  */
13946
14681
 
13947
14682
  addTree( name, data, options = {} ) {
13948
- const widget = new LX.Tree( name, data, options );
13949
- return this._attachWidget( widget );
14683
+ const component = new LX.Tree( name, data, options );
14684
+ return this._attachComponent( component );
13950
14685
  }
13951
14686
 
13952
14687
  /**
13953
14688
  * @method addTabSections
13954
- * @param {String} name Widget name
14689
+ * @param {String} name Component name
13955
14690
  * @param {Array} tabs Contains objects with {
13956
14691
  * name: Name of the tab (if icon, use as title)
13957
14692
  * icon: Icon to be used as the tab icon (optional)
@@ -13966,30 +14701,30 @@ class Panel {
13966
14701
  */
13967
14702
 
13968
14703
  addTabSections( name, tabs, options = {} ) {
13969
- const widget = new LX.TabSections( name, tabs, options );
13970
- return this._attachWidget( widget );
14704
+ const component = new LX.TabSections( name, tabs, options );
14705
+ return this._attachComponent( component );
13971
14706
  }
13972
14707
 
13973
14708
  /**
13974
14709
  * @method addCounter
13975
- * @param {String} name Widget name
14710
+ * @param {String} name Component name
13976
14711
  * @param {Number} value Counter value
13977
14712
  * @param {Function} callback Callback function on change
13978
14713
  * @param {Object} options:
13979
- * disabled: Make the widget disabled [false]
14714
+ * disabled: Make the component disabled [false]
13980
14715
  * min, max: Min and Max values
13981
14716
  * step: Step for adding/substracting
13982
14717
  * label: Text to show below the counter
13983
14718
  */
13984
14719
 
13985
14720
  addCounter( name, value, callback, options = { } ) {
13986
- const widget = new LX.Counter( name, value, callback, options );
13987
- return this._attachWidget( widget );
14721
+ const component = new LX.Counter( name, value, callback, options );
14722
+ return this._attachComponent( component );
13988
14723
  }
13989
14724
 
13990
14725
  /**
13991
14726
  * @method addTable
13992
- * @param {String} name Widget name
14727
+ * @param {String} name Component name
13993
14728
  * @param {Number} data Table data
13994
14729
  * @param {Object} options:
13995
14730
  * hideName: Don't use name as label [false]
@@ -14007,13 +14742,13 @@ class Panel {
14007
14742
  */
14008
14743
 
14009
14744
  addTable( name, data, options = { } ) {
14010
- const widget = new LX.Table( name, data, options );
14011
- return this._attachWidget( widget );
14745
+ const component = new LX.Table( name, data, options );
14746
+ return this._attachComponent( component );
14012
14747
  }
14013
14748
 
14014
14749
  /**
14015
14750
  * @method addDate
14016
- * @param {String} name Widget name
14751
+ * @param {String} name Component name
14017
14752
  * @param {String} dateValue
14018
14753
  * @param {Function} callback
14019
14754
  * @param {Object} options:
@@ -14024,21 +14759,34 @@ class Panel {
14024
14759
  */
14025
14760
 
14026
14761
  addDate( name, dateValue, callback, options = { } ) {
14027
- const widget = new LX.DatePicker( name, dateValue, callback, options );
14028
- return this._attachWidget( widget );
14762
+ const component = new LX.DatePicker( name, dateValue, callback, options );
14763
+ return this._attachComponent( component );
14029
14764
  }
14030
14765
 
14031
14766
  /**
14032
14767
  * @method addMap2D
14033
- * @param {String} name Widget name
14768
+ * @param {String} name Component name
14034
14769
  * @param {Array} points
14035
14770
  * @param {Function} callback
14036
14771
  * @param {Object} options:
14037
14772
  */
14038
14773
 
14039
14774
  addMap2D( name, points, callback, options = { } ) {
14040
- const widget = new LX.Map2D( name, points, callback, options );
14041
- return this._attachWidget( widget );
14775
+ const component = new LX.Map2D( name, points, callback, options );
14776
+ return this._attachComponent( component );
14777
+ }
14778
+
14779
+ /**
14780
+ * @method addRate
14781
+ * @param {String} name Component name
14782
+ * @param {Number} value
14783
+ * @param {Function} callback
14784
+ * @param {Object} options:
14785
+ */
14786
+
14787
+ addRate( name, value, callback, options = { } ) {
14788
+ const component = new LX.Rate( name, value, callback, options );
14789
+ return this._attachComponent( component );
14042
14790
  }
14043
14791
  }
14044
14792
 
@@ -14073,7 +14821,7 @@ class Branch {
14073
14821
 
14074
14822
  this.closed = options.closed ?? false;
14075
14823
  this.root = root;
14076
- this.widgets = [];
14824
+ this.components = [];
14077
14825
 
14078
14826
  // Create element
14079
14827
  var title = document.createElement( 'div' );
@@ -14144,8 +14892,8 @@ class Branch {
14144
14892
  _onMakeFloating() {
14145
14893
 
14146
14894
  const dialog = new LX.Dialog( this.name, p => {
14147
- // add widgets
14148
- for( let w of this.widgets )
14895
+ // Add components
14896
+ for( let w of this.components )
14149
14897
  {
14150
14898
  p.root.appendChild( w.root );
14151
14899
  }
@@ -14156,7 +14904,7 @@ class Branch {
14156
14904
 
14157
14905
  dialog.branchData = {
14158
14906
  name: this.name,
14159
- widgets: this.widgets,
14907
+ components: this.components,
14160
14908
  closed: this.closed,
14161
14909
  panel: this.panel,
14162
14910
  childIndex
@@ -14168,7 +14916,7 @@ class Branch {
14168
14916
  _addBranchSeparator() {
14169
14917
 
14170
14918
  const element = document.createElement('div');
14171
- element.className = "lexwidgetseparator";
14919
+ element.className = "lexcomponentseparator";
14172
14920
  element.style.width = "100%";
14173
14921
  element.style.background = "none";
14174
14922
 
@@ -14221,7 +14969,7 @@ class Branch {
14221
14969
 
14222
14970
  function innerMouseUp(e)
14223
14971
  {
14224
- that._updateWidgets();
14972
+ that._updateComponents();
14225
14973
 
14226
14974
  line.style.height = "0px";
14227
14975
 
@@ -14234,15 +14982,15 @@ class Branch {
14234
14982
  this.content.appendChild( element );
14235
14983
  }
14236
14984
 
14237
- _updateWidgets() {
14985
+ _updateComponents() {
14238
14986
 
14239
14987
  var size = this.grabber.style.marginLeft;
14240
14988
 
14241
- // Update sizes of widgets inside
14242
- for( let i = 0; i < this.widgets.length; i++ )
14989
+ // Update sizes of components inside
14990
+ for( let i = 0; i < this.components.length; i++ )
14243
14991
  {
14244
- let widget = this.widgets[ i ];
14245
- const element = widget.root;
14992
+ let component = this.components[ i ];
14993
+ const element = component.root;
14246
14994
 
14247
14995
  if( element.children.length < 2 )
14248
14996
  {
@@ -14255,10 +15003,10 @@ class Branch {
14255
15003
  name.style.width = size;
14256
15004
  name.style.minWidth = size;
14257
15005
 
14258
- switch( widget.type )
15006
+ switch( component.type )
14259
15007
  {
14260
- case LX.Widget.CUSTOM:
14261
- case LX.Widget.ARRAY:
15008
+ case LX.BaseComponent.CUSTOM:
15009
+ case LX.BaseComponent.ARRAY:
14262
15010
  continue;
14263
15011
  }
14264
15012
  value.style.width = "-moz-calc( 100% - " + size + " )";
@@ -14572,7 +15320,15 @@ class Menubar {
14572
15320
  {
14573
15321
  const data = buttons[ i ];
14574
15322
  const title = data.title;
14575
- 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" } );
15323
+ const button = new LX.Button( title, data.label, data.callback, {
15324
+ title,
15325
+ buttonClass: "bg-none",
15326
+ disabled: data.disabled,
15327
+ icon: data.icon,
15328
+ hideName: true,
15329
+ swap: data.swap,
15330
+ iconPosition: "start"
15331
+ } );
14576
15332
  this.buttonContainer.appendChild( button.root );
14577
15333
 
14578
15334
  if( title )
@@ -15146,9 +15902,21 @@ class Sidebar {
15146
15902
  LX.asTooltip( itemDom, key, { side: "right", offset: 16, active: false } );
15147
15903
  }
15148
15904
 
15149
- let itemName = document.createElement( 'a' );
15150
- itemName.innerHTML = key;
15151
- itemDom.appendChild( itemName );
15905
+ LX.makeElement( 'a', "grid-column-start-2", key, itemDom );
15906
+
15907
+ if( options.swap )
15908
+ {
15909
+ itemDom.classList.add( "swap", "inline-grid" );
15910
+ itemDom.querySelector( "a" ).classList.add( "swap-off" );
15911
+
15912
+ const input = document.createElement( "input" );
15913
+ input.className = "p-0 border-0";
15914
+ input.type = "checkbox";
15915
+ itemDom.prepend( input );
15916
+
15917
+ const swapIcon = LX.makeIcon( options.swap, { iconClass: "lexsidebarentryicon swap-on" } );
15918
+ itemDom.appendChild( swapIcon );
15919
+ }
15152
15920
 
15153
15921
  if( options.content )
15154
15922
  {
@@ -15177,8 +15945,14 @@ class Sidebar {
15177
15945
  item.checkbox.set( item.value, true );
15178
15946
  }
15179
15947
 
15948
+ if( options.swap && !( e.target instanceof HTMLInputElement ) )
15949
+ {
15950
+ const swapInput = itemDom.querySelector( "input" );
15951
+ swapInput.checked = !swapInput.checked;
15952
+ }
15953
+
15180
15954
  // Manage selected
15181
- if( this.displaySelected )
15955
+ if( this.displaySelected && !options.skipSelection )
15182
15956
  {
15183
15957
  this.root.querySelectorAll(".lexsidebarentry").forEach( e => e.classList.remove( 'selected' ) );
15184
15958
  entry.classList.add( "selected" );
@@ -15260,6 +16034,14 @@ class Sidebar {
15260
16034
 
15261
16035
  subentry.className = "lexsidebarentry";
15262
16036
  subentry.id = subkey;
16037
+
16038
+ if( suboptions.content )
16039
+ {
16040
+ const parentContainer = LX.makeElement( "div" );
16041
+ parentContainer.appendChild( suboptions.content );
16042
+ subentry.appendChild( parentContainer );
16043
+ }
16044
+
15263
16045
  subentryContainer.appendChild( subentry );
15264
16046
 
15265
16047
  subentry.addEventListener("click", (e) => {
@@ -15268,10 +16050,10 @@ class Sidebar {
15268
16050
  if( f ) f.call( this, subkey, subentry, e );
15269
16051
 
15270
16052
  // Manage selected
15271
- if( this.displaySelected )
16053
+ if( this.displaySelected && !suboptions.skipSelection )
15272
16054
  {
15273
16055
  this.root.querySelectorAll(".lexsidebarentry").forEach( e => e.classList.remove( 'selected' ) );
15274
- entry.classList.add( "selected" );
16056
+ subentry.classList.add( "selected" );
15275
16057
  }
15276
16058
  });
15277
16059
  }
@@ -15290,8 +16072,8 @@ class AssetViewEvent {
15290
16072
  static ASSET_RENAMED = 3;
15291
16073
  static ASSET_CLONED = 4;
15292
16074
  static ASSET_DBLCLICKED = 5;
15293
- static ENTER_FOLDER = 6;
15294
- static ASSET_CHECKED = 7;
16075
+ static ASSET_CHECKED = 6;
16076
+ static ENTER_FOLDER = 7;
15295
16077
 
15296
16078
  constructor( type, item, value ) {
15297
16079
  this.type = type || LX.TreeEvent.NONE;
@@ -15309,8 +16091,8 @@ class AssetViewEvent {
15309
16091
  case AssetViewEvent.ASSET_RENAMED: return "assetview_event_renamed";
15310
16092
  case AssetViewEvent.ASSET_CLONED: return "assetview_event_cloned";
15311
16093
  case AssetViewEvent.ASSET_DBLCLICKED: return "assetview_event_dblclicked";
15312
- case AssetViewEvent.ENTER_FOLDER: return "assetview_event_enter_folder";
15313
16094
  case AssetViewEvent.ASSET_CHECKED: return "assetview_event_checked";
16095
+ case AssetViewEvent.ENTER_FOLDER: return "assetview_event_enter_folder";
15314
16096
  }
15315
16097
  }
15316
16098
  }
@@ -15323,8 +16105,12 @@ LX.AssetViewEvent = AssetViewEvent;
15323
16105
 
15324
16106
  class AssetView {
15325
16107
 
15326
- static LAYOUT_CONTENT = 0;
16108
+ static LAYOUT_GRID = 0;
15327
16109
  static LAYOUT_LIST = 1;
16110
+
16111
+ static CONTENT_SORT_ASC = 0;
16112
+ static CONTENT_SORT_DESC = 1;
16113
+
15328
16114
  static MAX_PAGE_ELEMENTS = 50;
15329
16115
 
15330
16116
  /**
@@ -15333,7 +16119,8 @@ class AssetView {
15333
16119
  constructor( options = {} ) {
15334
16120
 
15335
16121
  this.rootPath = "https://raw.githubusercontent.com/jxarco/lexgui.js/master/";
15336
- this.layout = options.layout ?? AssetView.LAYOUT_CONTENT;
16122
+ this.layout = options.layout ?? AssetView.LAYOUT_GRID;
16123
+ this.sortMode = options.sortMode ?? AssetView.CONTENT_SORT_ASC;
15337
16124
  this.contentPage = 1;
15338
16125
 
15339
16126
  if( options.rootPath )
@@ -15451,9 +16238,9 @@ class AssetView {
15451
16238
  this.leftPanel.clear();
15452
16239
  }
15453
16240
 
15454
- if( this.rightPanel )
16241
+ if( this.toolsPanel )
15455
16242
  {
15456
- this.rightPanel.clear();
16243
+ this.toolsPanel.clear();
15457
16244
  }
15458
16245
  }
15459
16246
 
@@ -15565,7 +16352,7 @@ class AssetView {
15565
16352
  _setContentLayout( layoutMode ) {
15566
16353
 
15567
16354
  this.layout = layoutMode;
15568
-
16355
+ this.toolsPanel.refresh();
15569
16356
  this._refreshContent();
15570
16357
  }
15571
16358
 
@@ -15575,42 +16362,34 @@ class AssetView {
15575
16362
 
15576
16363
  _createContentPanel( area ) {
15577
16364
 
15578
- if( this.rightPanel )
16365
+ if( this.toolsPanel )
15579
16366
  {
15580
- this.rightPanel.clear();
16367
+ this.contentPanel.clear();
15581
16368
  }
15582
16369
  else
15583
16370
  {
15584
- this.rightPanel = area.addPanel({ className: 'lexassetcontentpanel flex flex-col overflow-hidden' });
16371
+ this.toolsPanel = area.addPanel({ className: 'flex flex-col overflow-hidden' });
16372
+ this.contentPanel = area.addPanel({ className: 'lexassetcontentpanel flex flex-col overflow-hidden' });
15585
16373
  }
15586
16374
 
15587
- const on_sort = ( value, event ) => {
15588
- const cmenu = LX.addContextMenu( "Sort by", event, c => {
15589
- c.add("Name", () => this._sortData('id') );
15590
- c.add("Type", () => this._sortData('type') );
15591
- c.add("");
15592
- c.add("Ascending", () => this._sortData() );
15593
- c.add("Descending", () => this._sortData(null, true) );
15594
- } );
15595
- const parent = this.parent.root.parentElement;
15596
- if( parent.classList.contains('lexdialog') )
15597
- {
15598
- cmenu.root.style.zIndex = (+getComputedStyle( parent ).zIndex) + 1;
15599
- }
16375
+ const _onSort = ( value, event ) => {
16376
+ new LX.DropdownMenu( event.target, [
16377
+ { name: "Name", icon: "ALargeSmall", callback: () => this._sortData( "id" ) },
16378
+ { name: "Type", icon: "Type", callback: () => this._sortData( "type" ) },
16379
+ null,
16380
+ { name: "Ascending", icon: "SortAsc", callback: () => this._sortData( null, AssetView.CONTENT_SORT_ASC ) },
16381
+ { name: "Descending", icon: "SortDesc", callback: () => this._sortData( null, AssetView.CONTENT_SORT_DESC ) }
16382
+ ], { side: "right", align: "start" });
15600
16383
  };
15601
16384
 
15602
- const on_change_view = ( value, event ) => {
15603
- const cmenu = LX.addContextMenu( "Layout", event, c => {
15604
- c.add("Content", () => this._setContentLayout( AssetView.LAYOUT_CONTENT ) );
15605
- c.add("");
15606
- c.add("List", () => this._setContentLayout( AssetView.LAYOUT_LIST ) );
15607
- } );
15608
- const parent = this.parent.root.parentElement;
15609
- if( parent.classList.contains('lexdialog') )
15610
- cmenu.root.style.zIndex = (+getComputedStyle( parent ).zIndex) + 1;
16385
+ const _onChangeView = ( value, event ) => {
16386
+ new LX.DropdownMenu( event.target, [
16387
+ { name: "Grid", icon: "LayoutGrid", callback: () => this._setContentLayout( AssetView.LAYOUT_GRID ) },
16388
+ { name: "List", icon: "LayoutList", callback: () => this._setContentLayout( AssetView.LAYOUT_LIST ) }
16389
+ ], { side: "right", align: "start" });
15611
16390
  };
15612
16391
 
15613
- const on_change_page = ( value, event ) => {
16392
+ const _onChangePage = ( value, event ) => {
15614
16393
  if( !this.allowNextPage )
15615
16394
  {
15616
16395
  return;
@@ -15626,64 +16405,71 @@ class AssetView {
15626
16405
  }
15627
16406
  };
15628
16407
 
15629
- this.rightPanel.sameLine();
15630
- this.rightPanel.addSelect( "Filter", this.allowedTypes, this.allowedTypes[ 0 ], v => this._refreshContent.call(this, null, v), { width: "30%", minWidth: "128px" } );
15631
- this.rightPanel.addText( null, this.searchValue ?? "", v => this._refreshContent.call(this, v, null), { placeholder: "Search assets.." } );
15632
- this.rightPanel.addButton( null, "", on_sort.bind(this), { title: "Sort", icon: "ArrowUpNarrowWide" } );
15633
- this.rightPanel.addButton( null, "", on_change_view.bind(this), { title: "View", icon: "GripHorizontal" } );
15634
- // Content Pages
15635
- this.rightPanel.addButton( null, "", on_change_page.bind(this, -1), { title: "Previous Page", icon: "ChevronsLeft", className: "ml-auto" } );
15636
- this.rightPanel.addButton( null, "", on_change_page.bind(this, 1), { title: "Next Page", icon: "ChevronsRight" } );
15637
- const textString = "Page " + this.contentPage + " / " + ((((this.currentData.length - 1) / AssetView.MAX_PAGE_ELEMENTS )|0) + 1);
15638
- this.rightPanel.addText(null, textString, null, {
15639
- inputClass: "nobg", disabled: true, signal: "@on_page_change", maxWidth: "16ch" }
15640
- );
15641
- this.rightPanel.endLine();
16408
+ this.toolsPanel.refresh = () => {
16409
+ this.toolsPanel.clear();
16410
+ this.toolsPanel.sameLine();
16411
+ this.toolsPanel.addSelect( "Filter", this.allowedTypes, this.filter ?? this.allowedTypes[ 0 ], v => {
16412
+ this._refreshContent( null, v );
16413
+ }, { width: "30%", minWidth: "128px" } );
16414
+ this.toolsPanel.addText( null, this.searchValue ?? "", v => this._refreshContent.call(this, v, null), { placeholder: "Search assets.." } );
16415
+ this.toolsPanel.addButton( null, "", _onSort.bind(this), { title: "Sort", tooltip: true, icon: ( this.sortMode === AssetView.CONTENT_SORT_ASC ) ? "SortAsc" : "SortDesc" } );
16416
+ this.toolsPanel.addButton( null, "", _onChangeView.bind(this), { title: "View", tooltip: true, icon: ( this.layout === AssetView.LAYOUT_GRID ) ? "LayoutGrid" : "LayoutList" } );
16417
+ // Content Pages
16418
+ this.toolsPanel.addButton( null, "", _onChangePage.bind(this, -1), { title: "Previous Page", icon: "ChevronsLeft", className: "ml-auto" } );
16419
+ this.toolsPanel.addButton( null, "", _onChangePage.bind(this, 1), { title: "Next Page", icon: "ChevronsRight" } );
16420
+ const textString = "Page " + this.contentPage + " / " + ((((this.currentData.length - 1) / AssetView.MAX_PAGE_ELEMENTS )|0) + 1);
16421
+ this.toolsPanel.addText(null, textString, null, {
16422
+ inputClass: "nobg", disabled: true, signal: "@on_page_change", maxWidth: "16ch" }
16423
+ );
15642
16424
 
15643
- if( !this.skipBrowser )
15644
- {
15645
- this.rightPanel.sameLine();
15646
- this.rightPanel.addComboButtons( null, [
15647
- {
15648
- value: "Left",
15649
- icon: "ArrowLeft",
15650
- callback: domEl => {
15651
- if(!this.prevData.length) return;
15652
- this.nextData.push( this.currentData );
15653
- this.currentData = this.prevData.pop();
15654
- this._refreshContent();
15655
- this._updatePath( this.currentData );
15656
- }
15657
- },
15658
- {
15659
- value: "Right",
15660
- icon: "ArrowRight",
15661
- callback: domEl => {
15662
- if(!this.nextData.length) return;
15663
- this.prevData.push( this.currentData );
15664
- this.currentData = this.nextData.pop();
15665
- this._refreshContent();
15666
- this._updatePath( this.currentData );
16425
+ if( !this.skipBrowser )
16426
+ {
16427
+ this.toolsPanel.addComboButtons( null, [
16428
+ {
16429
+ value: "Left",
16430
+ icon: "ArrowLeft",
16431
+ callback: domEl => {
16432
+ if(!this.prevData.length) return;
16433
+ this.nextData.push( this.currentData );
16434
+ this.currentData = this.prevData.pop();
16435
+ this._refreshContent();
16436
+ this._updatePath( this.currentData );
16437
+ }
16438
+ },
16439
+ {
16440
+ value: "Right",
16441
+ icon: "ArrowRight",
16442
+ callback: domEl => {
16443
+ if(!this.nextData.length) return;
16444
+ this.prevData.push( this.currentData );
16445
+ this.currentData = this.nextData.pop();
16446
+ this._refreshContent();
16447
+ this._updatePath( this.currentData );
16448
+ }
16449
+ },
16450
+ {
16451
+ value: "Refresh",
16452
+ icon: "Refresh",
16453
+ callback: domEl => { this._refreshContent(); }
15667
16454
  }
15668
- },
15669
- {
15670
- value: "Refresh",
15671
- icon: "Refresh",
15672
- callback: domEl => { this._refreshContent(); }
15673
- }
15674
- ], { noSelection: true } );
16455
+ ], { noSelection: true } );
15675
16456
 
15676
- this.rightPanel.addText(null, this.path.join('/'), null, {
15677
- inputClass: "nobg", disabled: true, signal: "@on_folder_change",
15678
- style: { fontWeight: "600", fontSize: "15px" }
15679
- });
16457
+ this.toolsPanel.addText(null, this.path.join('/'), null, {
16458
+ inputClass: "nobg", disabled: true, signal: "@on_folder_change",
16459
+ style: { fontWeight: "600", fontSize: "15px" }
16460
+ });
16461
+ }
15680
16462
 
15681
- this.rightPanel.endLine();
15682
- }
16463
+ this.toolsPanel.endLine();
16464
+ };
16465
+
16466
+ this.toolsPanel.refresh();
16467
+
16468
+ // Start content panel
15683
16469
 
15684
16470
  this.content = document.createElement('ul');
15685
16471
  this.content.className = "lexassetscontent";
15686
- this.rightPanel.root.appendChild(this.content);
16472
+ this.contentPanel.attach( this.content );
15687
16473
 
15688
16474
  this.content.addEventListener('dragenter', function( e ) {
15689
16475
  e.preventDefault();
@@ -15706,12 +16492,12 @@ class AssetView {
15706
16492
 
15707
16493
  _refreshContent( searchValue, filter ) {
15708
16494
 
15709
- const isContentLayout = ( this.layout == AssetView.LAYOUT_CONTENT ); // default
16495
+ const isGridLayout = ( this.layout == AssetView.LAYOUT_GRID ); // default
15710
16496
 
15711
16497
  this.filter = filter ?? ( this.filter ?? "None" );
15712
16498
  this.searchValue = searchValue ?? (this.searchValue ?? "");
15713
16499
  this.content.innerHTML = "";
15714
- this.content.className = (isContentLayout ? "lexassetscontent" : "lexassetscontent list");
16500
+ this.content.className = (isGridLayout ? "lexassetscontent" : "lexassetscontent list");
15715
16501
  let that = this;
15716
16502
 
15717
16503
  const _addItem = function(item) {
@@ -15734,7 +16520,7 @@ class AssetView {
15734
16520
 
15735
16521
  itemEl.addEventListener("mousemove", e => {
15736
16522
 
15737
- if( !isContentLayout )
16523
+ if( !isGridLayout )
15738
16524
  {
15739
16525
  return;
15740
16526
  }
@@ -15763,14 +16549,14 @@ class AssetView {
15763
16549
  });
15764
16550
 
15765
16551
  itemEl.addEventListener("mouseenter", () => {
15766
- if( isContentLayout )
16552
+ if( isGridLayout )
15767
16553
  {
15768
16554
  desc.style.display = "unset";
15769
16555
  }
15770
16556
  });
15771
16557
 
15772
16558
  itemEl.addEventListener("mouseleave", () => {
15773
- if( isContentLayout )
16559
+ if( isGridLayout )
15774
16560
  {
15775
16561
  setTimeout( () => {
15776
16562
  desc.style.display = "none";
@@ -15814,11 +16600,11 @@ class AssetView {
15814
16600
  let preview = null;
15815
16601
  const hasImage = item.src && (['png', 'jpg'].indexOf( LX.getExtension( item.src ) ) > -1 || item.src.includes("data:image/") ); // Support b64 image as src
15816
16602
 
15817
- if( hasImage || isFolder || !isContentLayout)
16603
+ if( hasImage || isFolder || !isGridLayout)
15818
16604
  {
15819
16605
  preview = document.createElement('img');
15820
16606
  let real_src = item.unknown_extension ? that.rootPath + "images/file.png" : (isFolder ? that.rootPath + "images/folder.png" : item.src);
15821
- preview.src = (isContentLayout || isFolder ? real_src : that.rootPath + "images/file.png");
16607
+ preview.src = (isGridLayout || isFolder ? real_src : that.rootPath + "images/file.png");
15822
16608
  itemEl.appendChild( preview );
15823
16609
  }
15824
16610
  else
@@ -16079,16 +16865,20 @@ class AssetView {
16079
16865
  }
16080
16866
  }
16081
16867
 
16082
- _sortData( sort_by, sort_descending = false ) {
16868
+ _sortData( sortBy, sortMode ) {
16083
16869
 
16084
- sort_by = sort_by ?? (this._lastSortBy ?? 'id');
16085
- this.currentData = this.currentData.sort( (a, b) => {
16086
- var r = sort_descending ? b[sort_by].localeCompare(a[sort_by]) : a[sort_by].localeCompare(b[sort_by]);
16087
- if(r == 0) r = sort_descending ? b['id'].localeCompare(a['id']) : a['id'].localeCompare(b['id']);
16870
+ sortBy = sortBy ?? ( this._lastSortBy ?? 'id' );
16871
+ sortMode = sortMode ?? this.sortMode;
16872
+ const sortDesc = ( sortMode === AssetView.CONTENT_SORT_DESC );
16873
+ this.currentData = this.currentData.sort( ( a, b ) => {
16874
+ var r = sortDesc ? b[ sortBy ].localeCompare( a[ sortBy ] ) : a[ sortBy ].localeCompare( b[ sortBy ] );
16875
+ if( r == 0 ) r = sortDesc ? b['id'].localeCompare( a['id'] ) : a[ 'id' ].localeCompare( b[ 'id' ] );
16088
16876
  return r;
16089
16877
  } );
16090
16878
 
16091
- this._lastSortBy = sort_by;
16879
+ this._lastSortBy = sortBy;
16880
+ this.sortMode = sortMode;
16881
+ this.toolsPanel.refresh();
16092
16882
  this._refreshContent();
16093
16883
  }
16094
16884