lexgui 0.6.11 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +14 -9
  2. package/build/{components → extensions}/audio.js +11 -11
  3. package/build/{components → extensions}/codeeditor.js +109 -74
  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 +150 -94
  8. package/build/{components → extensions}/videoeditor.js +1 -1
  9. package/build/lexgui-docs.css +9 -9
  10. package/build/lexgui.css +489 -223
  11. package/build/lexgui.js +1771 -777
  12. package/build/lexgui.min.css +2 -2
  13. package/build/lexgui.min.js +1 -1
  14. package/build/lexgui.module.js +1803 -809
  15. package/build/lexgui.module.min.js +1 -1
  16. package/changelog.md +90 -21
  17. package/demo.js +52 -32
  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 +5 -5
  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.11",
17
+ version: "0.7.0",
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,18 @@ 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 nestedDialog = trigger.closest( "dialog" );
773
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
774
+ {
775
+ this._parent = nestedDialog;
776
+ }
777
+ else
778
+ {
779
+ this._parent = LX.root;
780
+ }
781
+
782
+ this._parent.appendChild( this.root );
755
783
 
756
784
  this.root.addEventListener( "keydown", (e) => {
757
785
  if( e.key == "Escape" )
@@ -873,12 +901,71 @@ class Popover {
873
901
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
874
902
  }
875
903
 
904
+ if( this._parent instanceof HTMLDialogElement )
905
+ {
906
+ let parentRect = this._parent.getBoundingClientRect();
907
+ position[ 0 ] -= parentRect.x;
908
+ position[ 1 ] -= parentRect.y;
909
+ }
910
+
876
911
  this.root.style.left = `${ position[ 0 ] }px`;
877
912
  this.root.style.top = `${ position[ 1 ] }px`;
878
913
  }
879
914
  }
880
915
  LX.Popover = Popover;
881
916
 
917
+ /**
918
+ * @class PopConfirm
919
+ */
920
+
921
+ class PopConfirm {
922
+
923
+ constructor( reference, options = {} ) {
924
+
925
+ const okText = options.confirmText ?? "Yes";
926
+ const cancelText = options.cancelText ?? "No";
927
+ const title = options.title ?? "Confirm";
928
+ const content = options.content ?? "Are you sure you want to proceed?";
929
+ const onConfirm = options.onConfirm;
930
+ const onCancel = options.onCancel;
931
+
932
+ const popoverContainer = LX.makeContainer( ["auto", "auto"], "tour-step-container" );
933
+
934
+ {
935
+ const headerDiv = LX.makeContainer( ["100%", "auto"], "flex flex-row", "", popoverContainer );
936
+ LX.makeContainer( ["100%", "auto"], "p-1 font-medium text-md", title, headerDiv );
937
+ }
938
+
939
+ LX.makeContainer( ["100%", "auto"], "p-1 text-md", content, popoverContainer, { maxWidth: "400px" } );
940
+ const footer = LX.makeContainer( ["100%", "auto"], "flex flex-row text-md", "", popoverContainer );
941
+ const footerButtons = LX.makeContainer( ["100%", "auto"], "text-md", "", footer );
942
+ const footerPanel = new LX.Panel();
943
+ footerButtons.appendChild( footerPanel.root );
944
+
945
+ footerPanel.sameLine( 2, "justify-end" );
946
+ footerPanel.addButton( null, cancelText, () => {
947
+ if( onCancel ) onCancel();
948
+ this._popover?.destroy();
949
+ }, { xbuttonClass: "contrast" } );
950
+ footerPanel.addButton( null, okText, () => {
951
+ if( onConfirm ) onConfirm();
952
+ this._popover?.destroy();
953
+ }, { buttonClass: "accent" } );
954
+
955
+ this._popover?.destroy();
956
+ this._popover = new LX.Popover( null, [ popoverContainer ], {
957
+ reference,
958
+ side: options.side ?? "top",
959
+ align: options.align,
960
+ sideOffset: options.sideOffset,
961
+ alignOffset: options.alignOffset,
962
+ } );
963
+
964
+ }
965
+ }
966
+
967
+ LX.PopConfirm = PopConfirm;
968
+
882
969
  /**
883
970
  * @class Sheet
884
971
  */
@@ -894,7 +981,7 @@ class Sheet {
894
981
  this.root.tabIndex = "1";
895
982
  this.root.role = "dialog";
896
983
  this.root.className = "lexsheet fixed z-1000 bg-primary";
897
- LX.root.appendChild( this.root );
984
+ document.body.appendChild( this.root );
898
985
 
899
986
  this.root.addEventListener( "keydown", (e) => {
900
987
  if( e.key == "Escape" )
@@ -935,17 +1022,21 @@ class Sheet {
935
1022
  this.root.style.height = "100%";
936
1023
  break;
937
1024
  case "top":
1025
+ this.root.style.left = 0;
938
1026
  this.root.style.top = 0;
939
1027
  this.root.style.width = "100%";
940
1028
  this.root.style.height = size;
941
1029
  break;
942
1030
  case "bottom":
1031
+ this.root.style.left = 0;
943
1032
  this.root.style.bottom = 0;
944
1033
  this.root.style.width = "100%";
945
1034
  this.root.style.height = size;
946
1035
  break;
947
1036
  }
948
1037
 
1038
+ document.documentElement.setAttribute( "data-scale", `sheet-${ this.side }` );
1039
+
949
1040
  this.root.focus();
950
1041
 
951
1042
  this._onClick = e => {
@@ -963,6 +1054,8 @@ class Sheet {
963
1054
 
964
1055
  destroy() {
965
1056
 
1057
+ document.documentElement.setAttribute( "data-scale", "" );
1058
+
966
1059
  document.body.removeEventListener( "mousedown", this._onClick, true );
967
1060
  document.body.removeEventListener( "focusin", this._onClick, true );
968
1061
 
@@ -1012,7 +1105,18 @@ class DropdownMenu {
1012
1105
  this.root.dataset["side"] = this.side;
1013
1106
  this.root.tabIndex = "1";
1014
1107
  this.root.className = "lexdropdownmenu";
1015
- LX.root.appendChild( this.root );
1108
+
1109
+ const nestedDialog = trigger.closest( "dialog" );
1110
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
1111
+ {
1112
+ this._parent = nestedDialog;
1113
+ }
1114
+ else
1115
+ {
1116
+ this._parent = LX.root;
1117
+ }
1118
+
1119
+ this._parent.appendChild( this.root );
1016
1120
 
1017
1121
  this._create( this._items );
1018
1122
 
@@ -1048,7 +1152,7 @@ class DropdownMenu {
1048
1152
  document.body.removeEventListener( "mousedown", this._onClick, true );
1049
1153
  document.body.removeEventListener( "focusin", this._onClick, true );
1050
1154
 
1051
- LX.root.querySelectorAll( ".lexdropdownmenu" ).forEach( m => { m.remove(); } );
1155
+ this._parent.querySelectorAll( ".lexdropdownmenu" ).forEach( m => { m.remove(); } );
1052
1156
 
1053
1157
  DropdownMenu.currentMenu = null;
1054
1158
 
@@ -1073,14 +1177,22 @@ class DropdownMenu {
1073
1177
  newParent.className = "lexdropdownmenu";
1074
1178
  newParent.dataset["id"] = parentDom.dataset["id"];
1075
1179
  newParent.dataset["side"] = "right"; // submenus always come from the right
1076
- LX.root.appendChild( newParent );
1180
+ this._parent.appendChild( newParent );
1077
1181
 
1078
1182
  newParent.currentParent = parentDom;
1079
1183
  parentDom = newParent;
1080
1184
 
1081
1185
  LX.doAsync( () => {
1186
+
1082
1187
  const position = [ parentRect.x + parentRect.width, parentRect.y ];
1083
1188
 
1189
+ if( this._parent instanceof HTMLDialogElement )
1190
+ {
1191
+ let rootParentRect = this._parent.getBoundingClientRect();
1192
+ position[ 0 ] -= rootParentRect.x;
1193
+ position[ 1 ] -= rootParentRect.y;
1194
+ }
1195
+
1084
1196
  if( this.avoidCollisions )
1085
1197
  {
1086
1198
  position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - newParent.offsetWidth - this._windowPadding );
@@ -1096,139 +1208,180 @@ class DropdownMenu {
1096
1208
 
1097
1209
  for( let item of items )
1098
1210
  {
1099
- if( !item )
1100
- {
1101
- this._addSeparator( parentDom );
1102
- continue;
1103
- }
1211
+ this._createItem( item, parentDom, applyIconPadding );
1212
+ }
1213
+ }
1104
1214
 
1105
- const key = item.name ?? item;
1106
- const pKey = LX.getSupportedDOMName( key );
1215
+ _createItem( item, parentDom, applyIconPadding ) {
1107
1216
 
1108
- // Item already created
1109
- if( parentDom.querySelector( "#" + pKey ) )
1110
- {
1111
- continue;
1112
- }
1217
+ if( !item )
1218
+ {
1219
+ this._addSeparator( parentDom );
1220
+ return;
1221
+ }
1113
1222
 
1114
- const menuItem = document.createElement('div');
1115
- menuItem.className = "lexdropdownmenuitem" + ( item.name ? "" : " label" ) + ( item.disabled ?? false ? " disabled" : "" ) + ( ` ${ item.className ?? "" }` );
1116
- menuItem.dataset["id"] = pKey;
1117
- menuItem.innerHTML = `<span>${ key }</span>`;
1118
- menuItem.tabIndex = "1";
1119
- parentDom.appendChild( menuItem );
1223
+ const key = item.name ?? item;
1224
+ const pKey = LX.getSupportedDOMName( key );
1120
1225
 
1121
- if( item.constructor === String ) // Label case
1122
- {
1123
- continue;
1124
- }
1226
+ // Item already created
1227
+ if( parentDom.querySelector( "#" + pKey ) )
1228
+ {
1229
+ return;
1230
+ }
1125
1231
 
1126
- if( item.submenu )
1127
- {
1128
- const submenuIcon = LX.makeIcon( "Right", { svgClass: "sm" } );
1129
- menuItem.appendChild( submenuIcon );
1130
- }
1131
- else if( item.kbd )
1132
- {
1133
- item.kbd = [].concat( item.kbd );
1232
+ const menuItem = document.createElement('div');
1233
+ menuItem.className = "lexdropdownmenuitem" + ( item.name ? "" : " label" ) + ( item.disabled ?? false ? " disabled" : "" ) + ( ` ${ item.className ?? "" }` );
1234
+ menuItem.dataset["id"] = pKey;
1235
+ menuItem.innerHTML = `<span>${ key }</span>`;
1236
+ menuItem.tabIndex = "1";
1237
+ parentDom.appendChild( menuItem );
1134
1238
 
1135
- const kbd = LX.makeKbd( item.kbd );
1136
- menuItem.appendChild( kbd );
1239
+ if( item.constructor === String ) // Label case
1240
+ {
1241
+ return;
1242
+ }
1137
1243
 
1138
- document.addEventListener( "keydown", e => {
1139
- if( !this._trigger.ddm ) return;
1140
- e.preventDefault();
1141
- // Check if it's a letter or other key
1142
- let kdbKey = item.kbd.join("");
1143
- kdbKey = kdbKey.length == 1 ? kdbKey.toLowerCase() : kdbKey;
1144
- if( kdbKey == e.key )
1145
- {
1146
- menuItem.click();
1147
- }
1148
- } );
1149
- }
1244
+ if( item.submenu )
1245
+ {
1246
+ const submenuIcon = LX.makeIcon( "Right", { svgClass: "sm" } );
1247
+ menuItem.appendChild( submenuIcon );
1248
+ }
1249
+ else if( item.kbd )
1250
+ {
1251
+ item.kbd = [].concat( item.kbd );
1150
1252
 
1151
- const disabled = item.disabled ?? false;
1253
+ const kbd = LX.makeKbd( item.kbd );
1254
+ menuItem.appendChild( kbd );
1152
1255
 
1153
- if( item.icon )
1154
- {
1155
- const icon = LX.makeIcon( item.icon, { svgClass: disabled ? "fg-tertiary" : item.className } );
1156
- menuItem.prepend( icon );
1157
- }
1158
- else if( item.checked == undefined && applyIconPadding ) // no checkbox, no icon, apply padding if there's checkbox or icon in other items
1159
- {
1160
- menuItem.classList.add( "pl-8" );
1161
- }
1256
+ document.addEventListener( "keydown", e => {
1257
+ if( !this._trigger.ddm ) return;
1258
+ e.preventDefault();
1259
+ // Check if it's a letter or other key
1260
+ let kdbKey = item.kbd.join("");
1261
+ kdbKey = kdbKey.length == 1 ? kdbKey.toLowerCase() : kdbKey;
1262
+ if( kdbKey == e.key )
1263
+ {
1264
+ menuItem.click();
1265
+ }
1266
+ } );
1267
+ }
1162
1268
 
1163
- if( disabled )
1164
- {
1165
- continue;
1166
- }
1269
+ const disabled = item.disabled ?? false;
1167
1270
 
1168
- if( item.checked != undefined )
1271
+ if( this._radioGroup !== undefined )
1272
+ {
1273
+ if( item.name === this._radioGroup.selected )
1169
1274
  {
1170
- const checkbox = new LX.Checkbox( pKey + "_entryChecked", item.checked, (v) => {
1171
- const f = item[ 'callback' ];
1172
- item.checked = v;
1173
- if( f )
1174
- {
1175
- f.call( this, key, v, menuItem );
1176
- }
1177
- }, { className: "accent" });
1178
- const input = checkbox.root.querySelector( "input" );
1179
- input.classList.add( "ml-auto" );
1180
- menuItem.appendChild( input );
1181
-
1182
- menuItem.addEventListener( "click", (e) => {
1183
- if( e.target.type == "checkbox" ) return;
1184
- input.checked = !input.checked;
1185
- checkbox.set( input.checked );
1186
- } );
1275
+ const icon = LX.makeIcon( "Circle", { svgClass: "xxs fill-current" } );
1276
+ menuItem.prepend( icon );
1187
1277
  }
1188
- else
1189
- {
1190
- menuItem.addEventListener( "click", () => {
1191
- const f = item[ 'callback' ];
1192
- if( f )
1193
- {
1194
- f.call( this, key, menuItem );
1195
- }
1196
1278
 
1197
- this.destroy( true );
1198
- } );
1199
- }
1279
+ menuItem.setAttribute( "data-radioname", this._radioGroup.name );
1280
+ }
1281
+ else if( item.icon )
1282
+ {
1283
+ const icon = LX.makeIcon( item.icon, { svgClass: disabled ? "fg-tertiary" : item.className } );
1284
+ menuItem.prepend( icon );
1285
+ }
1286
+ else if( item.checked == undefined && applyIconPadding ) // no checkbox, no icon, apply padding if there's checkbox or icon in other items
1287
+ {
1288
+ menuItem.classList.add( "pl-8" );
1289
+ }
1200
1290
 
1201
- menuItem.addEventListener("mouseover", e => {
1291
+ if( disabled )
1292
+ {
1293
+ return;
1294
+ }
1202
1295
 
1203
- let path = menuItem.dataset["id"];
1204
- let p = parentDom;
1296
+ if( item.checked != undefined )
1297
+ {
1298
+ const checkbox = new LX.Checkbox( pKey + "_entryChecked", item.checked, (v) => {
1299
+ const f = item[ 'callback' ];
1300
+ item.checked = v;
1301
+ if( f )
1302
+ {
1303
+ f.call( this, key, v, menuItem );
1304
+ }
1305
+ }, { className: "accent" });
1306
+ const input = checkbox.root.querySelector( "input" );
1307
+ input.classList.add( "ml-auto" );
1308
+ menuItem.appendChild( input );
1309
+
1310
+ menuItem.addEventListener( "click", (e) => {
1311
+ if( e.target.type == "checkbox" ) return;
1312
+ input.checked = !input.checked;
1313
+ checkbox.set( input.checked );
1314
+ } );
1315
+ }
1316
+ else
1317
+ {
1318
+ menuItem.addEventListener( "click", () => {
1319
+ const f = item[ 'callback' ];
1320
+ if( f )
1321
+ {
1322
+ f.call( this, key, menuItem );
1323
+ }
1205
1324
 
1206
- while( p )
1325
+ const radioName = menuItem.getAttribute( "data-radioname" );
1326
+ if( radioName )
1207
1327
  {
1208
- path += "/" + p.dataset["id"];
1209
- p = p.currentParent?.parentElement;
1328
+ this._trigger[ radioName ] = key;
1210
1329
  }
1211
1330
 
1212
- LX.root.querySelectorAll( ".lexdropdownmenu" ).forEach( m => {
1213
- if( !path.includes( m.dataset["id"] ) )
1214
- {
1215
- m.currentParent.built = false;
1216
- m.remove();
1217
- }
1218
- } );
1331
+ this.destroy( true );
1332
+ } );
1333
+ }
1334
+
1335
+ menuItem.addEventListener("mouseover", e => {
1336
+
1337
+ let path = menuItem.dataset["id"];
1338
+ let p = parentDom;
1219
1339
 
1220
- if( item.submenu && this.inPlace )
1340
+ while( p )
1341
+ {
1342
+ path += "/" + p.dataset["id"];
1343
+ p = p.currentParent?.parentElement;
1344
+ }
1345
+
1346
+ this._parent.querySelectorAll( ".lexdropdownmenu" ).forEach( m => {
1347
+ if( !path.includes( m.dataset["id"] ) )
1221
1348
  {
1222
- if( menuItem.built )
1223
- {
1224
- return;
1225
- }
1226
- menuItem.built = true;
1227
- this._create( item.submenu, menuItem );
1349
+ m.currentParent.built = false;
1350
+ m.remove();
1228
1351
  }
1352
+ } );
1229
1353
 
1230
- e.stopPropagation();
1231
- });
1354
+ if( item.submenu && this.inPlace )
1355
+ {
1356
+ if( menuItem.built )
1357
+ {
1358
+ return;
1359
+ }
1360
+ menuItem.built = true;
1361
+ this._create( item.submenu, menuItem );
1362
+ }
1363
+
1364
+ e.stopPropagation();
1365
+ });
1366
+
1367
+ if( item.options )
1368
+ {
1369
+ this._addSeparator();
1370
+
1371
+ console.assert( this._trigger[ item.name ] && "An item of the radio group must be selected!" );
1372
+ this._radioGroup = {
1373
+ name: item.name,
1374
+ selected: this._trigger[ item.name ]
1375
+ };
1376
+
1377
+ for( let o of item.options )
1378
+ {
1379
+ this._createItem( o, parentDom, applyIconPadding );
1380
+ }
1381
+
1382
+ delete this._radioGroup;
1383
+
1384
+ this._addSeparator();
1232
1385
  }
1233
1386
  }
1234
1387
 
@@ -1288,6 +1441,13 @@ class DropdownMenu {
1288
1441
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
1289
1442
  }
1290
1443
 
1444
+ if( this._parent instanceof HTMLDialogElement )
1445
+ {
1446
+ let parentRect = this._parent.getBoundingClientRect();
1447
+ position[ 0 ] -= parentRect.x;
1448
+ position[ 1 ] -= parentRect.y;
1449
+ }
1450
+
1291
1451
  this.root.style.left = `${ position[ 0 ] }px`;
1292
1452
  this.root.style.top = `${ position[ 1 ] }px`;
1293
1453
  this.inPlace = true;
@@ -1548,25 +1708,25 @@ class ColorPicker {
1548
1708
  this._updateColorValue( null, true );
1549
1709
  } ).root );
1550
1710
 
1551
- this.labelWidget = new LX.TextInput( null, "", null, { inputClass: "bg-none", fit: true, disabled: true } );
1552
- colorLabel.appendChild( this.labelWidget.root );
1711
+ this.labelComponent = new LX.TextInput( null, "", null, { inputClass: "bg-none", fit: true, disabled: true } );
1712
+ colorLabel.appendChild( this.labelComponent.root );
1553
1713
 
1554
1714
  // Copy button
1555
1715
  {
1556
- const copyButtonWidget = new LX.Button(null, "copy", async () => {
1557
- navigator.clipboard.writeText( this.labelWidget.value() );
1558
- copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "none";
1716
+ const copyButtonComponent = new LX.Button(null, "copy", async () => {
1717
+ navigator.clipboard.writeText( this.labelComponent.value() );
1718
+ copyButtonComponent.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "none";
1559
1719
 
1560
1720
  LX.doAsync( () => {
1561
- copyButtonWidget.swap( true );
1562
- copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "auto";
1721
+ copyButtonComponent.swap( true );
1722
+ copyButtonComponent.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "auto";
1563
1723
  }, 3000 );
1564
1724
 
1565
1725
  }, { swap: "Check", icon: "Copy", buttonClass: "bg-none", className: "ml-auto", title: "Copy" } );
1566
1726
 
1567
- copyButtonWidget.root.querySelector( ".swap-on svg" ).addClass( "fg-success" );
1727
+ copyButtonComponent.root.querySelector( ".swap-on svg" ).addClass( "fg-success" );
1568
1728
 
1569
- colorLabel.appendChild( copyButtonWidget.root );
1729
+ colorLabel.appendChild( copyButtonComponent.root );
1570
1730
  }
1571
1731
 
1572
1732
  this._updateColorValue( hexValue, true );
@@ -1621,25 +1781,25 @@ class ColorPicker {
1621
1781
  if( this.colorModel == "CSS" )
1622
1782
  {
1623
1783
  const { r, g, b, a } = this.currentColor.css;
1624
- this.labelWidget.set( `rgb${ this.useAlpha ? 'a' : '' }(${ r },${ g },${ b }${ this.useAlpha ? ',' + toFixed( a ) : '' })` );
1784
+ this.labelComponent.set( `rgb${ this.useAlpha ? 'a' : '' }(${ r },${ g },${ b }${ this.useAlpha ? ',' + toFixed( a ) : '' })` );
1625
1785
  }
1626
1786
  else if( this.colorModel == "Hex" )
1627
1787
  {
1628
- this.labelWidget.set( ( this.useAlpha ? this.currentColor.hex : this.currentColor.hex.substr( 0, 7 ) ).toUpperCase() );
1788
+ this.labelComponent.set( ( this.useAlpha ? this.currentColor.hex : this.currentColor.hex.substr( 0, 7 ) ).toUpperCase() );
1629
1789
  }
1630
1790
  else if( this.colorModel == "HSV" )
1631
1791
  {
1632
1792
  const { h, s, v, a } = this.currentColor.hsv;
1633
1793
  const components = [ Math.floor( h ) + 'º', Math.floor( s * 100 ) + '%', Math.floor( v * 100 ) + '%' ];
1634
1794
  if( this.useAlpha ) components.push( toFixed( a ) );
1635
- this.labelWidget.set( components.join( ' ' ) );
1795
+ this.labelComponent.set( components.join( ' ' ) );
1636
1796
  }
1637
1797
  else // RGB
1638
1798
  {
1639
1799
  const { r, g, b, a } = this.currentColor.rgb;
1640
1800
  const components = [ toFixed( r ), toFixed( g ), toFixed( b ) ];
1641
1801
  if( this.useAlpha ) components.push( toFixed( a ) );
1642
- this.labelWidget.set( components.join( ' ' ) );
1802
+ this.labelComponent.set( components.join( ' ' ) );
1643
1803
  }
1644
1804
  }
1645
1805
 
@@ -1674,8 +1834,15 @@ class Calendar {
1674
1834
  this.root = LX.makeContainer( ["256px", "auto"], "p-1 text-md" );
1675
1835
 
1676
1836
  this.onChange = options.onChange;
1837
+ this.onPreviousMonth = options.onPreviousMonth;
1838
+ this.onNextMonth = options.onNextMonth;
1839
+
1677
1840
  this.untilToday = options.untilToday;
1678
1841
  this.fromToday = options.fromToday;
1842
+ this.range = options.range;
1843
+
1844
+ this.skipPrevMonth = options.skipPrevMonth;
1845
+ this.skipNextMonth = options.skipNextMonth;
1679
1846
 
1680
1847
  if( dateString )
1681
1848
  {
@@ -1699,7 +1866,7 @@ class Calendar {
1699
1866
  }
1700
1867
  }
1701
1868
 
1702
- _previousMonth() {
1869
+ _previousMonth( skipCallback ) {
1703
1870
 
1704
1871
  this.month = Math.max( 0, this.month - 1 );
1705
1872
 
@@ -1710,9 +1877,14 @@ class Calendar {
1710
1877
  }
1711
1878
 
1712
1879
  this.fromMonthYear( this.month, this.year );
1880
+
1881
+ if( !skipCallback && this.onPreviousMonth )
1882
+ {
1883
+ this.onPreviousMonth( this.currentDate );
1884
+ }
1713
1885
  }
1714
1886
 
1715
- _nextMonth() {
1887
+ _nextMonth( skipCallback ) {
1716
1888
 
1717
1889
  this.month = Math.min( this.month + 1, 12 );
1718
1890
 
@@ -1723,6 +1895,11 @@ class Calendar {
1723
1895
  }
1724
1896
 
1725
1897
  this.fromMonthYear( this.month, this.year );
1898
+
1899
+ if( !skipCallback && this.onNextMonth )
1900
+ {
1901
+ this.onNextMonth( this.currentDate );
1902
+ }
1726
1903
  }
1727
1904
 
1728
1905
  refresh() {
@@ -1733,19 +1910,25 @@ class Calendar {
1733
1910
  {
1734
1911
  const header = LX.makeContainer( ["100%", "auto"], "flex flex-row p-1", "", this.root );
1735
1912
 
1736
- const prevMonthIcon = LX.makeIcon( "Left", { title: "Previous Month", iconClass: "border p-1 rounded hover:bg-secondary", svgClass: "sm" } );
1737
- header.appendChild( prevMonthIcon );
1738
- prevMonthIcon.addEventListener( "click", () => {
1739
- this._previousMonth();
1740
- } );
1913
+ if( !this.skipPrevMonth )
1914
+ {
1915
+ const prevMonthIcon = LX.makeIcon( "Left", { title: "Previous Month", iconClass: "border p-1 rounded hover:bg-secondary", svgClass: "sm" } );
1916
+ header.appendChild( prevMonthIcon );
1917
+ prevMonthIcon.addEventListener( "click", () => {
1918
+ this._previousMonth();
1919
+ } );
1920
+ }
1741
1921
 
1742
1922
  LX.makeContainer( ["100%", "auto"], "text-center font-medium select-none", `${ this.monthName } ${ this.year }`, header );
1743
1923
 
1744
- const nextMonthIcon = LX.makeIcon( "Right", { title: "Next Month", iconClass: "border p-1 rounded hover:bg-secondary", svgClass: "sm" } );
1745
- header.appendChild( nextMonthIcon );
1746
- nextMonthIcon.addEventListener( "click", () => {
1747
- this._nextMonth();
1748
- } );
1924
+ if( !this.skipNextMonth )
1925
+ {
1926
+ const nextMonthIcon = LX.makeIcon( "Right", { title: "Next Month", iconClass: "border p-1 rounded hover:bg-secondary", svgClass: "sm" } );
1927
+ header.appendChild( nextMonthIcon );
1928
+ nextMonthIcon.addEventListener( "click", () => {
1929
+ this._nextMonth();
1930
+ } );
1931
+ }
1749
1932
  }
1750
1933
 
1751
1934
  // Body
@@ -1777,6 +1960,9 @@ class Calendar {
1777
1960
  const body = document.createElement( 'tbody' );
1778
1961
  daysTable.appendChild( body );
1779
1962
 
1963
+ let fromRangeDate = this.range ? LX.dateFromDateString( this.range[ 0 ] ) : null;
1964
+ let toRangeDate = this.range ? LX.dateFromDateString( this.range[ 1 ] ) : null;
1965
+
1780
1966
  for( let week = 0; week < 6; week++ )
1781
1967
  {
1782
1968
  const hrow = document.createElement( 'tr' );
@@ -1789,15 +1975,28 @@ class Calendar {
1789
1975
 
1790
1976
  const dayDate = new Date( `${ this.month }/${ dayData.day }/${ this.year }` );
1791
1977
  const date = new Date();
1978
+ // today inclusives
1792
1979
  const beforeToday = this.untilToday ? ( dayDate.getTime() < date.getTime() ) : true;
1793
- const afterToday = this.fromToday ? ( dayDate.getTime() > date.getTime() ) : true;
1980
+ const afterToday = this.fromToday ? ( dayDate.getFullYear() > date.getFullYear() ||
1981
+ (dayDate.getFullYear() === date.getFullYear() && dayDate.getMonth() > date.getMonth()) ||
1982
+ (dayDate.getFullYear() === date.getFullYear() && dayDate.getMonth() === date.getMonth() && dayDate.getDate() >= date.getDate())
1983
+ ) : true;
1794
1984
  const selectable = dayData.currentMonth && beforeToday && afterToday;
1795
-
1796
- if( this.currentDate && ( dayData.day == this.currentDate.day ) && ( this.month == this.currentDate.month )
1797
- && ( this.year == this.currentDate.year ) && dayData.currentMonth )
1985
+ const currentDay = this.currentDate && ( dayData.day == this.currentDate.day ) && ( this.month == this.currentDate.month )
1986
+ && ( this.year == this.currentDate.year ) && dayData.currentMonth;
1987
+ const currentFromRange = selectable && fromRangeDate && ( dayData.day == fromRangeDate.getDate() ) && ( this.month == ( fromRangeDate.getMonth() + 1 ) )
1988
+ && ( this.year == fromRangeDate.getFullYear() );
1989
+ const currentToRange = selectable && toRangeDate && ( dayData.day == toRangeDate.getDate() ) && ( this.month == ( toRangeDate.getMonth() + 1 ) )
1990
+ && ( this.year == toRangeDate.getFullYear() );
1991
+
1992
+ if( ( !this.range && currentDay ) || this.range && ( currentFromRange || currentToRange ) )
1798
1993
  {
1799
1994
  th.className += ` bg-contrast fg-contrast`;
1800
1995
  }
1996
+ else if( this.range && selectable && ( dayDate > fromRangeDate ) && ( dayDate < toRangeDate ) )
1997
+ {
1998
+ th.className += ` bg-accent fg-contrast`;
1999
+ }
1801
2000
  else
1802
2001
  {
1803
2002
  th.className += ` ${ selectable ? "fg-primary" : "fg-tertiary" } hover:bg-secondary`;
@@ -1817,6 +2016,20 @@ class Calendar {
1817
2016
  }
1818
2017
  } );
1819
2018
  }
2019
+ // This event should only be applied in non current month days
2020
+ else if( this.range === undefined && !dayData.currentMonth )
2021
+ {
2022
+ th.addEventListener( "click", () => {
2023
+ if( dayData?.prevMonth )
2024
+ {
2025
+ this._previousMonth();
2026
+ }
2027
+ else
2028
+ {
2029
+ this._nextMonth();
2030
+ }
2031
+ } );
2032
+ }
1820
2033
  }
1821
2034
 
1822
2035
  body.appendChild( hrow );
@@ -1831,6 +2044,7 @@ class Calendar {
1831
2044
 
1832
2045
  this.day = parseInt( tokens[ 0 ] );
1833
2046
  this.month = parseInt( tokens[ 1 ] );
2047
+ this.monthName = this.getMonthName( this.month - 1 );
1834
2048
  this.year = parseInt( tokens[ 2 ] );
1835
2049
 
1836
2050
  this.currentDate = this._getCurrentDate();
@@ -1860,7 +2074,7 @@ class Calendar {
1860
2074
  // Fill in days from previous month
1861
2075
  for( let i = firstDay - 1; i >= 0; i--)
1862
2076
  {
1863
- calendarDays.push( { day: daysInPrevMonth - i, currentMonth: false } );
2077
+ calendarDays.push( { day: daysInPrevMonth - i, currentMonth: false, prevMonth: true } );
1864
2078
  }
1865
2079
 
1866
2080
  // Fill in current month days
@@ -1873,7 +2087,7 @@ class Calendar {
1873
2087
  const remaining = 42 - calendarDays.length;
1874
2088
  for( let i = 1; i <= remaining; i++ )
1875
2089
  {
1876
- calendarDays.push( { day: i, currentMonth: false } );
2090
+ calendarDays.push( { day: i, currentMonth: false, nextMonth: true } );
1877
2091
  }
1878
2092
 
1879
2093
  this.monthName = this.getMonthName( month );
@@ -1889,8 +2103,21 @@ class Calendar {
1889
2103
  return formatter.format( new Date( 2000, monthIndex, 1 ) );
1890
2104
  }
1891
2105
 
1892
- getFullDate() {
1893
- return `${ this.monthName } ${ this.day }${ this._getOrdinalSuffix( this.day ) }, ${ this.year }`;
2106
+ getFullDate( monthName, day, year ) {
2107
+ return `${ monthName ?? this.monthName } ${ day ?? this.day }${ this._getOrdinalSuffix( day ?? this.day ) }, ${ year ?? this.year }`;
2108
+ }
2109
+
2110
+ setRange( range ) {
2111
+ console.assert( range.constructor === Array, "Date Range must be in Array format" );
2112
+ this.range = range;
2113
+ this.refresh();
2114
+ }
2115
+
2116
+ setMonth( month ) {
2117
+
2118
+ this.month = month;
2119
+
2120
+ this.fromMonthYear( this.month, this.year );
1894
2121
  }
1895
2122
 
1896
2123
  _getOrdinalSuffix( day ) {
@@ -1907,6 +2134,110 @@ class Calendar {
1907
2134
 
1908
2135
  LX.Calendar = Calendar;
1909
2136
 
2137
+ class CalendarRange {
2138
+
2139
+ /**
2140
+ * @constructor CalendarRange
2141
+ * @param {Array} range ["DD/MM/YYYY", "DD/MM/YYYY"]
2142
+ * @param {Object} options
2143
+ */
2144
+
2145
+ constructor( range, options = {} ) {
2146
+
2147
+ this.root = LX.makeContainer( ["auto", "auto"], "flex flex-row" );
2148
+
2149
+ console.assert( range && range.constructor === Array, "Range cannot be empty and has to be an Array!" );
2150
+
2151
+ let mustSetMonth = null;
2152
+ let dateReversed = false;
2153
+
2154
+ // Fix any issues with date range picking
2155
+ {
2156
+ const t0 = LX.dateFromDateString( range[ 0 ] );
2157
+ const t1 = LX.dateFromDateString( range[ 1 ] );
2158
+
2159
+ if( t0 > t1 )
2160
+ {
2161
+ const tmp = range[ 0 ];
2162
+ range[ 0 ] = range[ 1 ];
2163
+ range[ 1 ] = tmp;
2164
+ dateReversed = true;
2165
+ }
2166
+
2167
+ mustSetMonth = (dateReversed ? t1.getMonth() : t0.getMonth() ) + 2; // +1 to convert range, +1 to use next month
2168
+ }
2169
+
2170
+ this.from = range[ 0 ];
2171
+ this.to = range[ 1 ];
2172
+
2173
+ this._selectingRange = false;
2174
+
2175
+ const onChange = ( date ) => {
2176
+
2177
+ const newDateString = `${ date.day }/${ date.month }/${ date.year }`;
2178
+
2179
+ if( !this._selectingRange )
2180
+ {
2181
+ this.from = this.to = newDateString;
2182
+ this._selectingRange = true;
2183
+ }
2184
+ else
2185
+ {
2186
+ this.to = newDateString;
2187
+ this._selectingRange = false;
2188
+ }
2189
+
2190
+ const newRange = [ this.from, this.to ];
2191
+
2192
+ this.fromCalendar.setRange( newRange );
2193
+ this.toCalendar.setRange( newRange );
2194
+
2195
+ if( options.onChange )
2196
+ {
2197
+ options.onChange( newRange );
2198
+ }
2199
+
2200
+ };
2201
+
2202
+ this.fromCalendar = new LX.Calendar( this.from, {
2203
+ skipNextMonth: true,
2204
+ onChange,
2205
+ onPreviousMonth: () => {
2206
+ this.toCalendar._previousMonth();
2207
+ },
2208
+ range
2209
+ });
2210
+
2211
+ this.toCalendar = new LX.Calendar( this.to, {
2212
+ skipPrevMonth: true,
2213
+ onChange,
2214
+ onNextMonth: () => {
2215
+ this.fromCalendar._nextMonth();
2216
+ },
2217
+ range
2218
+ });
2219
+
2220
+ console.assert( mustSetMonth && "New Month must be valid" );
2221
+ this.toCalendar.setMonth( mustSetMonth );
2222
+
2223
+ this.root.appendChild( this.fromCalendar.root );
2224
+ this.root.appendChild( this.toCalendar.root );
2225
+ }
2226
+
2227
+ getFullDate() {
2228
+
2229
+ const d0 = LX.dateFromDateString( this.from );
2230
+ const d0Month = this.fromCalendar.getMonthName( d0.getMonth() );
2231
+
2232
+ const d1 = LX.dateFromDateString( this.to );
2233
+ const d1Month = this.toCalendar.getMonthName( d1.getMonth() );
2234
+
2235
+ return `${ this.fromCalendar.getFullDate( d0Month, d0.getDate(), d0.getFullYear() ) } to ${ this.toCalendar.getFullDate( d1Month, d1.getDate(), d1.getFullYear() ) }`;
2236
+ }
2237
+ }
2238
+
2239
+ LX.CalendarRange = CalendarRange;
2240
+
1910
2241
  /**
1911
2242
  * @class Tabs
1912
2243
  */
@@ -2111,6 +2442,19 @@ class Tabs {
2111
2442
  tabEl.instance = this;
2112
2443
  contentEl.id = tabEl.id + "_content";
2113
2444
 
2445
+ if( options.badge )
2446
+ {
2447
+ const asChild = options.badge.asChild ?? false;
2448
+ const badgeOptions = { };
2449
+
2450
+ if( asChild )
2451
+ {
2452
+ badgeOptions.parent = tabEl;
2453
+ }
2454
+
2455
+ tabEl.innerHTML += LX.badge( options.badge.content ?? "", options.badge.className, badgeOptions );
2456
+ }
2457
+
2114
2458
  if( tabEl.selected )
2115
2459
  {
2116
2460
  this.selected = name;
@@ -2383,7 +2727,7 @@ class Dialog {
2383
2727
 
2384
2728
  if( !callback )
2385
2729
  {
2386
- console.warn("Content is empty, add some widgets using 'callback' parameter!");
2730
+ console.warn("Content is empty, add some components using 'callback' parameter!");
2387
2731
  }
2388
2732
 
2389
2733
  this._oncreate = callback;
@@ -2398,6 +2742,7 @@ class Dialog {
2398
2742
  let root = document.createElement('dialog');
2399
2743
  root.className = "lexdialog " + (options.className ?? "");
2400
2744
  root.id = options.id ?? "dialog" + Dialog._last_id++;
2745
+ root.dataset["modal"] = modal;
2401
2746
  LX.root.appendChild( root );
2402
2747
 
2403
2748
  LX.doAsync( () => {
@@ -2451,9 +2796,9 @@ class Dialog {
2451
2796
  const panelChildCount = panel.root.childElementCount;
2452
2797
 
2453
2798
  const branch = panel.branch( data.name, { closed: data.closed } );
2454
- branch.widgets = data.widgets;
2799
+ branch.components = data.components;
2455
2800
 
2456
- for( let w of branch.widgets )
2801
+ for( let w of branch.components )
2457
2802
  {
2458
2803
  branch.content.appendChild( w.root );
2459
2804
  }
@@ -2697,9 +3042,6 @@ class ContextMenu {
2697
3042
 
2698
3043
  this.root = document.createElement( "div" );
2699
3044
  this.root.className = "lexcontextmenu";
2700
- this.root.style.left = ( event.x - 48 ) + "px";
2701
- this.root.style.top = ( event.y - 8 ) + "px";
2702
-
2703
3045
  this.root.addEventListener("mouseleave", function() {
2704
3046
  this.remove();
2705
3047
  });
@@ -2715,31 +3057,57 @@ class ContextMenu {
2715
3057
  item[ "icon" ] = options.icon;
2716
3058
  this.items.push( item );
2717
3059
  }
3060
+
3061
+ const nestedDialog = event.target.closest( "dialog" );
3062
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
3063
+ {
3064
+ this._parent = nestedDialog;
3065
+ }
3066
+ else
3067
+ {
3068
+ this._parent = LX.root;
3069
+ }
3070
+
3071
+ this._parent.appendChild( this.root );
3072
+
3073
+ // Set position based on parent
3074
+ const position = [ event.x - 48, event.y - 8 ];
3075
+ if( this._parent instanceof HTMLDialogElement )
3076
+ {
3077
+ let parentRect = this._parent.getBoundingClientRect();
3078
+ position[ 0 ] -= parentRect.x;
3079
+ position[ 1 ] -= parentRect.y;
3080
+ }
3081
+
3082
+ this.root.style.left = `${ position[ 0 ] }px`;
3083
+ this.root.style.top = `${ position[ 1 ] }px`;
2718
3084
  }
2719
3085
 
2720
3086
  _adjustPosition( div, margin, useAbsolute = false ) {
2721
3087
 
2722
3088
  let rect = div.getBoundingClientRect();
3089
+ let left = parseInt( div.style.left );
3090
+ let top = parseInt( div.style.top );
2723
3091
 
2724
3092
  if( !useAbsolute )
2725
3093
  {
2726
3094
  let width = rect.width;
2727
3095
  if( rect.left < 0 )
2728
3096
  {
2729
- div.style.left = margin + "px";
3097
+ left = margin;
2730
3098
  }
2731
3099
  else if( window.innerWidth - rect.right < 0 )
2732
3100
  {
2733
- div.style.left = (window.innerWidth - width - margin) + "px";
3101
+ left = (window.innerWidth - width - margin);
2734
3102
  }
2735
3103
 
2736
3104
  if( rect.top < 0 )
2737
3105
  {
2738
- div.style.top = margin + "px";
3106
+ top = margin;
2739
3107
  }
2740
3108
  else if( (rect.top + rect.height) > window.innerHeight )
2741
3109
  {
2742
- div.style.top = (window.innerHeight - rect.height - margin) + "px";
3110
+ top = (window.innerHeight - rect.height - margin);
2743
3111
  }
2744
3112
  }
2745
3113
  else
@@ -2747,15 +3115,18 @@ class ContextMenu {
2747
3115
  let dt = window.innerWidth - rect.right;
2748
3116
  if( dt < 0 )
2749
3117
  {
2750
- div.style.left = div.offsetLeft + (dt - margin) + "px";
3118
+ left = div.offsetLeft + (dt - margin);
2751
3119
  }
2752
3120
 
2753
3121
  dt = window.innerHeight - (rect.top + rect.height);
2754
3122
  if( dt < 0 )
2755
3123
  {
2756
- div.style.top = div.offsetTop + (dt - margin + 20 ) + "px";
3124
+ top = div.offsetTop + (dt - margin + 20 );
2757
3125
  }
2758
3126
  }
3127
+
3128
+ div.style.left = `${ left }px`;
3129
+ div.style.top = `${ top }px`;
2759
3130
  }
2760
3131
 
2761
3132
  _createSubmenu( o, k, c, d ) {
@@ -2977,7 +3348,6 @@ LX.ContextMenu = ContextMenu;
2977
3348
  function addContextMenu( title, event, callback, options )
2978
3349
  {
2979
3350
  const menu = new ContextMenu( event, title, options );
2980
- LX.root.appendChild( menu.root );
2981
3351
 
2982
3352
  if( callback )
2983
3353
  {
@@ -4093,6 +4463,43 @@ class CanvasMap2D {
4093
4463
 
4094
4464
  LX.CanvasMap2D = CanvasMap2D;
4095
4465
 
4466
+ class Skeleton {
4467
+
4468
+ constructor( elements ) {
4469
+
4470
+ this.root = LX.makeContainer( [ "auto", "auto" ], "flex flex-row lexskeleton" );
4471
+
4472
+ if( elements.constructor === String )
4473
+ {
4474
+ this.root.innerHTML = elements;
4475
+ }
4476
+ else
4477
+ {
4478
+ // Force array
4479
+ elements = [].concat( elements );
4480
+
4481
+ for( let e of elements )
4482
+ {
4483
+ this.root.appendChild( e );
4484
+ }
4485
+ }
4486
+ }
4487
+
4488
+ destroy() {
4489
+
4490
+ this.root.dataset[ "closed" ] = true;
4491
+
4492
+ LX.doAsync( () => {
4493
+ this.root.remove();
4494
+ this.root = null;
4495
+ }, 200 );
4496
+ }
4497
+ }
4498
+
4499
+ LX.Skeleton = Skeleton;
4500
+
4501
+ // Js native overrides
4502
+
4096
4503
  Object.defineProperty(String.prototype, 'lastChar', {
4097
4504
  get: function() { return this[ this.length - 1 ]; },
4098
4505
  enumerable: true,
@@ -4393,6 +4800,28 @@ function deleteElement( element )
4393
4800
 
4394
4801
  LX.deleteElement = deleteElement;
4395
4802
 
4803
+ /**
4804
+ * @method toCamelCase
4805
+ * @param {String} str
4806
+ */
4807
+ function toCamelCase( str )
4808
+ {
4809
+ return str.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase());
4810
+ }
4811
+
4812
+ LX.toCamelCase = toCamelCase;
4813
+
4814
+ /**
4815
+ * @method toTitleCase
4816
+ * @param {String} str
4817
+ */
4818
+ function toTitleCase( str )
4819
+ {
4820
+ return str.replace(/-/g, " ").toLowerCase().replace(/\b\w/g, char => char.toUpperCase());
4821
+ }
4822
+
4823
+ LX.toTitleCase = toTitleCase;
4824
+
4396
4825
  /**
4397
4826
  * @method getSupportedDOMName
4398
4827
  * @description Convert a text string to a valid DOM name
@@ -4421,12 +4850,12 @@ LX.getSupportedDOMName = getSupportedDOMName;
4421
4850
 
4422
4851
  /**
4423
4852
  * @method has
4424
- * @description Ask if LexGUI is using a specific component
4425
- * @param {String} componentName Name of the LexGUI component
4853
+ * @description Ask if LexGUI is using a specific extension
4854
+ * @param {String} extensionName Name of the LexGUI extension
4426
4855
  */
4427
- function has( componentName )
4856
+ function has( extensionName )
4428
4857
  {
4429
- return ( LX.components.indexOf( componentName ) > -1 );
4858
+ return ( LX.extensions.indexOf( extensionName ) > -1 );
4430
4859
  }
4431
4860
 
4432
4861
  LX.has = has;
@@ -4603,6 +5032,19 @@ function getThemeColor( colorName )
4603
5032
 
4604
5033
  LX.getThemeColor = getThemeColor;
4605
5034
 
5035
+ /**
5036
+ * @method switchSpacing
5037
+ * @description Toggles between "default" and "compact" spacing layouts
5038
+ */
5039
+ function switchSpacing()
5040
+ {
5041
+ const currentSpacing = document.documentElement.getAttribute( "data-spacing" ) ?? "default";
5042
+ document.documentElement.setAttribute( "data-spacing", ( currentSpacing == "default" ) ? "compact" : "default" );
5043
+ LX.emit( "@on_new_spacing_layout", currentSpacing );
5044
+ }
5045
+
5046
+ LX.switchSpacing = switchSpacing;
5047
+
4606
5048
  /**
4607
5049
  * @method getBase64Image
4608
5050
  * @description Convert an image to a base64 string
@@ -4757,6 +5199,22 @@ function hsvToRgb( hsv )
4757
5199
 
4758
5200
  LX.hsvToRgb = hsvToRgb;
4759
5201
 
5202
+ /**
5203
+ * @method dateFromDateString
5204
+ * @description Get an instance of Date() from a Date in String format (DD/MM/YYYY)
5205
+ * @param {String} dateString
5206
+ */
5207
+ function dateFromDateString( dateString )
5208
+ {
5209
+ const tokens = dateString.split( '/' );
5210
+ const day = parseInt( tokens[ 0 ] );
5211
+ const month = parseInt( tokens[ 1 ] );
5212
+ const year = parseInt( tokens[ 2 ] );
5213
+ return new Date( `${ month }/${ day }/${ year }` );
5214
+ }
5215
+
5216
+ LX.dateFromDateString = dateFromDateString;
5217
+
4760
5218
  /**
4761
5219
  * @method measureRealWidth
4762
5220
  * @description Measure the pixel width of a text
@@ -5128,6 +5586,80 @@ function makeKbd( keys, useSpecialKeys = true, extraClass = "" )
5128
5586
 
5129
5587
  LX.makeKbd = makeKbd;
5130
5588
 
5589
+ /**
5590
+ * @method makeBreadcrumb
5591
+ * @description Displays the path to the current resource using a hierarchy
5592
+ * @param {Array} items
5593
+ * @param {Object} options
5594
+ * maxItems: Max items until ellipsis is used to overflow
5595
+ * separatorIcon: Customize separator icon
5596
+ */
5597
+ function makeBreadcrumb( items, options = {} )
5598
+ {
5599
+ const breadcrumb = LX.makeContainer( ["auto", "auto"], "flex flex-row gap-1" );
5600
+
5601
+ const separatorIcon = options.separatorIcon ?? "ChevronRight";
5602
+ const maxItems = options.maxItems ?? 4;
5603
+ const eraseNum = items.length - maxItems;
5604
+ if( eraseNum > 0 )
5605
+ {
5606
+ const erased = items.splice( 1, eraseNum + 1 );
5607
+ const ellipsisItem = { title: "...", ellipsis: erased.map( v => v.title ).join( "/" ) };
5608
+ items.splice( 1, 0, ellipsisItem );
5609
+ }
5610
+
5611
+ for( let i = 0; i < items.length; ++i )
5612
+ {
5613
+ const item = items[ i ];
5614
+ console.assert( item.title, "Breadcrumb item must have a title!" );
5615
+
5616
+ if( i != 0 )
5617
+ {
5618
+ const icon = LX.makeIcon( separatorIcon, { svgClass: "sm fg-secondary separator" } );
5619
+ breadcrumb.appendChild( icon );
5620
+ }
5621
+
5622
+ const lastElement = ( i == items.length - 1 );
5623
+ const breadcrumbItem = LX.makeContainer( ["auto", "auto"], `p-1 flex flex-row gap-1 items-center ${ lastElement ? "" : "fg-secondary" }` );
5624
+ breadcrumb.appendChild( breadcrumbItem );
5625
+
5626
+ let itemTitle = LX.makeElement( "p", "", item.title );
5627
+ if( item.icon )
5628
+ {
5629
+ breadcrumbItem.appendChild( LX.makeIcon( item.icon, { svgClass: "sm" } ) );
5630
+ }
5631
+
5632
+ if( item.items !== undefined )
5633
+ {
5634
+ const bDropdownTrigger = LX.makeContainer( ["auto", "auto"], `${ lastElement ? "" : "fg-secondary" }` );
5635
+ bDropdownTrigger.listen( "click", (e) => {
5636
+ new LX.DropdownMenu( e.target, item.items, { side: "bottom", align: "start" });
5637
+ } );
5638
+ bDropdownTrigger.append( itemTitle );
5639
+ breadcrumbItem.appendChild( bDropdownTrigger );
5640
+ }
5641
+ else if( item.url !== undefined )
5642
+ {
5643
+ let itemUrl = LX.makeElement( "a", `decoration-none fg-${ lastElement ? "primary" : "secondary" }`, "", breadcrumbItem );
5644
+ itemUrl.href = item.url;
5645
+ itemUrl.appendChild( itemTitle );
5646
+ }
5647
+ else
5648
+ {
5649
+ breadcrumbItem.appendChild( itemTitle );
5650
+ }
5651
+
5652
+ if( item.ellipsis )
5653
+ {
5654
+ LX.asTooltip( breadcrumbItem, item.ellipsis, { side: "bottom", offset: 4 } );
5655
+ }
5656
+ }
5657
+
5658
+ return breadcrumb;
5659
+ }
5660
+
5661
+ LX.makeBreadcrumb = makeBreadcrumb;
5662
+
5131
5663
  /**
5132
5664
  * @method makeIcon
5133
5665
  * @description Gets an SVG element using one of LX.ICONS
@@ -5444,6 +5976,7 @@ LX.prompt = prompt;
5444
5976
  * @param {String} title
5445
5977
  * @param {String} description (Optional)
5446
5978
  * @param {Object} options
5979
+ * position: Set new position for the toasts ("top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right")
5447
5980
  * action: Data of the custom action { name, callback }
5448
5981
  * closable: Allow closing the toast
5449
5982
  * timeout: Time in which the toast closed automatically, in ms. -1 means persistent. [3000]
@@ -5460,9 +5993,48 @@ function toast( title, description, options = {} )
5460
5993
 
5461
5994
  const toast = document.createElement( "li" );
5462
5995
  toast.className = "lextoast";
5463
- toast.style.translate = "0 calc(100% + 30px)";
5464
5996
  this.notifications.prepend( toast );
5465
5997
 
5998
+ const [ positionVertical, positionHorizontal ] = options.position ? options.position.split( "-" ) : [ "bottom", "right" ];
5999
+
6000
+ // Reset style
6001
+ this.notifications.style.right = "unset";
6002
+ this.notifications.style.left = "unset";
6003
+ this.notifications.style.top = "unset";
6004
+ this.notifications.style.bottom = "unset";
6005
+ this.notifications.style.placeSelf = "unset";
6006
+
6007
+ switch( positionVertical )
6008
+ {
6009
+ case "top":
6010
+ toast.style.translate = "0 -30px";
6011
+ this.notifications.style.top = "1rem";
6012
+ this.notifications.style.flexDirection = "column";
6013
+ break;
6014
+ case "bottom":
6015
+ toast.style.translate = "0 calc(100% + 30px)";
6016
+ this.notifications.style.top = "auto";
6017
+ this.notifications.style.bottom = "1rem";
6018
+ this.notifications.style.flexDirection = "column-reverse";
6019
+ break;
6020
+ }
6021
+
6022
+ switch( positionHorizontal )
6023
+ {
6024
+ case "left":
6025
+ this.notifications.style.left = "1rem";
6026
+ break;
6027
+ case "center":
6028
+ this.notifications.style.placeSelf = "center";
6029
+ break;
6030
+ case "right":
6031
+ this.notifications.style.right = "1rem";
6032
+ break;
6033
+ }
6034
+
6035
+ toast.classList.add( positionVertical );
6036
+ toast.classList.add( positionHorizontal );
6037
+
5466
6038
  LX.doAsync( () => {
5467
6039
 
5468
6040
  if( this.notifications.offsetWidth > this.notifications.iWidth )
@@ -5501,7 +6073,7 @@ function toast( title, description, options = {} )
5501
6073
  const that = this;
5502
6074
 
5503
6075
  toast.close = function() {
5504
- this.dataset[ "closed" ] = true;
6076
+ this.dataset[ "open" ] = false;
5505
6077
  LX.doAsync( () => {
5506
6078
  this.remove();
5507
6079
  if( !that.notifications.childElementCount )
@@ -5547,7 +6119,32 @@ function badge( text, className, options = {} )
5547
6119
  const container = document.createElement( "div" );
5548
6120
  container.innerHTML = text;
5549
6121
  container.className = "lexbadge " + ( className ?? "" );
6122
+
6123
+ if( options.chip )
6124
+ {
6125
+ container.classList.add( "chip" );
6126
+ }
6127
+
5550
6128
  Object.assign( container.style, options.style ?? {} );
6129
+
6130
+ if( options.callback )
6131
+ {
6132
+ const arrowIcon = LX.makeIcon( "ArrowUpRight", { svgClass: "xs fg-contrast" } );
6133
+ arrowIcon.querySelector("svg").style.marginLeft = "-0.25rem";
6134
+ container.innerHTML += arrowIcon.innerHTML;
6135
+ container.addEventListener( "click", e => {
6136
+ e.preventDefault();
6137
+ e.stopPropagation();
6138
+ options.callback();
6139
+ } );
6140
+ }
6141
+
6142
+ if( options.parent )
6143
+ {
6144
+ options.parent.classList.add( "lexbadge-parent" );
6145
+ options.parent.appendChild( container );
6146
+ }
6147
+
5551
6148
  return ( options.asElement ?? false ) ? container : container.outerHTML;
5552
6149
  }
5553
6150
 
@@ -5622,6 +6219,7 @@ function asTooltip( trigger, content, options = {} )
5622
6219
  trigger.dataset[ "disableTooltip" ] = !( options.active ?? true );
5623
6220
 
5624
6221
  let tooltipDom = null;
6222
+ let tooltipParent = LX.root;
5625
6223
 
5626
6224
  trigger.addEventListener( "mouseenter", function(e) {
5627
6225
 
@@ -5630,12 +6228,22 @@ function asTooltip( trigger, content, options = {} )
5630
6228
  return;
5631
6229
  }
5632
6230
 
5633
- LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
5634
-
5635
6231
  tooltipDom = document.createElement( "div" );
5636
6232
  tooltipDom.className = "lextooltip";
5637
6233
  tooltipDom.innerHTML = content;
5638
6234
 
6235
+ const nestedDialog = trigger.closest( "dialog" );
6236
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
6237
+ {
6238
+ tooltipParent = nestedDialog;
6239
+ }
6240
+
6241
+ // Remove other first
6242
+ LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
6243
+
6244
+ // Append new tooltip
6245
+ tooltipParent.appendChild( tooltipDom );
6246
+
5639
6247
  LX.doAsync( () => {
5640
6248
 
5641
6249
  const position = [ 0, 0 ];
@@ -5670,11 +6278,16 @@ function asTooltip( trigger, content, options = {} )
5670
6278
  position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - tooltipDom.offsetWidth - 4 );
5671
6279
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - tooltipDom.offsetHeight - 4 );
5672
6280
 
6281
+ if( tooltipParent instanceof HTMLDialogElement )
6282
+ {
6283
+ let parentRect = tooltipParent.getBoundingClientRect();
6284
+ position[ 0 ] -= parentRect.x;
6285
+ position[ 1 ] -= parentRect.y;
6286
+ }
6287
+
5673
6288
  tooltipDom.style.left = `${ position[ 0 ] }px`;
5674
6289
  tooltipDom.style.top = `${ position[ 1 ] }px`;
5675
6290
  } );
5676
-
5677
- LX.root.appendChild( tooltipDom );
5678
6291
  } );
5679
6292
 
5680
6293
  trigger.addEventListener( "mouseleave", function(e) {
@@ -6120,15 +6733,15 @@ class AreaOverlayButtons {
6120
6733
  }
6121
6734
 
6122
6735
  let callback = b.callback;
6123
- let widget = null;
6736
+ let component = null;
6124
6737
 
6125
6738
  if( b.options )
6126
6739
  {
6127
- widget = overlayPanel.addSelect( null, b.options, b.value ?? b.name, callback, _options );
6740
+ component = overlayPanel.addSelect( null, b.options, b.value ?? b.name, callback, _options );
6128
6741
  }
6129
6742
  else
6130
6743
  {
6131
- widget = overlayPanel.addButton( null, b.name, function( value, event ) {
6744
+ component = overlayPanel.addButton( null, b.name, function( value, event ) {
6132
6745
  if( b.selectable )
6133
6746
  {
6134
6747
  if( b.group )
@@ -6145,13 +6758,13 @@ class AreaOverlayButtons {
6145
6758
 
6146
6759
  if( callback )
6147
6760
  {
6148
- callback( value, event, widget.root );
6761
+ callback( value, event, component.root );
6149
6762
  }
6150
6763
 
6151
6764
  }, _options );
6152
6765
  }
6153
6766
 
6154
- this.buttons[ b.name ] = widget;
6767
+ this.buttons[ b.name ] = component;
6155
6768
 
6156
6769
  // ends the group
6157
6770
  if( overlayGroup && last )
@@ -6225,6 +6838,7 @@ class Area {
6225
6838
  * minHeight: Minimum height to be applied when resizing
6226
6839
  * maxWidth: Maximum width to be applied when resizing
6227
6840
  * maxHeight: Maximum height to be applied when resizing
6841
+ * layout: Layout to automatically split the area
6228
6842
  */
6229
6843
 
6230
6844
  constructor( options = {} ) {
@@ -6270,6 +6884,11 @@ class Area {
6270
6884
  lexroot.appendChild( this.root );
6271
6885
  }
6272
6886
 
6887
+ if( options.layout )
6888
+ {
6889
+ this.setLayout( options.layout );
6890
+ }
6891
+
6273
6892
  let overlay = options.overlay;
6274
6893
 
6275
6894
  if( overlay )
@@ -6430,6 +7049,47 @@ class Area {
6430
7049
  this.root.appendChild( element );
6431
7050
  }
6432
7051
 
7052
+ /**
7053
+ * @method setLayout
7054
+ * @description Automatically split the area to generate the desired layout
7055
+ * @param {Array} layout
7056
+ */
7057
+
7058
+ setLayout( layout ) {
7059
+
7060
+ this.layout = LX.deepCopy( layout );
7061
+
7062
+ if( !layout.splits )
7063
+ {
7064
+ console.warn( "Area layout has no splits!" );
7065
+ return;
7066
+ }
7067
+
7068
+ const _splitArea = ( area, layout ) => {
7069
+
7070
+ if( layout.className )
7071
+ {
7072
+ area.root.className += ` ${ layout.className }`;
7073
+ }
7074
+
7075
+ if( !layout.splits )
7076
+ {
7077
+ return;
7078
+ }
7079
+
7080
+ const type = layout.type ?? "horizontal";
7081
+ const resize = layout.resize ?? true;
7082
+ const minimizable = layout.minimizable ?? false;
7083
+ const [ splitA, splitB ] = area.split( { type, resize, minimizable, sizes: [ layout.splits[ 0 ].size, layout.splits[ 1 ].size ] } );
7084
+
7085
+ _splitArea( splitA, layout.splits[ 0 ] );
7086
+ _splitArea( splitB, layout.splits[ 1 ] );
7087
+
7088
+ };
7089
+
7090
+ _splitArea( this, layout );
7091
+ }
7092
+
6433
7093
  /**
6434
7094
  * @method split
6435
7095
  * @param {Object} options
@@ -6688,12 +7348,17 @@ class Area {
6688
7348
  doc.addEventListener( 'mouseup', innerMouseUp );
6689
7349
  e.stopPropagation();
6690
7350
  e.preventDefault();
6691
- document.body.classList.add( 'nocursor' );
6692
- that.splitBar.classList.add( 'nocursor' );
6693
7351
  }
6694
7352
 
6695
7353
  function innerMouseMove( e )
6696
7354
  {
7355
+ const rect = that.root.getBoundingClientRect();
7356
+ if( ( ( e.x ) < rect.x || ( e.x ) > ( rect.x + rect.width ) ) ||
7357
+ ( ( e.y ) < rect.y || ( e.y ) > ( rect.y + rect.height ) ) )
7358
+ {
7359
+ return;
7360
+ }
7361
+
6697
7362
  if( that.type == "horizontal" )
6698
7363
  {
6699
7364
  that._moveSplit( -e.movementX );
@@ -6712,8 +7377,6 @@ class Area {
6712
7377
  const doc = that.root.ownerDocument;
6713
7378
  doc.removeEventListener( 'mousemove', innerMouseMove );
6714
7379
  doc.removeEventListener( 'mouseup', innerMouseUp );
6715
- document.body.classList.remove( 'nocursor' );
6716
- that.splitBar.classList.remove( 'nocursor' );
6717
7380
  }
6718
7381
 
6719
7382
  return this.sections;
@@ -7072,10 +7735,11 @@ class Area {
7072
7735
  }
7073
7736
  else
7074
7737
  {
7738
+ const parentHeight = this.size[ 1 ];
7075
7739
  var size = Math.max( ( a2Root.offsetHeight + dt ) + a2.offset, parseInt( a2.minHeight ) );
7740
+ size = Math.min( parentHeight - LX.DEFAULT_SPLITBAR_SIZE, size );
7076
7741
  if( forceWidth ) size = forceWidth;
7077
7742
 
7078
- const parentHeight = this.size[ 1 ];
7079
7743
  const bottomPercent = ( size / parentHeight ) * 100;
7080
7744
  const topPercent = Math.max( 0, 100 - bottomPercent );
7081
7745
 
@@ -7134,13 +7798,13 @@ class Area {
7134
7798
  }
7135
7799
  LX.Area = Area;
7136
7800
 
7137
- // widget.js @jxarco
7801
+ // base_component.js @jxarco
7138
7802
 
7139
7803
  /**
7140
- * @class Widget
7804
+ * @class BaseComponent
7141
7805
  */
7142
7806
 
7143
- class Widget {
7807
+ class BaseComponent {
7144
7808
 
7145
7809
  static NONE = 0;
7146
7810
  static TEXT = 1;
@@ -7180,14 +7844,15 @@ class Widget {
7180
7844
  static TABS = 35;
7181
7845
  static DATE = 36;
7182
7846
  static MAP2D = 37;
7183
- static LABEL = 38;
7184
- static BLANK = 39;
7847
+ static LABEL = 39;
7848
+ static BLANK = 40;
7849
+ static RATE = 41;
7185
7850
 
7186
7851
  static NO_CONTEXT_TYPES = [
7187
- Widget.BUTTON,
7188
- Widget.LIST,
7189
- Widget.FILE,
7190
- Widget.PROGRESS
7852
+ BaseComponent.BUTTON,
7853
+ BaseComponent.LIST,
7854
+ BaseComponent.FILE,
7855
+ BaseComponent.PROGRESS
7191
7856
  ];
7192
7857
 
7193
7858
  constructor( type, name, value, options = {} ) {
@@ -7198,7 +7863,7 @@ class Widget {
7198
7863
  this._initialValue = value;
7199
7864
 
7200
7865
  const root = document.createElement( 'div' );
7201
- root.className = "lexwidget";
7866
+ root.className = "lexcomponent";
7202
7867
 
7203
7868
  if( options.id )
7204
7869
  {
@@ -7215,7 +7880,7 @@ class Widget {
7215
7880
  root.className += " " + options.className;
7216
7881
  }
7217
7882
 
7218
- if( type != Widget.TITLE )
7883
+ if( type != BaseComponent.TITLE )
7219
7884
  {
7220
7885
  if( options.width )
7221
7886
  {
@@ -7234,7 +7899,7 @@ class Widget {
7234
7899
  root.style.height = root.style.minHeight = options.height;
7235
7900
  }
7236
7901
 
7237
- LX.widgetResizeObserver.observe( root );
7902
+ LX.componentResizeObserver.observe( root );
7238
7903
  }
7239
7904
 
7240
7905
  if( name != undefined )
@@ -7242,7 +7907,7 @@ class Widget {
7242
7907
  if( !( options.hideName ?? false ) )
7243
7908
  {
7244
7909
  let domName = document.createElement( 'div' );
7245
- domName.className = "lexwidgetname";
7910
+ domName.className = "lexcomponentname";
7246
7911
 
7247
7912
  if( options.justifyName )
7248
7913
  {
@@ -7304,7 +7969,7 @@ class Widget {
7304
7969
  }
7305
7970
 
7306
7971
  _canPaste() {
7307
- let pasteAllowed = this.type === Widget.CUSTOM ?
7972
+ let pasteAllowed = this.type === BaseComponent.CUSTOM ?
7308
7973
  ( navigator.clipboard.customIdx !== undefined && this.customIdx == navigator.clipboard.customIdx ) : navigator.clipboard.type === this.type;
7309
7974
 
7310
7975
  pasteAllowed &= ( this.disabled !== true );
@@ -7341,7 +8006,7 @@ class Widget {
7341
8006
 
7342
8007
  if( this.onSetValue )
7343
8008
  {
7344
- let resetButton = this.root.querySelector( ".lexwidgetname .lexicon" );
8009
+ let resetButton = this.root.querySelector( ".lexcomponentname .lexicon" );
7345
8010
  if( resetButton )
7346
8011
  {
7347
8012
  resetButton.style.display = ( value != this.value() ? "block" : "none" );
@@ -7367,7 +8032,7 @@ class Widget {
7367
8032
 
7368
8033
  oncontextmenu( e ) {
7369
8034
 
7370
- if( Widget.NO_CONTEXT_TYPES.includes( this.type ) )
8035
+ if( BaseComponent.NO_CONTEXT_TYPES.includes( this.type ) )
7371
8036
  {
7372
8037
  return;
7373
8038
  }
@@ -7398,41 +8063,42 @@ class Widget {
7398
8063
 
7399
8064
  switch( this.type )
7400
8065
  {
7401
- case Widget.TEXT: return "Text";
7402
- case Widget.TEXTAREA: return "TextArea";
7403
- case Widget.BUTTON: return "Button";
7404
- case Widget.SELECT: return "Select";
7405
- case Widget.CHECKBOX: return "Checkbox";
7406
- case Widget.TOGGLE: return "Toggle";
7407
- case Widget.RADIO: return "Radio";
7408
- case Widget.COLOR: return "Color";
7409
- case Widget.RANGE: return "Range";
7410
- case Widget.NUMBER: return "Number";
7411
- case Widget.VECTOR: return "Vector";
7412
- case Widget.TREE: return "Tree";
7413
- case Widget.PROGRESS: return "Progress";
7414
- case Widget.FILE: return "File";
7415
- case Widget.LAYERS: return "Layers";
7416
- case Widget.ARRAY: return "Array";
7417
- case Widget.LIST: return "List";
7418
- case Widget.TAGS: return "Tags";
7419
- case Widget.CURVE: return "Curve";
7420
- case Widget.KNOB: return "Knob";
7421
- case Widget.SIZE: return "Size";
7422
- case Widget.PAD: return "Pad";
7423
- case Widget.FORM: return "Form";
7424
- case Widget.DIAL: return "Dial";
7425
- case Widget.COUNTER: return "Counter";
7426
- case Widget.TABLE: return "Table";
7427
- case Widget.TABS: return "Tabs";
7428
- case Widget.DATE: return "Date";
7429
- case Widget.MAP2D: return "Map2D";
7430
- case Widget.LABEL: return "Label";
7431
- case Widget.BLANK: return "Blank";
7432
- case Widget.CUSTOM: return this.customName;
7433
- }
7434
-
7435
- console.error( `Unknown Widget type: ${ this.type }` );
8066
+ case BaseComponent.TEXT: return "Text";
8067
+ case BaseComponent.TEXTAREA: return "TextArea";
8068
+ case BaseComponent.BUTTON: return "Button";
8069
+ case BaseComponent.SELECT: return "Select";
8070
+ case BaseComponent.CHECKBOX: return "Checkbox";
8071
+ case BaseComponent.TOGGLE: return "Toggle";
8072
+ case BaseComponent.RADIO: return "Radio";
8073
+ case BaseComponent.COLOR: return "Color";
8074
+ case BaseComponent.RANGE: return "Range";
8075
+ case BaseComponent.NUMBER: return "Number";
8076
+ case BaseComponent.VECTOR: return "Vector";
8077
+ case BaseComponent.TREE: return "Tree";
8078
+ case BaseComponent.PROGRESS: return "Progress";
8079
+ case BaseComponent.FILE: return "File";
8080
+ case BaseComponent.LAYERS: return "Layers";
8081
+ case BaseComponent.ARRAY: return "Array";
8082
+ case BaseComponent.LIST: return "List";
8083
+ case BaseComponent.TAGS: return "Tags";
8084
+ case BaseComponent.CURVE: return "Curve";
8085
+ case BaseComponent.KNOB: return "Knob";
8086
+ case BaseComponent.SIZE: return "Size";
8087
+ case BaseComponent.PAD: return "Pad";
8088
+ case BaseComponent.FORM: return "Form";
8089
+ case BaseComponent.DIAL: return "Dial";
8090
+ case BaseComponent.COUNTER: return "Counter";
8091
+ case BaseComponent.TABLE: return "Table";
8092
+ case BaseComponent.TABS: return "Tabs";
8093
+ case BaseComponent.DATE: return "Date";
8094
+ case BaseComponent.MAP2D: return "Map2D";
8095
+ case BaseComponent.RATE: return "Rate";
8096
+ case BaseComponent.LABEL: return "Label";
8097
+ case BaseComponent.BLANK: return "Blank";
8098
+ case BaseComponent.CUSTOM: return this.customName;
8099
+ }
8100
+
8101
+ console.error( `Unknown Component type: ${ this.type }` );
7436
8102
  }
7437
8103
 
7438
8104
  refresh() {
@@ -7440,52 +8106,52 @@ class Widget {
7440
8106
  }
7441
8107
  }
7442
8108
 
7443
- LX.Widget = Widget;
8109
+ LX.BaseComponent = BaseComponent;
7444
8110
 
7445
- function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
8111
+ function ADD_CUSTOM_COMPONENT( customComponentName, options = {} )
7446
8112
  {
7447
8113
  let customIdx = LX.guidGenerator();
7448
8114
 
7449
- LX.Panel.prototype[ 'add' + customWidgetName ] = function( name, instance, callback ) {
8115
+ LX.Panel.prototype[ 'add' + customComponentName ] = function( name, instance, callback ) {
7450
8116
 
7451
8117
  const userParams = Array.from( arguments ).slice( 3 );
7452
8118
 
7453
- let widget = new Widget( Widget.CUSTOM, name, null, options );
7454
- this._attachWidget( widget );
8119
+ let component = new BaseComponent( BaseComponent.CUSTOM, name, null, options );
8120
+ this._attachComponent( component );
7455
8121
 
7456
- widget.customName = customWidgetName;
7457
- widget.customIdx = customIdx;
8122
+ component.customName = customComponentName;
8123
+ component.customIdx = customIdx;
7458
8124
 
7459
- widget.onGetValue = () => {
8125
+ component.onGetValue = () => {
7460
8126
  return instance;
7461
8127
  };
7462
8128
 
7463
- widget.onSetValue = ( newValue, skipCallback, event ) => {
8129
+ component.onSetValue = ( newValue, skipCallback, event ) => {
7464
8130
  instance = newValue;
7465
- refresh_widget();
8131
+ _refreshComponent();
7466
8132
  element.querySelector( ".lexcustomitems" ).toggleAttribute( 'hidden', false );
7467
8133
  if( !skipCallback )
7468
8134
  {
7469
- widget._trigger( new LX.IEvent( name, instance, event ), callback );
8135
+ component._trigger( new LX.IEvent( name, instance, event ), callback );
7470
8136
  }
7471
8137
  };
7472
8138
 
7473
- widget.onResize = ( rect ) => {
7474
- const realNameWidth = ( widget.root.domName?.style.width ?? "0px" );
8139
+ component.onResize = ( rect ) => {
8140
+ const realNameWidth = ( component.root.domName?.style.width ?? "0px" );
7475
8141
  container.style.width = `calc( 100% - ${ realNameWidth })`;
7476
8142
  };
7477
8143
 
7478
- const element = widget.root;
8144
+ const element = component.root;
7479
8145
 
7480
- let container, customWidgetsDom;
8146
+ let container, customComponentsDom;
7481
8147
  let defaultInstance = options.default ?? {};
7482
8148
 
7483
8149
  // Add instance button
7484
8150
 
7485
- const refresh_widget = () => {
8151
+ const _refreshComponent = () => {
7486
8152
 
7487
8153
  if( container ) container.remove();
7488
- if( customWidgetsDom ) customWidgetsDom.remove();
8154
+ if( customComponentsDom ) customComponentsDom.remove();
7489
8155
 
7490
8156
  container = document.createElement('div');
7491
8157
  container.className = "lexcustomcontainer w-full";
@@ -7495,7 +8161,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7495
8161
  const customIcon = LX.makeIcon( options.icon ?? "Box" );
7496
8162
  const menuIcon = LX.makeIcon( "Menu" );
7497
8163
 
7498
- let buttonName = customWidgetName + (!instance ? " [empty]" : "");
8164
+ let buttonName = customComponentName + (!instance ? " [empty]" : "");
7499
8165
  let buttonEl = this.addButton(null, buttonName, (value, event) => {
7500
8166
  if( instance )
7501
8167
  {
@@ -7505,9 +8171,9 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7505
8171
  else
7506
8172
  {
7507
8173
  LX.addContextMenu(null, event, c => {
7508
- c.add("New " + customWidgetName, () => {
8174
+ c.add("New " + customComponentName, () => {
7509
8175
  instance = {};
7510
- refresh_widget();
8176
+ _refreshComponent();
7511
8177
  element.querySelector(".lexcustomitems").toggleAttribute('hidden', false);
7512
8178
  element.dataset["opened"] = !element.querySelector(".lexcustomitems").hasAttribute("hidden");
7513
8179
  });
@@ -7529,7 +8195,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7529
8195
  LX.addContextMenu(null, e, c => {
7530
8196
  c.add("Clear", () => {
7531
8197
  instance = null;
7532
- refresh_widget();
8198
+ _refreshComponent();
7533
8199
  });
7534
8200
  });
7535
8201
  });
@@ -7537,14 +8203,14 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7537
8203
 
7538
8204
  // Show elements
7539
8205
 
7540
- customWidgetsDom = document.createElement('div');
7541
- customWidgetsDom.className = "lexcustomitems";
7542
- customWidgetsDom.toggleAttribute('hidden', true);
7543
- element.appendChild( customWidgetsDom );
8206
+ customComponentsDom = document.createElement('div');
8207
+ customComponentsDom.className = "lexcustomitems";
8208
+ customComponentsDom.toggleAttribute('hidden', true);
8209
+ element.appendChild( customComponentsDom );
7544
8210
 
7545
8211
  if( instance )
7546
8212
  {
7547
- this.queue( customWidgetsDom );
8213
+ this.queue( customComponentsDom );
7548
8214
 
7549
8215
  const on_instance_changed = ( key, value, event ) => {
7550
8216
  const setter = options[ `_set_${ key }` ];
@@ -7556,7 +8222,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7556
8222
  {
7557
8223
  instance[ key ] = value;
7558
8224
  }
7559
- widget._trigger( new LX.IEvent( name, instance, event ), callback );
8225
+ component._trigger( new LX.IEvent( name, instance, event ), callback );
7560
8226
  };
7561
8227
 
7562
8228
  for( let key in defaultInstance )
@@ -7621,11 +8287,11 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7621
8287
  }
7622
8288
  };
7623
8289
 
7624
- refresh_widget();
8290
+ _refreshComponent();
7625
8291
  };
7626
8292
  }
7627
8293
 
7628
- LX.ADD_CUSTOM_WIDGET = ADD_CUSTOM_WIDGET;
8294
+ LX.ADD_CUSTOM_COMPONENT = ADD_CUSTOM_COMPONENT;
7629
8295
 
7630
8296
  /**
7631
8297
  * @class NodeTree
@@ -8197,14 +8863,14 @@ LX.NodeTree = NodeTree;
8197
8863
 
8198
8864
  /**
8199
8865
  * @class Blank
8200
- * @description Blank Widget
8866
+ * @description Blank Component
8201
8867
  */
8202
8868
 
8203
- class Blank extends Widget {
8869
+ class Blank extends BaseComponent {
8204
8870
 
8205
8871
  constructor( width, height ) {
8206
8872
 
8207
- super( Widget.BLANK );
8873
+ super( BaseComponent.BLANK );
8208
8874
 
8209
8875
  this.root.style.width = width ?? "auto";
8210
8876
  this.root.style.height = height ?? "8px";
@@ -8215,17 +8881,17 @@ LX.Blank = Blank;
8215
8881
 
8216
8882
  /**
8217
8883
  * @class Title
8218
- * @description Title Widget
8884
+ * @description Title Component
8219
8885
  */
8220
8886
 
8221
- class Title extends Widget {
8887
+ class Title extends BaseComponent {
8222
8888
 
8223
8889
  constructor( name, options = {} ) {
8224
8890
 
8225
- console.assert( name, "Can't create Title Widget without text!" );
8891
+ console.assert( name, "Can't create Title Component without text!" );
8226
8892
 
8227
- // Note: Titles are not registered in Panel.widgets by now
8228
- super( Widget.TITLE, null, null, options );
8893
+ // Note: Titles are not registered in Panel.components by now
8894
+ super( BaseComponent.TITLE, null, null, options );
8229
8895
 
8230
8896
  this.root.className = `lextitle ${ this.root.className }`;
8231
8897
 
@@ -8259,14 +8925,14 @@ LX.Title = Title;
8259
8925
 
8260
8926
  /**
8261
8927
  * @class TextInput
8262
- * @description TextInput Widget
8928
+ * @description TextInput Component
8263
8929
  */
8264
8930
 
8265
- class TextInput extends Widget {
8931
+ class TextInput extends BaseComponent {
8266
8932
 
8267
8933
  constructor( name, value, callback, options = {} ) {
8268
8934
 
8269
- super( Widget.TEXT, name, String( value ), options );
8935
+ super( BaseComponent.TEXT, name, String( value ), options );
8270
8936
 
8271
8937
  this.onGetValue = () => {
8272
8938
  return value;
@@ -8399,14 +9065,14 @@ LX.TextInput = TextInput;
8399
9065
 
8400
9066
  /**
8401
9067
  * @class TextArea
8402
- * @description TextArea Widget
9068
+ * @description TextArea Component
8403
9069
  */
8404
9070
 
8405
- class TextArea extends Widget {
9071
+ class TextArea extends BaseComponent {
8406
9072
 
8407
9073
  constructor( name, value, callback, options = {} ) {
8408
9074
 
8409
- super( Widget.TEXTAREA, name, value, options );
9075
+ super( BaseComponent.TEXTAREA, name, value, options );
8410
9076
 
8411
9077
  this.onGetValue = () => {
8412
9078
  return value;
@@ -8500,14 +9166,14 @@ LX.TextArea = TextArea;
8500
9166
 
8501
9167
  /**
8502
9168
  * @class Button
8503
- * @description Button Widget
9169
+ * @description Button Component
8504
9170
  */
8505
9171
 
8506
- class Button extends Widget {
9172
+ class Button extends BaseComponent {
8507
9173
 
8508
9174
  constructor( name, value, callback, options = {} ) {
8509
9175
 
8510
- super( Widget.BUTTON, name, null, options );
9176
+ super( BaseComponent.BUTTON, name, null, options );
8511
9177
 
8512
9178
  this.onGetValue = () => {
8513
9179
  const swapInput = wValue.querySelector( "input" );
@@ -8688,6 +9354,20 @@ class Button extends Widget {
8688
9354
  {
8689
9355
  wValue.querySelector( ".file-input" ).click();
8690
9356
  }
9357
+ else if( options.mustConfirm )
9358
+ {
9359
+ new LX.PopConfirm( wValue, {
9360
+ onConfirm: () => {
9361
+ this._trigger( new LX.IEvent( name, value, e ), callback );
9362
+ },
9363
+ side: options.confirmSide,
9364
+ align: options.confirmAlign,
9365
+ confirmText: options.confirmText,
9366
+ cancelText: options.confirmCancelText,
9367
+ title: options.confirmTitle,
9368
+ content: options.confirmContent
9369
+ } );
9370
+ }
8691
9371
  else
8692
9372
  {
8693
9373
  const swapInput = wValue.querySelector( "input" );
@@ -8708,10 +9388,10 @@ LX.Button = Button;
8708
9388
 
8709
9389
  /**
8710
9390
  * @class ComboButtons
8711
- * @description ComboButtons Widget
9391
+ * @description ComboButtons Component
8712
9392
  */
8713
9393
 
8714
- class ComboButtons extends Widget {
9394
+ class ComboButtons extends BaseComponent {
8715
9395
 
8716
9396
  constructor( name, values, options = {} ) {
8717
9397
 
@@ -8826,7 +9506,7 @@ class ComboButtons extends Widget {
8826
9506
  currentValue = currentValue[ 0 ];
8827
9507
  }
8828
9508
 
8829
- super( Widget.BUTTONS, name, null, options );
9509
+ super( BaseComponent.BUTTONS, name, null, options );
8830
9510
 
8831
9511
  this.onGetValue = () => {
8832
9512
  return currentValue;
@@ -8869,16 +9549,16 @@ LX.ComboButtons = ComboButtons;
8869
9549
 
8870
9550
  /**
8871
9551
  * @class Card
8872
- * @description Card Widget
9552
+ * @description Card Component
8873
9553
  */
8874
9554
 
8875
- class Card extends Widget {
9555
+ class Card extends BaseComponent {
8876
9556
 
8877
9557
  constructor( name, options = {} ) {
8878
9558
 
8879
9559
  options.hideName = true;
8880
9560
 
8881
- super( Widget.CARD, name, null, options );
9561
+ super( BaseComponent.CARD, name, null, options );
8882
9562
 
8883
9563
  let container = document.createElement('div');
8884
9564
  container.className = "lexcard";
@@ -8932,10 +9612,10 @@ LX.Card = Card;
8932
9612
 
8933
9613
  /**
8934
9614
  * @class Form
8935
- * @description Form Widget
9615
+ * @description Form Component
8936
9616
  */
8937
9617
 
8938
- class Form extends Widget {
9618
+ class Form extends BaseComponent {
8939
9619
 
8940
9620
  constructor( name, data, callback, options = {} ) {
8941
9621
 
@@ -8948,7 +9628,7 @@ class Form extends Widget {
8948
9628
  // Always hide name for this one
8949
9629
  options.hideName = true;
8950
9630
 
8951
- super( Widget.FORM, name, null, options );
9631
+ super( BaseComponent.FORM, name, null, options );
8952
9632
 
8953
9633
  this.onGetValue = () => {
8954
9634
  return container.formData;
@@ -8956,18 +9636,18 @@ class Form extends Widget {
8956
9636
 
8957
9637
  this.onSetValue = ( newValue, skipCallback, event ) => {
8958
9638
  container.formData = newValue;
8959
- const entries = container.querySelectorAll( ".lexwidget" );
9639
+ const entries = container.querySelectorAll( ".lexcomponent" );
8960
9640
  for( let i = 0; i < entries.length; ++i )
8961
9641
  {
8962
9642
  const entry = entries[ i ];
8963
- if( entry.jsInstance.type != LX.Widget.TEXT )
9643
+ if( entry.jsInstance.type != BaseComponent.TEXT )
8964
9644
  {
8965
9645
  continue;
8966
9646
  }
8967
- let entryName = entries[ i ].querySelector( ".lexwidgetname" ).innerText;
9647
+ let entryName = entries[ i ].querySelector( ".lexcomponentname" ).innerText;
8968
9648
  let entryInput = entries[ i ].querySelector( ".lextext input" );
8969
9649
  entryInput.value = newValue[ entryName ] ?? "";
8970
- Widget._dispatchEvent( entryInput, "focusout", skipCallback );
9650
+ BaseComponent._dispatchEvent( entryInput, "focusout", skipCallback );
8971
9651
  }
8972
9652
  };
8973
9653
 
@@ -8997,10 +9677,10 @@ class Form extends Widget {
8997
9677
  container.appendChild( label.root );
8998
9678
  }
8999
9679
 
9000
- entryData.textWidget = new LX.TextInput( null, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
9680
+ entryData.textComponent = new LX.TextInput( null, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
9001
9681
  container.formData[ entry ] = value;
9002
9682
  }, entryData );
9003
- container.appendChild( entryData.textWidget.root );
9683
+ container.appendChild( entryData.textComponent.root );
9004
9684
 
9005
9685
  container.formData[ entry ] = entryData.constructor == Object ? entryData.value : entryData;
9006
9686
  }
@@ -9025,7 +9705,7 @@ class Form extends Widget {
9025
9705
  {
9026
9706
  let entryData = data[ entry ];
9027
9707
 
9028
- if( !entryData.textWidget.valid() )
9708
+ if( !entryData.textComponent.valid() )
9029
9709
  {
9030
9710
  return;
9031
9711
  }
@@ -9045,14 +9725,14 @@ LX.Form = Form;
9045
9725
 
9046
9726
  /**
9047
9727
  * @class Select
9048
- * @description Select Widget
9728
+ * @description Select Component
9049
9729
  */
9050
9730
 
9051
- class Select extends Widget {
9731
+ class Select extends BaseComponent {
9052
9732
 
9053
9733
  constructor( name, values, value, callback, options = {} ) {
9054
9734
 
9055
- super( Widget.SELECT, name, value, options );
9735
+ super( BaseComponent.SELECT, name, value, options );
9056
9736
 
9057
9737
  this.onGetValue = () => {
9058
9738
  return value;
@@ -9125,7 +9805,7 @@ class Select extends Widget {
9125
9805
  options.overflowContainerX = options.overflowContainerY = options.overflowContainer;
9126
9806
  }
9127
9807
 
9128
- const _placeOptions = ( parent ) => {
9808
+ const _placeOptions = ( parent, forceLastPlacement ) => {
9129
9809
 
9130
9810
  const selectRoot = selectedOption.root;
9131
9811
  const rect = selectRoot.getBoundingClientRect();
@@ -9155,8 +9835,8 @@ class Select extends Widget {
9155
9835
  parent.style.top = ( topPosition + selectRoot.offsetHeight ) + 'px';
9156
9836
  list.style.height = ""; // set auto height by default
9157
9837
 
9158
- const failBelow = ( topPosition + listHeight ) > maxY;
9159
- const failAbove = ( topPosition - listHeight ) < 0;
9838
+ const failAbove = forceLastPlacement ? this._lastPlacement[ 0 ] : ( topPosition - listHeight ) < 0;
9839
+ const failBelow = forceLastPlacement ? this._lastPlacement[ 1 ] : ( topPosition + listHeight ) > maxY;
9160
9840
  if( failBelow && !failAbove )
9161
9841
  {
9162
9842
  parent.style.top = ( topPosition - listHeight ) + 'px';
@@ -9167,6 +9847,8 @@ class Select extends Widget {
9167
9847
  {
9168
9848
  list.style.height = `${ maxY - topPosition - 32 }px`; // 32px margin
9169
9849
  }
9850
+
9851
+ this._lastPlacement = [ failAbove, failBelow ];
9170
9852
  }
9171
9853
 
9172
9854
  // Manage horizontal aspect
@@ -9280,7 +9962,7 @@ class Select extends Widget {
9280
9962
  {
9281
9963
  const filterOptions = LX.deepCopy( options );
9282
9964
  filterOptions.placeholder = filterOptions.placeholder ?? "Search...";
9283
- filterOptions.skipWidget = filterOptions.skipWidget ?? true;
9965
+ filterOptions.skipComponent = filterOptions.skipComponent ?? true;
9284
9966
  filterOptions.trigger = "input";
9285
9967
  filterOptions.icon = "Search";
9286
9968
  filterOptions.className = "lexfilter";
@@ -9289,6 +9971,7 @@ class Select extends Widget {
9289
9971
  filter = new LX.TextInput(null, options.filterValue ?? "", ( v ) => {
9290
9972
  const filteredOptions = this._filterOptions( values, v );
9291
9973
  list.refresh( filteredOptions );
9974
+ _placeOptions( listDialog, true );
9292
9975
  }, filterOptions );
9293
9976
  filter.root.querySelector( ".lextext" ).style.border = "1px solid transparent";
9294
9977
 
@@ -9322,7 +10005,7 @@ class Select extends Widget {
9322
10005
 
9323
10006
  let option = document.createElement( "div" );
9324
10007
  option.className = "option";
9325
- option.innerHTML = iValue;
10008
+ option.innerHTML = ( LX.makeIcon( "Inbox", { svgClass: "mr-2" } ).innerHTML + iValue );
9326
10009
 
9327
10010
  let li = document.createElement( "li" );
9328
10011
  li.className = "lexselectitem empty";
@@ -9431,7 +10114,7 @@ class Select extends Widget {
9431
10114
  const emptyFilter = !value.length;
9432
10115
  let filteredOptions = [];
9433
10116
 
9434
- // Add widgets
10117
+ // Add components
9435
10118
  for( let i = 0; i < options.length; i++ )
9436
10119
  {
9437
10120
  let o = options[ i ];
@@ -9454,16 +10137,16 @@ LX.Select = Select;
9454
10137
 
9455
10138
  /**
9456
10139
  * @class Curve
9457
- * @description Curve Widget
10140
+ * @description Curve Component
9458
10141
  */
9459
10142
 
9460
- class Curve extends Widget {
10143
+ class Curve extends BaseComponent {
9461
10144
 
9462
10145
  constructor( name, values, callback, options = {} ) {
9463
10146
 
9464
10147
  let defaultValues = JSON.parse( JSON.stringify( values ) );
9465
10148
 
9466
- super( Widget.CURVE, name, defaultValues, options );
10149
+ super( BaseComponent.CURVE, name, defaultValues, options );
9467
10150
 
9468
10151
  this.onGetValue = () => {
9469
10152
  return JSON.parse(JSON.stringify( curveInstance.element.value ));
@@ -9515,16 +10198,16 @@ LX.Curve = Curve;
9515
10198
 
9516
10199
  /**
9517
10200
  * @class Dial
9518
- * @description Dial Widget
10201
+ * @description Dial Component
9519
10202
  */
9520
10203
 
9521
- class Dial extends Widget {
10204
+ class Dial extends BaseComponent {
9522
10205
 
9523
10206
  constructor( name, values, callback, options = {} ) {
9524
10207
 
9525
10208
  let defaultValues = JSON.parse( JSON.stringify( values ) );
9526
10209
 
9527
- super( Widget.DIAL, name, defaultValues, options );
10210
+ super( BaseComponent.DIAL, name, defaultValues, options );
9528
10211
 
9529
10212
  this.onGetValue = () => {
9530
10213
  return JSON.parse( JSON.stringify( dialInstance.element.value ) );
@@ -9572,14 +10255,14 @@ LX.Dial = Dial;
9572
10255
 
9573
10256
  /**
9574
10257
  * @class Layers
9575
- * @description Layers Widget
10258
+ * @description Layers Component
9576
10259
  */
9577
10260
 
9578
- class Layers extends Widget {
10261
+ class Layers extends BaseComponent {
9579
10262
 
9580
10263
  constructor( name, value, callback, options = {} ) {
9581
10264
 
9582
- super( Widget.LAYERS, name, value, options );
10265
+ super( BaseComponent.LAYERS, name, value, options );
9583
10266
 
9584
10267
  this.onGetValue = () => {
9585
10268
  return value;
@@ -9656,16 +10339,16 @@ LX.Layers = Layers;
9656
10339
 
9657
10340
  /**
9658
10341
  * @class ItemArray
9659
- * @description ItemArray Widget
10342
+ * @description ItemArray Component
9660
10343
  */
9661
10344
 
9662
- class ItemArray extends Widget {
10345
+ class ItemArray extends BaseComponent {
9663
10346
 
9664
10347
  constructor( name, values = [], callback, options = {} ) {
9665
10348
 
9666
10349
  options.nameWidth = "100%";
9667
10350
 
9668
- super( Widget.ARRAY, name, null, options );
10351
+ super( BaseComponent.ARRAY, name, null, options );
9669
10352
 
9670
10353
  this.onGetValue = () => {
9671
10354
  return values;
@@ -9720,41 +10403,41 @@ class ItemArray extends Widget {
9720
10403
  {
9721
10404
  const value = values[ i ];
9722
10405
  let baseclass = options.innerValues ? 'select' : value.constructor;
9723
- let widget = null;
10406
+ let component = null;
9724
10407
 
9725
10408
  switch( baseclass )
9726
10409
  {
9727
10410
  case String:
9728
- widget = new LX.TextInput(i + "", value, function(value, event) {
10411
+ component = new LX.TextInput(i + "", value, function(value, event) {
9729
10412
  values[ i ] = value;
9730
10413
  callback( values );
9731
10414
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9732
10415
  break;
9733
10416
  case Number:
9734
- widget = new NumberInput(i + "", value, function(value, event) {
10417
+ component = new NumberInput(i + "", value, function(value, event) {
9735
10418
  values[ i ] = value;
9736
10419
  callback( values );
9737
10420
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9738
10421
  break;
9739
10422
  case 'select':
9740
- widget = new Select(i + "", options.innerValues, value, function(value, event) {
10423
+ component = new Select(i + "", options.innerValues, value, function(value, event) {
9741
10424
  values[ i ] = value;
9742
10425
  callback( values );
9743
10426
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9744
10427
  break;
9745
10428
  }
9746
10429
 
9747
- console.assert( widget, `Value of type ${ baseclass } cannot be modified in ItemArray` );
10430
+ console.assert( component, `Value of type ${ baseclass } cannot be modified in ItemArray` );
9748
10431
 
9749
- arrayItems.appendChild( widget.root );
10432
+ arrayItems.appendChild( component.root );
9750
10433
 
9751
- const removeWidget = new LX.Button( null, "", ( v, event) => {
10434
+ const removeComponent = new LX.Button( null, "", ( v, event) => {
9752
10435
  values.splice( values.indexOf( value ), 1 );
9753
10436
  this._updateItems();
9754
10437
  this._trigger( new LX.IEvent(name, values, event), callback );
9755
10438
  }, { title: "Remove item", icon: "Trash3"} );
9756
10439
 
9757
- widget.root.appendChild( removeWidget.root );
10440
+ component.root.appendChild( removeComponent.root );
9758
10441
  }
9759
10442
 
9760
10443
  const addButton = new LX.Button(null, LX.makeIcon( "Plus", { svgClass: "sm" } ).innerHTML + "Add item", (v, event) => {
@@ -9774,14 +10457,14 @@ LX.ItemArray = ItemArray;
9774
10457
 
9775
10458
  /**
9776
10459
  * @class List
9777
- * @description List Widget
10460
+ * @description List Component
9778
10461
  */
9779
10462
 
9780
- class List extends Widget {
10463
+ class List extends BaseComponent {
9781
10464
 
9782
10465
  constructor( name, values, value, callback, options = {} ) {
9783
10466
 
9784
- super( Widget.LIST, name, value, options );
10467
+ super( BaseComponent.LIST, name, value, options );
9785
10468
 
9786
10469
  this.onGetValue = () => {
9787
10470
  return value;
@@ -9874,17 +10557,17 @@ LX.List = List;
9874
10557
 
9875
10558
  /**
9876
10559
  * @class Tags
9877
- * @description Tags Widget
10560
+ * @description Tags Component
9878
10561
  */
9879
10562
 
9880
- class Tags extends Widget {
10563
+ class Tags extends BaseComponent {
9881
10564
 
9882
10565
  constructor( name, value, callback, options = {} ) {
9883
10566
 
9884
10567
  value = value.replace( /\s/g, '' ).split( ',' );
9885
10568
 
9886
10569
  let defaultValue = [].concat( value );
9887
- super( Widget.TAGS, name, defaultValue, options );
10570
+ super( BaseComponent.TAGS, name, defaultValue, options );
9888
10571
 
9889
10572
  this.onGetValue = () => {
9890
10573
  return [].concat( value );
@@ -9963,19 +10646,19 @@ LX.Tags = Tags;
9963
10646
 
9964
10647
  /**
9965
10648
  * @class Checkbox
9966
- * @description Checkbox Widget
10649
+ * @description Checkbox Component
9967
10650
  */
9968
10651
 
9969
- class Checkbox extends Widget {
10652
+ class Checkbox extends BaseComponent {
9970
10653
 
9971
10654
  constructor( name, value, callback, options = {} ) {
9972
10655
 
9973
10656
  if( !name && !options.label )
9974
10657
  {
9975
- throw( "Set Widget Name or at least a label!" );
10658
+ throw( "Set Component Name or at least a label!" );
9976
10659
  }
9977
10660
 
9978
- super( Widget.CHECKBOX, name, value, options );
10661
+ super( BaseComponent.CHECKBOX, name, value, options );
9979
10662
 
9980
10663
  this.onGetValue = () => {
9981
10664
  return value;
@@ -10046,19 +10729,19 @@ LX.Checkbox = Checkbox;
10046
10729
 
10047
10730
  /**
10048
10731
  * @class Toggle
10049
- * @description Toggle Widget
10732
+ * @description Toggle Component
10050
10733
  */
10051
10734
 
10052
- class Toggle extends Widget {
10735
+ class Toggle extends BaseComponent {
10053
10736
 
10054
10737
  constructor( name, value, callback, options = {} ) {
10055
10738
 
10056
10739
  if( !name && !options.label )
10057
10740
  {
10058
- throw( "Set Widget Name or at least a label!" );
10741
+ throw( "Set Component Name or at least a label!" );
10059
10742
  }
10060
10743
 
10061
- super( Widget.TOGGLE, name, value, options );
10744
+ super( BaseComponent.TOGGLE, name, value, options );
10062
10745
 
10063
10746
  this.onGetValue = () => {
10064
10747
  return toggle.checked;
@@ -10130,14 +10813,14 @@ LX.Toggle = Toggle;
10130
10813
 
10131
10814
  /**
10132
10815
  * @class RadioGroup
10133
- * @description RadioGroup Widget
10816
+ * @description RadioGroup Component
10134
10817
  */
10135
10818
 
10136
- class RadioGroup extends Widget {
10819
+ class RadioGroup extends BaseComponent {
10137
10820
 
10138
10821
  constructor( name, label, values, callback, options = {} ) {
10139
10822
 
10140
- super( Widget.RADIO, name, null, options );
10823
+ super( BaseComponent.RADIO, name, null, options );
10141
10824
 
10142
10825
  let currentIndex = null;
10143
10826
 
@@ -10209,10 +10892,10 @@ LX.RadioGroup = RadioGroup;
10209
10892
 
10210
10893
  /**
10211
10894
  * @class ColorInput
10212
- * @description ColorInput Widget
10895
+ * @description ColorInput Component
10213
10896
  */
10214
10897
 
10215
- class ColorInput extends Widget {
10898
+ class ColorInput extends BaseComponent {
10216
10899
 
10217
10900
  constructor( name, value, callback, options = {} ) {
10218
10901
 
@@ -10221,12 +10904,12 @@ class ColorInput extends Widget {
10221
10904
  const useAlpha = options.useAlpha ??
10222
10905
  ( ( value.constructor === Object && 'a' in value ) || ( value.constructor === String && [ 5, 9 ].includes( value.length ) ) );
10223
10906
 
10224
- const widgetColor = new LX.Color( value );
10907
+ const componentColor = new LX.Color( value );
10225
10908
 
10226
10909
  // Force always hex internally
10227
- value = useAlpha ? widgetColor.hex : widgetColor.hex.substr( 0, 7 );
10910
+ value = useAlpha ? componentColor.hex : componentColor.hex.substr( 0, 7 );
10228
10911
 
10229
- super( Widget.COLOR, name, value, options );
10912
+ super( BaseComponent.COLOR, name, value, options );
10230
10913
 
10231
10914
  this.onGetValue = () => {
10232
10915
  const currentColor = new LX.Color( value );
@@ -10246,7 +10929,7 @@ class ColorInput extends Widget {
10246
10929
 
10247
10930
  if( !this._skipTextUpdate )
10248
10931
  {
10249
- textWidget.set( value, true, event );
10932
+ textComponent.set( value, true, event );
10250
10933
  }
10251
10934
 
10252
10935
  if( !skipCallback )
@@ -10314,15 +10997,15 @@ class ColorInput extends Widget {
10314
10997
  colorSampleRGB.style.width = "18px";
10315
10998
  }
10316
10999
 
10317
- const textWidget = new LX.TextInput( null, value, v => {
11000
+ const textComponent = new LX.TextInput( null, value, v => {
10318
11001
  this._skipTextUpdate = true;
10319
11002
  this.set( v );
10320
11003
  delete this._skipTextUpdate;
10321
11004
  this.picker.fromHexColor( v );
10322
11005
  }, { width: "calc( 100% - 24px )", disabled: options.disabled });
10323
11006
 
10324
- textWidget.root.style.marginLeft = "6px";
10325
- container.appendChild( textWidget.root );
11007
+ textComponent.root.style.marginLeft = "6px";
11008
+ container.appendChild( textComponent.root );
10326
11009
 
10327
11010
  LX.doAsync( this.onResize.bind( this ) );
10328
11011
  }
@@ -10332,14 +11015,14 @@ LX.ColorInput = ColorInput;
10332
11015
 
10333
11016
  /**
10334
11017
  * @class RangeInput
10335
- * @description RangeInput Widget
11018
+ * @description RangeInput Component
10336
11019
  */
10337
11020
 
10338
- class RangeInput extends Widget {
11021
+ class RangeInput extends BaseComponent {
10339
11022
 
10340
11023
  constructor( name, value, callback, options = {} ) {
10341
11024
 
10342
- super( Widget.RANGE, name, value, options );
11025
+ super( BaseComponent.RANGE, name, value, options );
10343
11026
 
10344
11027
  this.onGetValue = () => {
10345
11028
  return value;
@@ -10423,7 +11106,7 @@ class RangeInput extends Widget {
10423
11106
  slider.min = newMin ?? slider.min;
10424
11107
  slider.max = newMax ?? slider.max;
10425
11108
  slider.step = newStep ?? slider.step;
10426
- Widget._dispatchEvent( slider, "input", true );
11109
+ BaseComponent._dispatchEvent( slider, "input", true );
10427
11110
  };
10428
11111
 
10429
11112
  LX.doAsync( this.onResize.bind( this ) );
@@ -10434,14 +11117,14 @@ LX.RangeInput = RangeInput;
10434
11117
 
10435
11118
  /**
10436
11119
  * @class NumberInput
10437
- * @description NumberInput Widget
11120
+ * @description NumberInput Component
10438
11121
  */
10439
11122
 
10440
- class NumberInput extends Widget {
11123
+ class NumberInput extends BaseComponent {
10441
11124
 
10442
11125
  constructor( name, value, callback, options = {} ) {
10443
11126
 
10444
- super( Widget.NUMBER, name, value, options );
11127
+ super( BaseComponent.NUMBER, name, value, options );
10445
11128
 
10446
11129
  this.onGetValue = () => {
10447
11130
  return value;
@@ -10518,7 +11201,7 @@ class NumberInput extends Widget {
10518
11201
  // Add slider below
10519
11202
  if( !options.skipSlider && options.min !== undefined && options.max !== undefined )
10520
11203
  {
10521
- let sliderBox = LX.makeContainer( [ "100%", "auto" ], "", "", box );
11204
+ let sliderBox = LX.makeContainer( [ "100%", "auto" ], "z-1 input-box", "", box );
10522
11205
  let slider = document.createElement( 'input' );
10523
11206
  slider.className = "lexinputslider";
10524
11207
  slider.min = options.min;
@@ -10656,17 +11339,17 @@ LX.NumberInput = NumberInput;
10656
11339
 
10657
11340
  /**
10658
11341
  * @class Vector
10659
- * @description Vector Widget
11342
+ * @description Vector Component
10660
11343
  */
10661
11344
 
10662
- class Vector extends Widget {
11345
+ class Vector extends BaseComponent {
10663
11346
 
10664
11347
  constructor( numComponents, name, value, callback, options = {} ) {
10665
11348
 
10666
11349
  numComponents = LX.clamp( numComponents, 2, 4 );
10667
11350
  value = value ?? new Array( numComponents ).fill( 0 );
10668
11351
 
10669
- super( Widget.VECTOR, name, [].concat( value ), options );
11352
+ super( BaseComponent.VECTOR, name, [].concat( value ), options );
10670
11353
 
10671
11354
  this.onGetValue = () => {
10672
11355
  let inputs = this.root.querySelectorAll( "input[type='number']" );
@@ -10761,13 +11444,13 @@ class Vector extends Widget {
10761
11444
  for( let v of that.querySelectorAll(".vecinput") )
10762
11445
  {
10763
11446
  v.value = LX.round( +v.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ), options.precision );
10764
- Widget._dispatchEvent( v, "change" );
11447
+ BaseComponent._dispatchEvent( v, "change" );
10765
11448
  }
10766
11449
  }
10767
11450
  else
10768
11451
  {
10769
11452
  this.value = LX.round( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ), options.precision );
10770
- Widget._dispatchEvent( vecinput, "change" );
11453
+ BaseComponent._dispatchEvent( vecinput, "change" );
10771
11454
  }
10772
11455
  }, { passive: false } );
10773
11456
 
@@ -10841,13 +11524,13 @@ class Vector extends Widget {
10841
11524
  for( let v of that.root.querySelectorAll( ".vecinput" ) )
10842
11525
  {
10843
11526
  v.value = LX.round( +v.valueAsNumber + mult * dt, options.precision );
10844
- Widget._dispatchEvent( v, "change" );
11527
+ BaseComponent._dispatchEvent( v, "change" );
10845
11528
  }
10846
11529
  }
10847
11530
  else
10848
11531
  {
10849
11532
  vecinput.value = LX.round( +vecinput.valueAsNumber + mult * dt, options.precision );
10850
- Widget._dispatchEvent( vecinput, "change" );
11533
+ BaseComponent._dispatchEvent( vecinput, "change" );
10851
11534
  }
10852
11535
  }
10853
11536
 
@@ -10906,14 +11589,14 @@ LX.Vector = Vector;
10906
11589
 
10907
11590
  /**
10908
11591
  * @class SizeInput
10909
- * @description SizeInput Widget
11592
+ * @description SizeInput Component
10910
11593
  */
10911
11594
 
10912
- class SizeInput extends Widget {
11595
+ class SizeInput extends BaseComponent {
10913
11596
 
10914
11597
  constructor( name, value, callback, options = {} ) {
10915
11598
 
10916
- super( Widget.SIZE, name, value, options );
11599
+ super( BaseComponent.SIZE, name, value, options );
10917
11600
 
10918
11601
  this.onGetValue = () => {
10919
11602
  const value = [];
@@ -10994,10 +11677,10 @@ LX.SizeInput = SizeInput;
10994
11677
 
10995
11678
  /**
10996
11679
  * @class OTPInput
10997
- * @description OTPInput Widget
11680
+ * @description OTPInput Component
10998
11681
  */
10999
11682
 
11000
- class OTPInput extends Widget {
11683
+ class OTPInput extends BaseComponent {
11001
11684
 
11002
11685
  constructor( name, value, callback, options = {} ) {
11003
11686
 
@@ -11010,7 +11693,7 @@ class OTPInput extends Widget {
11010
11693
  value = "x".repeat( patternSize );
11011
11694
  }
11012
11695
 
11013
- super( Widget.OTP, name, value, options );
11696
+ super( BaseComponent.OTP, name, value, options );
11014
11697
 
11015
11698
  this.onGetValue = () => {
11016
11699
  return +value;
@@ -11152,14 +11835,14 @@ LX.OTPInput = OTPInput;
11152
11835
 
11153
11836
  /**
11154
11837
  * @class Pad
11155
- * @description Pad Widget
11838
+ * @description Pad Component
11156
11839
  */
11157
11840
 
11158
- class Pad extends Widget {
11841
+ class Pad extends BaseComponent {
11159
11842
 
11160
11843
  constructor( name, value, callback, options = {} ) {
11161
11844
 
11162
- super( Widget.PAD, name, null, options );
11845
+ super( BaseComponent.PAD, name, null, options );
11163
11846
 
11164
11847
  this.onGetValue = () => {
11165
11848
  return thumb.value.xy;
@@ -11272,14 +11955,14 @@ LX.Pad = Pad;
11272
11955
 
11273
11956
  /**
11274
11957
  * @class Progress
11275
- * @description Progress Widget
11958
+ * @description Progress Component
11276
11959
  */
11277
11960
 
11278
- class Progress extends Widget {
11961
+ class Progress extends BaseComponent {
11279
11962
 
11280
11963
  constructor( name, value, options = {} ) {
11281
11964
 
11282
- super( Widget.PROGRESS, name, value, options );
11965
+ super( BaseComponent.PROGRESS, name, value, options );
11283
11966
 
11284
11967
  this.onGetValue = () => {
11285
11968
  return progress.value;
@@ -11409,14 +12092,14 @@ LX.Progress = Progress;
11409
12092
 
11410
12093
  /**
11411
12094
  * @class FileInput
11412
- * @description FileInput Widget
12095
+ * @description FileInput Component
11413
12096
  */
11414
12097
 
11415
- class FileInput extends Widget {
12098
+ class FileInput extends BaseComponent {
11416
12099
 
11417
12100
  constructor( name, callback, options = { } ) {
11418
12101
 
11419
- super( Widget.FILE, name, null, options );
12102
+ super( BaseComponent.FILE, name, null, options );
11420
12103
 
11421
12104
  let local = options.local ?? true;
11422
12105
  let type = options.type ?? 'text';
@@ -11494,16 +12177,16 @@ LX.FileInput = FileInput;
11494
12177
 
11495
12178
  /**
11496
12179
  * @class Tree
11497
- * @description Tree Widget
12180
+ * @description Tree Component
11498
12181
  */
11499
12182
 
11500
- class Tree extends Widget {
12183
+ class Tree extends BaseComponent {
11501
12184
 
11502
12185
  constructor( name, data, options = {} ) {
11503
12186
 
11504
12187
  options.hideName = true;
11505
12188
 
11506
- super( Widget.TREE, name, null, options );
12189
+ super( BaseComponent.TREE, name, null, options );
11507
12190
 
11508
12191
  let container = document.createElement('div');
11509
12192
  container.className = "lextree";
@@ -11576,16 +12259,16 @@ LX.Tree = Tree;
11576
12259
 
11577
12260
  /**
11578
12261
  * @class TabSections
11579
- * @description TabSections Widget
12262
+ * @description TabSections Component
11580
12263
  */
11581
12264
 
11582
- class TabSections extends Widget {
12265
+ class TabSections extends BaseComponent {
11583
12266
 
11584
12267
  constructor( name, tabs, options = {} ) {
11585
12268
 
11586
12269
  options.hideName = true;
11587
12270
 
11588
- super( Widget.TABS, name, null, options );
12271
+ super( BaseComponent.TABS, name, null, options );
11589
12272
 
11590
12273
  if( tabs.constructor != Array )
11591
12274
  {
@@ -11631,7 +12314,7 @@ class TabSections extends Widget {
11631
12314
 
11632
12315
  let infoContainer = document.createElement( "div" );
11633
12316
  infoContainer.id = tab.name.replace( /\s/g, '' );
11634
- infoContainer.className = "widgets";
12317
+ infoContainer.className = "components";
11635
12318
  infoContainer.toggleAttribute( "hidden", !( tab.selected ?? false ) );
11636
12319
  container.appendChild( infoContainer );
11637
12320
 
@@ -11640,7 +12323,7 @@ class TabSections extends Widget {
11640
12323
  tabContainer.querySelectorAll( ".lextab" ).forEach( e => { e.classList.remove( "selected" ); } );
11641
12324
  tabEl.classList.add( "selected" );
11642
12325
  // Hide all tabs content
11643
- container.querySelectorAll(".widgets").forEach( e => { e.toggleAttribute( "hidden", true ); } );
12326
+ container.querySelectorAll(".components").forEach( e => { e.toggleAttribute( "hidden", true ); } );
11644
12327
  // Show tab content
11645
12328
  const el = container.querySelector( '#' + infoContainer.id );
11646
12329
  el.toggleAttribute( "hidden" );
@@ -11683,14 +12366,14 @@ LX.TabSections = TabSections;
11683
12366
 
11684
12367
  /**
11685
12368
  * @class Counter
11686
- * @description Counter Widget
12369
+ * @description Counter Component
11687
12370
  */
11688
12371
 
11689
- class Counter extends Widget {
12372
+ class Counter extends BaseComponent {
11690
12373
 
11691
12374
  constructor( name, value, callback, options = { } ) {
11692
12375
 
11693
- super( Widget.COUNTER, name, value, options );
12376
+ super( BaseComponent.COUNTER, name, value, options );
11694
12377
 
11695
12378
  this.onGetValue = () => {
11696
12379
  return counterText.count;
@@ -11753,10 +12436,10 @@ LX.Counter = Counter;
11753
12436
 
11754
12437
  /**
11755
12438
  * @class Table
11756
- * @description Table Widget
12439
+ * @description Table Component
11757
12440
  */
11758
12441
 
11759
- class Table extends Widget {
12442
+ class Table extends BaseComponent {
11760
12443
 
11761
12444
  constructor( name, data, options = { } ) {
11762
12445
 
@@ -11765,7 +12448,7 @@ class Table extends Widget {
11765
12448
  throw( "Data is needed to create a table!" );
11766
12449
  }
11767
12450
 
11768
- super( Widget.TABLE, name, null, options );
12451
+ super( BaseComponent.TABLE, name, null, options );
11769
12452
 
11770
12453
  this.onResize = ( rect ) => {
11771
12454
  const realNameWidth = ( this.root.domName?.style.width ?? "0px" );
@@ -11815,7 +12498,7 @@ class Table extends Widget {
11815
12498
  {
11816
12499
  const filterOptions = LX.deepCopy( options );
11817
12500
  filterOptions.placeholder = `Filter ${ this.filter }...`;
11818
- filterOptions.skipWidget = true;
12501
+ filterOptions.skipComponent = true;
11819
12502
  filterOptions.trigger = "input";
11820
12503
  filterOptions.inputClass = "outline";
11821
12504
 
@@ -11834,9 +12517,9 @@ class Table extends Widget {
11834
12517
 
11835
12518
  for( let f of this.customFilters )
11836
12519
  {
11837
- f.widget = new LX.Button(null, icon.innerHTML + f.name, ( v ) => {
12520
+ f.component = new LX.Button(null, icon.innerHTML + f.name, ( v ) => {
11838
12521
 
11839
- const spanName = f.widget.root.querySelector( "span" );
12522
+ const spanName = f.component.root.querySelector( "span" );
11840
12523
 
11841
12524
  if( f.options )
11842
12525
  {
@@ -11857,7 +12540,7 @@ class Table extends Widget {
11857
12540
  };
11858
12541
  return item;
11859
12542
  } );
11860
- new LX.DropdownMenu( f.widget.root, menuOptions, { side: "bottom", align: "start" });
12543
+ new LX.DropdownMenu( f.component.root, menuOptions, { side: "bottom", align: "start" });
11861
12544
  }
11862
12545
  else if( f.type == "range" )
11863
12546
  {
@@ -11876,12 +12559,14 @@ class Table extends Widget {
11876
12559
  f.start = v;
11877
12560
  const inUse = ( f.start != f.min || f.end != f.max );
11878
12561
  spanName.innerHTML = icon.innerHTML + f.name + ( inUse ? separatorHtml + LX.badge( `${ f.start } - ${ f.end } ${ f.units ?? "" }`, "bg-tertiary fg-secondary text-sm border-0" ) : "" );
12562
+ if( inUse ) this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
11879
12563
  this.refresh();
11880
12564
  }, { skipSlider: true, min: f.min, max: f.max, step: f.step, units: f.units } );
11881
12565
  panel.addNumber( null, f.end, (v) => {
11882
12566
  f.end = v;
11883
12567
  const inUse = ( f.start != f.min || f.end != f.max );
11884
12568
  spanName.innerHTML = icon.innerHTML + f.name + ( inUse ? separatorHtml + LX.badge( `${ f.start } - ${ f.end } ${ f.units ?? "" }`, "bg-tertiary fg-secondary text-sm border-0" ) : "" );
12569
+ if( inUse ) this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
11885
12570
  this.refresh();
11886
12571
  }, { skipSlider: true, min: f.min, max: f.max, step: f.step, units: f.units } );
11887
12572
  panel.addButton( null, "Reset", () => {
@@ -11894,11 +12579,43 @@ class Table extends Widget {
11894
12579
  };
11895
12580
  panel.refresh();
11896
12581
  container.appendChild( panel.root );
11897
- new LX.Popover( f.widget.root, [ container ], { side: "bottom" } );
12582
+ new LX.Popover( f.component.root, [ container ], { side: "bottom" } );
12583
+ }
12584
+ else if( f.type == "date" )
12585
+ {
12586
+ const container = LX.makeContainer( ["auto", "auto"], "text-md" );
12587
+ const panel = new LX.Panel();
12588
+ LX.makeContainer( ["100%", "auto"], "px-3 p-2 pb-0 text-md font-medium", f.name, container );
12589
+
12590
+ panel.refresh = () => {
12591
+ panel.clear();
12592
+
12593
+ // Generate default value once the filter is used
12594
+ if( !f.default )
12595
+ {
12596
+ const date = new Date();
12597
+ const todayStringDate = `${ date.getDate() }/${ date.getMonth() + 1 }/${ date.getFullYear() }`;
12598
+ f.default = [ todayStringDate, todayStringDate ];
12599
+ }
12600
+
12601
+ const calendar = new LX.CalendarRange( f.value, {
12602
+ onChange: ( dateRange ) => {
12603
+ f.value = dateRange;
12604
+ spanName.innerHTML = icon.innerHTML + f.name + ( separatorHtml + LX.badge( `${ calendar.getFullDate() }`, "bg-tertiary fg-secondary text-sm border-0" ) );
12605
+ this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
12606
+ this.refresh();
12607
+ }
12608
+ });
12609
+
12610
+ panel.attach( calendar );
12611
+ };
12612
+ panel.refresh();
12613
+ container.appendChild( panel.root );
12614
+ new LX.Popover( f.component.root, [ container ], { side: "bottom" } );
11898
12615
  }
11899
12616
 
11900
12617
  }, { buttonClass: "px-2 primary dashed" } );
11901
- headerContainer.appendChild( f.widget.root );
12618
+ headerContainer.appendChild( f.component.root );
11902
12619
  }
11903
12620
 
11904
12621
  this._resetCustomFiltersBtn = new LX.Button(null, "resetButton", ( v ) => {
@@ -11906,12 +12623,16 @@ class Table extends Widget {
11906
12623
  this._resetCustomFiltersBtn.root.classList.add( "hidden" );
11907
12624
  for( let f of this.customFilters )
11908
12625
  {
11909
- f.widget.root.querySelector( "span" ).innerHTML = ( icon.innerHTML + f.name );
12626
+ f.component.root.querySelector( "span" ).innerHTML = ( icon.innerHTML + f.name );
11910
12627
  if( f.type == "range" )
11911
12628
  {
11912
12629
  f.start = f.min;
11913
12630
  f.end = f.max;
11914
12631
  }
12632
+ else if( f.type == "date" )
12633
+ {
12634
+ delete f.default;
12635
+ }
11915
12636
  }
11916
12637
  this.refresh();
11917
12638
  }, { title: "Reset filters", tooltip: true, icon: "X" } );
@@ -12193,7 +12914,30 @@ class Table extends Widget {
12193
12914
  }
12194
12915
  }
12195
12916
  else if( f.type == "date" )
12196
- ;
12917
+ {
12918
+ acfMap[ acfName ] = acfMap[ acfName ] ?? false;
12919
+
12920
+ const filterColIndex = data.head.indexOf( acfName );
12921
+ if( filterColIndex > -1 )
12922
+ {
12923
+ if( !f.default )
12924
+ {
12925
+ const date = new Date();
12926
+ const todayStringDate = `${ date.getDate() }/${ date.getMonth() + 1 }/${ date.getFullYear() }`;
12927
+ f.value = [ todayStringDate, todayStringDate ];
12928
+ acfMap[ acfName ] |= true;
12929
+ continue;
12930
+ }
12931
+
12932
+ f.value = f.value ?? f.default;
12933
+
12934
+ const dateString = bodyData[ filterColIndex ];
12935
+ const date = LX.dateFromDateString( dateString );
12936
+ const minDate = LX.dateFromDateString( f.value[ 0 ] );
12937
+ const maxDate = LX.dateFromDateString( f.value[ 1 ] );
12938
+ acfMap[ acfName ] |= ( date >= minDate ) && ( date <= maxDate );
12939
+ }
12940
+ }
12197
12941
  }
12198
12942
 
12199
12943
  const show = Object.values( acfMap ).reduce( ( e, acc ) => acc *= e, true );
@@ -12438,30 +13182,35 @@ LX.Table = Table;
12438
13182
 
12439
13183
  /**
12440
13184
  * @class DatePicker
12441
- * @description DatePicker Widget
13185
+ * @description DatePicker Component
12442
13186
  */
12443
13187
 
12444
- class DatePicker extends Widget {
13188
+ class DatePicker extends BaseComponent {
13189
+
13190
+ constructor( name, dateValue, callback, options = { } ) {
12445
13191
 
12446
- constructor( name, dateString, callback, options = { } ) {
13192
+ super( BaseComponent.DATE, name, null, options );
12447
13193
 
12448
- super( Widget.DATE, name, null, options );
13194
+ const dateAsRange = ( dateValue?.constructor === Array );
12449
13195
 
12450
- if( options.today )
13196
+ if( !dateAsRange && options.today )
12451
13197
  {
12452
13198
  const date = new Date();
12453
- dateString = `${ date.getDate() }/${ date.getMonth() + 1 }/${ date.getFullYear() }`;
13199
+ dateValue = `${ date.getDate() }/${ date.getMonth() + 1 }/${ date.getFullYear() }`;
12454
13200
  }
12455
13201
 
12456
13202
  this.onGetValue = () => {
12457
- return dateString;
13203
+ return dateValue;
12458
13204
  };
12459
13205
 
12460
13206
  this.onSetValue = ( newValue, skipCallback, event ) => {
12461
13207
 
12462
- dateString = newValue;
13208
+ if( !dateAsRange )
13209
+ {
13210
+ this.calendar.fromDateString( newValue );
13211
+ }
12463
13212
 
12464
- this.calendar.fromDateString( newValue );
13213
+ dateValue = newValue;
12465
13214
 
12466
13215
  refresh( this.calendar.getFullDate() );
12467
13216
 
@@ -12476,26 +13225,72 @@ class DatePicker extends Widget {
12476
13225
  container.style.width = `calc( 100% - ${ realNameWidth })`;
12477
13226
  };
12478
13227
 
12479
- const container = document.createElement('div');
12480
- container.className = "lexdate";
13228
+ const container = LX.makeContainer( [ "auto", "auto" ], "lexdate flex flex-row" );
12481
13229
  this.root.appendChild( container );
12482
13230
 
12483
- this.calendar = new LX.Calendar( dateString, { onChange: ( date ) => {
12484
- this.set( `${ date.day }/${ date.month }/${ date.year }` );
12485
- }, ...options });
13231
+ if( !dateAsRange )
13232
+ {
13233
+ this.calendar = new LX.Calendar( dateValue, {
13234
+ onChange: ( date ) => {
13235
+ const newDateString = `${ date.day }/${ date.month }/${ date.year }`;
13236
+ this.set( newDateString );
13237
+ },
13238
+ ...options
13239
+ });
13240
+ }
13241
+ else
13242
+ {
13243
+ this.calendar = new LX.CalendarRange( dateValue, {
13244
+ onChange: ( dateRange ) => {
13245
+ this.set( dateRange );
13246
+ },
13247
+ ...options
13248
+ });
13249
+ }
12486
13250
 
12487
13251
  const refresh = ( currentDate ) => {
13252
+
13253
+ const emptyDate = !!currentDate;
13254
+
12488
13255
  container.innerHTML = "";
13256
+
13257
+ currentDate = currentDate ?? "Pick a date";
13258
+
13259
+ const dts = currentDate.split( " to " );
13260
+ const d0 = dateAsRange ? dts[ 0 ] : currentDate;
13261
+
12489
13262
  const calendarIcon = LX.makeIcon( "Calendar" );
12490
- const calendarButton = new LX.Button( null, currentDate ?? "Pick a date", () => {
13263
+ const calendarButton = new LX.Button( null, d0, () => {
12491
13264
  this._popover = new LX.Popover( calendarButton.root, [ this.calendar ] );
12492
- }, { buttonClass: `flex flex-row px-3 ${ currentDate ? "" : "fg-tertiary" } justify-between` } );
12493
-
13265
+ }, { buttonClass: `flex flex-row px-3 ${ emptyDate ? "" : "fg-tertiary" } justify-between` } );
12494
13266
  calendarButton.root.querySelector( "button" ).appendChild( calendarIcon );
13267
+ calendarButton.root.style.width = "100%";
12495
13268
  container.appendChild( calendarButton.root );
13269
+
13270
+ if( dateAsRange )
13271
+ {
13272
+ const arrowRightIcon = LX.makeIcon( "ArrowRight" );
13273
+ LX.makeContainer( ["32px", "auto"], "content-center", arrowRightIcon.innerHTML, container );
13274
+
13275
+ const d1 = dts[ 1 ];
13276
+ const calendarIcon = LX.makeIcon( "Calendar" );
13277
+ const calendarButton = new LX.Button( null, d1, () => {
13278
+ this._popover = new LX.Popover( calendarButton.root, [ this.calendar ] );
13279
+ }, { buttonClass: `flex flex-row px-3 ${ emptyDate ? "" : "fg-tertiary" } justify-between` } );
13280
+ calendarButton.root.querySelector( "button" ).appendChild( calendarIcon );
13281
+ calendarButton.root.style.width = "100%";
13282
+ container.appendChild( calendarButton.root );
13283
+ }
12496
13284
  };
12497
13285
 
12498
- refresh( dateString ? this.calendar.getFullDate(): null );
13286
+ if( dateValue )
13287
+ {
13288
+ refresh( this.calendar.getFullDate() );
13289
+ }
13290
+ else
13291
+ {
13292
+ refresh();
13293
+ }
12499
13294
 
12500
13295
  LX.doAsync( this.onResize.bind( this ) );
12501
13296
  }
@@ -12505,14 +13300,14 @@ LX.DatePicker = DatePicker;
12505
13300
 
12506
13301
  /**
12507
13302
  * @class Map2D
12508
- * @description Map2D Widget
13303
+ * @description Map2D Component
12509
13304
  */
12510
13305
 
12511
- class Map2D extends Widget {
13306
+ class Map2D extends BaseComponent {
12512
13307
 
12513
13308
  constructor( name, points, callback, options = {} ) {
12514
13309
 
12515
- super( Widget.MAP2D, name, null, options );
13310
+ super( BaseComponent.MAP2D, name, null, options );
12516
13311
 
12517
13312
  this.onGetValue = () => {
12518
13313
  return this.map2d.weightsObj;
@@ -12550,6 +13345,127 @@ class Map2D extends Widget {
12550
13345
 
12551
13346
  LX.Map2D = Map2D;
12552
13347
 
13348
+ /**
13349
+ * @class Rate
13350
+ * @description Rate Component
13351
+ */
13352
+
13353
+ class Rate extends BaseComponent {
13354
+
13355
+ constructor( name, value, callback, options = {} ) {
13356
+
13357
+ const allowHalf = options.allowHalf ?? false;
13358
+
13359
+ if( !allowHalf)
13360
+ {
13361
+ value = Math.floor( value );
13362
+ }
13363
+
13364
+ super( BaseComponent.RATE, name, value, options );
13365
+
13366
+ this.onGetValue = () => {
13367
+ return value;
13368
+ };
13369
+
13370
+ this.onSetValue = ( newValue, skipCallback, event ) => {
13371
+
13372
+ value = newValue;
13373
+
13374
+ _updateStars( value );
13375
+
13376
+ if( !skipCallback )
13377
+ {
13378
+ this._trigger( new LX.IEvent( name, newValue, event ), callback );
13379
+ }
13380
+ };
13381
+
13382
+ this.onResize = ( rect ) => {
13383
+ const realNameWidth = ( this.root.domName?.style.width ?? "0px" );
13384
+ container.style.width = `calc( 100% - ${ realNameWidth })`;
13385
+ };
13386
+
13387
+ const container = document.createElement('div');
13388
+ container.className = "lexrate relative";
13389
+ this.root.appendChild( container );
13390
+
13391
+ const starsContainer = LX.makeContainer( ["fit-content", "auto"], "flex flex-row gap-1", "", container );
13392
+ const filledStarsContainer = LX.makeContainer( ["fit-content", "auto"], "absolute top-0 flex flex-row gap-1 pointer-events-none", "", container );
13393
+ const halfStarsContainer = LX.makeContainer( ["fit-content", "auto"], "absolute top-0 flex flex-row gap-1 pointer-events-none", "", container );
13394
+
13395
+ starsContainer.addEventListener("mousemove", e => {
13396
+ const star = e.target;
13397
+ const idx = star.dataset["idx"];
13398
+
13399
+ if( idx !== undefined )
13400
+ {
13401
+ const rect = star.getBoundingClientRect();
13402
+ const half = allowHalf && e.offsetX < ( rect.width * 0.5 );
13403
+ _updateStars( idx - ( half ? 0.5 : 0.0 ) );
13404
+ }
13405
+ }, false );
13406
+
13407
+ starsContainer.addEventListener("mouseleave", e => {
13408
+ _updateStars( value );
13409
+ }, false );
13410
+
13411
+ // Create all layers of stars
13412
+
13413
+ for( let i = 0; i < 5; ++i )
13414
+ {
13415
+ const starIcon = LX.makeIcon( "Star", { svgClass: `lg fill-current fg-secondary` } );
13416
+ starIcon.dataset["idx"] = ( i + 1 );
13417
+ starsContainer.appendChild( starIcon );
13418
+
13419
+ starIcon.addEventListener("click", e => {
13420
+ const rect = e.target.getBoundingClientRect();
13421
+ const half = allowHalf && e.offsetX < ( rect.width * 0.5 );
13422
+ this.set( parseFloat( e.target.dataset["idx"] ) - ( half ? 0.5 : 0.0 ) );
13423
+ }, false );
13424
+
13425
+ const filledStarIcon = LX.makeIcon( "Star", { svgClass: `lg fill-current metallicyellow` } );
13426
+ filledStarsContainer.appendChild( filledStarIcon );
13427
+
13428
+ const halfStarIcon = LX.makeIcon( "StarHalf", { svgClass: `lg fill-current metallicyellow` } );
13429
+ halfStarsContainer.appendChild( halfStarIcon );
13430
+ }
13431
+
13432
+ const _updateStars = ( v ) => {
13433
+
13434
+ for( let i = 0; i < 5; ++i )
13435
+ {
13436
+ const filled = ( v > ( i + 0.5 ) );
13437
+ const starIcon = filledStarsContainer.childNodes[ i ];
13438
+ const halfStarIcon = halfStarsContainer.childNodes[ i ];
13439
+ if( filled )
13440
+ {
13441
+ starIcon.style.opacity = 1;
13442
+ }
13443
+ else
13444
+ {
13445
+ starIcon.style.opacity = 0;
13446
+
13447
+ const halfFilled = allowHalf && ( v > i );
13448
+ if( halfFilled )
13449
+ {
13450
+ halfStarIcon.style.opacity = 1;
13451
+ }
13452
+ else
13453
+ {
13454
+ halfStarIcon.style.opacity = 0;
13455
+ }
13456
+
13457
+ }
13458
+ }
13459
+ };
13460
+
13461
+ _updateStars( value );
13462
+
13463
+ LX.doAsync( this.onResize.bind( this ) );
13464
+ }
13465
+ }
13466
+
13467
+ LX.Rate = Rate;
13468
+
12553
13469
  // panel.js @jxarco
12554
13470
 
12555
13471
  /**
@@ -12588,42 +13504,42 @@ class Panel {
12588
13504
 
12589
13505
  this.root = root;
12590
13506
  this.branches = [];
12591
- this.widgets = {};
13507
+ this.components = {};
12592
13508
 
12593
13509
  this._branchOpen = false;
12594
13510
  this._currentBranch = null;
12595
- this._queue = []; // Append widgets in other locations
12596
- this._inlineWidgetsLeft = -1;
12597
- this._inline_queued_container = null;
13511
+ this._queue = []; // Append components in other locations
13512
+ this._inlineComponentsLeft = -1;
13513
+ this._inlineQueuedContainer = null;
12598
13514
  }
12599
13515
 
12600
13516
  get( name ) {
12601
13517
 
12602
- return this.widgets[ name ];
13518
+ return this.components[ name ];
12603
13519
  }
12604
13520
 
12605
13521
  getValue( name ) {
12606
13522
 
12607
- let widget = this.widgets[ name ];
13523
+ let component = this.components[ name ];
12608
13524
 
12609
- if( !widget )
13525
+ if( !component )
12610
13526
  {
12611
- throw( "No widget called " + name );
13527
+ throw( "No component called " + name );
12612
13528
  }
12613
13529
 
12614
- return widget.value();
13530
+ return component.value();
12615
13531
  }
12616
13532
 
12617
13533
  setValue( name, value, skipCallback ) {
12618
13534
 
12619
- let widget = this.widgets[ name ];
13535
+ let component = this.components[ name ];
12620
13536
 
12621
- if( !widget )
13537
+ if( !component )
12622
13538
  {
12623
- throw( "No widget called " + name );
13539
+ throw( "No component called " + name );
12624
13540
  }
12625
13541
 
12626
- return widget.set( value, skipCallback );
13542
+ return component.set( value, skipCallback );
12627
13543
  }
12628
13544
 
12629
13545
  /**
@@ -12644,18 +13560,18 @@ class Panel {
12644
13560
 
12645
13561
  clear() {
12646
13562
 
12647
- this._branchOpen = false;
12648
13563
  this.branches = [];
13564
+ this._branchOpen = false;
12649
13565
  this._currentBranch = null;
12650
13566
 
12651
- for( let w in this.widgets )
13567
+ for( let w in this.components )
12652
13568
  {
12653
- if( this.widgets[ w ].options && this.widgets[ w ].options.signal )
13569
+ if( this.components[ w ].options && this.components[ w ].options.signal )
12654
13570
  {
12655
- const signal = this.widgets[ w ].options.signal;
13571
+ const signal = this.components[ w ].options.signal;
12656
13572
  for( let i = 0; i < LX.signals[signal].length; i++ )
12657
13573
  {
12658
- if( LX.signals[signal][ i ] == this.widgets[ w ] )
13574
+ if( LX.signals[signal][ i ] == this.components[ w ] )
12659
13575
  {
12660
13576
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
12661
13577
  }
@@ -12667,11 +13583,11 @@ class Panel {
12667
13583
  {
12668
13584
  for( let w = 0; w < this.signals.length; w++ )
12669
13585
  {
12670
- let widget = Object.values(this.signals[ w ])[ 0 ];
12671
- let signal = widget.options.signal;
13586
+ let c = Object.values(this.signals[ w ])[ 0 ];
13587
+ let signal = c.options.signal;
12672
13588
  for( let i = 0; i < LX.signals[signal].length; i++ )
12673
13589
  {
12674
- if( LX.signals[signal][ i ] == widget )
13590
+ if( LX.signals[signal][ i ] == c )
12675
13591
  {
12676
13592
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
12677
13593
  }
@@ -12679,46 +13595,46 @@ class Panel {
12679
13595
  }
12680
13596
  }
12681
13597
 
12682
- this.widgets = {};
13598
+ this.components = {};
12683
13599
  this.root.innerHTML = "";
12684
13600
  }
12685
13601
 
12686
13602
  /**
12687
13603
  * @method sameLine
12688
- * @param {Number} number Of widgets that will be placed in the same line
12689
- * @param {String} className Extra class to customize inline widgets parent container
12690
- * @description Next N widgets will be in the same line. If no number, it will inline all until calling nextLine()
13604
+ * @param {Number} numberOfComponents Of components that will be placed in the same line
13605
+ * @param {String} className Extra class to customize inline components parent container
13606
+ * @description Next N components will be in the same line. If no number, it will inline all until calling nextLine()
12691
13607
  */
12692
13608
 
12693
- sameLine( number, className ) {
13609
+ sameLine( numberOfComponents, className ) {
12694
13610
 
12695
- this._inline_queued_container = this.queuedContainer;
12696
- this._inlineWidgetsLeft = ( number ?? Infinity );
13611
+ this._inlineQueuedContainer = this.queuedContainer;
13612
+ this._inlineComponentsLeft = ( numberOfComponents ?? Infinity );
12697
13613
  this._inlineExtraClass = className ?? null;
12698
13614
  }
12699
13615
 
12700
13616
  /**
12701
13617
  * @method endLine
12702
- * @param {String} className Extra class to customize inline widgets parent container
12703
- * @description Stop inlining widgets. Use it only if the number of widgets to be inlined is NOT specified.
13618
+ * @param {String} className Extra class to customize inline components parent container
13619
+ * @description Stop inlining components. Use it only if the number of components to be inlined is NOT specified.
12704
13620
  */
12705
13621
 
12706
13622
  endLine( className ) {
12707
13623
 
12708
13624
  className = className ?? this._inlineExtraClass;
12709
13625
 
12710
- if( this._inlineWidgetsLeft == -1 )
13626
+ if( this._inlineComponentsLeft == -1 )
12711
13627
  {
12712
- console.warn("No pending widgets to be inlined!");
13628
+ console.warn("No pending components to be inlined!");
12713
13629
  return;
12714
13630
  }
12715
13631
 
12716
- this._inlineWidgetsLeft = -1;
13632
+ this._inlineComponentsLeft = -1;
12717
13633
 
12718
13634
  if( !this._inlineContainer )
12719
13635
  {
12720
13636
  this._inlineContainer = document.createElement('div');
12721
- this._inlineContainer.className = "lexinlinewidgets";
13637
+ this._inlineContainer.className = "lexinlinecomponents";
12722
13638
 
12723
13639
  if( className )
12724
13640
  {
@@ -12727,14 +13643,14 @@ class Panel {
12727
13643
  }
12728
13644
 
12729
13645
  // Push all elements single element or Array[element, container]
12730
- for( let item of this._inlineWidgets )
13646
+ for( let item of this._inlineComponents )
12731
13647
  {
12732
13648
  const isPair = ( item.constructor == Array );
12733
13649
 
12734
13650
  if( isPair )
12735
13651
  {
12736
13652
  // eg. an array, inline items appended later to
12737
- if( this._inline_queued_container )
13653
+ if( this._inlineQueuedContainer )
12738
13654
  {
12739
13655
  this._inlineContainer.appendChild( item[ 0 ] );
12740
13656
  }
@@ -12750,7 +13666,7 @@ class Panel {
12750
13666
  }
12751
13667
  }
12752
13668
 
12753
- if( !this._inline_queued_container )
13669
+ if( !this._inlineQueuedContainer )
12754
13670
  {
12755
13671
  if( this._currentBranch )
12756
13672
  {
@@ -12763,10 +13679,10 @@ class Panel {
12763
13679
  }
12764
13680
  else
12765
13681
  {
12766
- this._inline_queued_container.appendChild( this._inlineContainer );
13682
+ this._inlineQueuedContainer.appendChild( this._inlineContainer );
12767
13683
  }
12768
13684
 
12769
- delete this._inlineWidgets;
13685
+ delete this._inlineComponents;
12770
13686
  delete this._inlineContainer;
12771
13687
  delete this._inlineExtraClass;
12772
13688
  }
@@ -12779,7 +13695,7 @@ class Panel {
12779
13695
  * className: Add class to the branch
12780
13696
  * closed: Set branch collapsed/opened [false]
12781
13697
  * icon: Set branch icon (LX.ICONS)
12782
- * filter: Allow filter widgets in branch by name [false]
13698
+ * filter: Allow filter components in branch by name [false]
12783
13699
  */
12784
13700
 
12785
13701
  branch( name, options = {} ) {
@@ -12800,10 +13716,10 @@ class Panel {
12800
13716
  this.branches.push( branch );
12801
13717
  this.root.appendChild( branch.root );
12802
13718
 
12803
- // Add widget filter
13719
+ // Add component filter
12804
13720
  if( options.filter )
12805
13721
  {
12806
- this._addFilter( options.filter, { callback: this._searchWidgets.bind( this, branch.name ) } );
13722
+ this._addFilter( options.filter, { callback: this._searchComponents.bind( this, branch.name ) } );
12807
13723
  }
12808
13724
 
12809
13725
  return branch;
@@ -12819,27 +13735,27 @@ class Panel {
12819
13735
  }
12820
13736
 
12821
13737
  /*
12822
- Panel Widgets
13738
+ Panel Components
12823
13739
  */
12824
13740
 
12825
- _attachWidget( widget, options = {} ) {
13741
+ _attachComponent( component, options = {} ) {
12826
13742
 
12827
- if( widget.name != undefined )
13743
+ if( component.name != undefined )
12828
13744
  {
12829
- this.widgets[ widget.name ] = widget;
13745
+ this.components[ component.name ] = component;
12830
13746
  }
12831
13747
 
12832
- if( widget.options.signal && !widget.name )
13748
+ if( component.options.signal && !component.name )
12833
13749
  {
12834
13750
  if( !this.signals )
12835
13751
  {
12836
13752
  this.signals = [];
12837
13753
  }
12838
13754
 
12839
- this.signals.push( { [ widget.options.signal ]: widget } );
13755
+ this.signals.push( { [ component.options.signal ]: component } );
12840
13756
  }
12841
13757
 
12842
- const _insertWidget = el => {
13758
+ const _insertComponent = el => {
12843
13759
  if( options.container )
12844
13760
  {
12845
13761
  options.container.appendChild( el );
@@ -12848,9 +13764,9 @@ class Panel {
12848
13764
  {
12849
13765
  if( this._currentBranch )
12850
13766
  {
12851
- if( !options.skipWidget )
13767
+ if( !options.skipComponent )
12852
13768
  {
12853
- this._currentBranch.widgets.push( widget );
13769
+ this._currentBranch.components.push( component );
12854
13770
  }
12855
13771
  this._currentBranch.content.appendChild( el );
12856
13772
  }
@@ -12867,54 +13783,54 @@ class Panel {
12867
13783
  }
12868
13784
  };
12869
13785
 
12870
- const _storeWidget = el => {
13786
+ const _storeComponent = el => {
12871
13787
 
12872
13788
  if( !this.queuedContainer )
12873
13789
  {
12874
- this._inlineWidgets.push( el );
13790
+ this._inlineComponents.push( el );
12875
13791
  }
12876
13792
  // Append content to queued tab container
12877
13793
  else
12878
13794
  {
12879
- this._inlineWidgets.push( [ el, this.queuedContainer ] );
13795
+ this._inlineComponents.push( [ el, this.queuedContainer ] );
12880
13796
  }
12881
13797
  };
12882
13798
 
12883
- // Process inline widgets
12884
- if( this._inlineWidgetsLeft > 0 && !options.skipInlineCount )
13799
+ // Process inline components
13800
+ if( this._inlineComponentsLeft > 0 && !options.skipInlineCount )
12885
13801
  {
12886
- if( !this._inlineWidgets )
13802
+ if( !this._inlineComponents )
12887
13803
  {
12888
- this._inlineWidgets = [];
13804
+ this._inlineComponents = [];
12889
13805
  }
12890
13806
 
12891
- // Store widget and its container
12892
- _storeWidget( widget.root );
13807
+ // Store component and its container
13808
+ _storeComponent( component.root );
12893
13809
 
12894
- this._inlineWidgetsLeft--;
13810
+ this._inlineComponentsLeft--;
12895
13811
 
12896
- // Last widget
12897
- if( !this._inlineWidgetsLeft )
13812
+ // Last component
13813
+ if( !this._inlineComponentsLeft )
12898
13814
  {
12899
13815
  this.endLine();
12900
13816
  }
12901
13817
  }
12902
13818
  else
12903
13819
  {
12904
- _insertWidget( widget.root );
13820
+ _insertComponent( component.root );
12905
13821
  }
12906
13822
 
12907
- return widget;
13823
+ return component;
12908
13824
  }
12909
13825
 
12910
13826
  _addFilter( placeholder, options = {} ) {
12911
13827
 
12912
13828
  options.placeholder = placeholder.constructor == String ? placeholder : "Filter properties..";
12913
- options.skipWidget = options.skipWidget ?? true;
13829
+ options.skipComponent = options.skipComponent ?? true;
12914
13830
  options.skipInlineCount = true;
12915
13831
 
12916
- let widget = new LX.TextInput( null, null, null, options );
12917
- const element = widget.root;
13832
+ let component = new LX.TextInput( null, null, null, options );
13833
+ const element = component.root;
12918
13834
  element.className += " lexfilter";
12919
13835
 
12920
13836
  let input = document.createElement('input');
@@ -12937,7 +13853,7 @@ class Panel {
12937
13853
  return element;
12938
13854
  }
12939
13855
 
12940
- _searchWidgets( branchName, value ) {
13856
+ _searchComponents( branchName, value ) {
12941
13857
 
12942
13858
  for( let b of this.branches )
12943
13859
  {
@@ -12946,8 +13862,8 @@ class Panel {
12946
13862
  continue;
12947
13863
  }
12948
13864
 
12949
- // remove all widgets
12950
- for( let w of b.widgets )
13865
+ // remove all components
13866
+ for( let w of b.components )
12951
13867
  {
12952
13868
  if( w.domEl.classList.contains('lexfilter') )
12953
13869
  {
@@ -12961,8 +13877,8 @@ class Panel {
12961
13877
 
12962
13878
  const emptyFilter = !value.length;
12963
13879
 
12964
- // add widgets
12965
- for( let w of b.widgets )
13880
+ // add components
13881
+ for( let w of b.components )
12966
13882
  {
12967
13883
  if( !emptyFilter )
12968
13884
  {
@@ -12972,7 +13888,7 @@ class Panel {
12972
13888
  if(!name.includes(value)) continue;
12973
13889
  }
12974
13890
 
12975
- // insert filtered widget
13891
+ // insert filtered component
12976
13892
  this.queuedContainer.appendChild( w.domEl );
12977
13893
  }
12978
13894
 
@@ -13043,13 +13959,13 @@ class Panel {
13043
13959
  var element = document.createElement('div');
13044
13960
  element.className = "lexseparator";
13045
13961
 
13046
- let widget = new LX.Widget( LX.Widget.SEPARATOR );
13047
- widget.root = element;
13962
+ let component = new LX.BaseComponent( LX.BaseComponent.SEPARATOR );
13963
+ component.root = element;
13048
13964
 
13049
13965
  if( this._currentBranch )
13050
13966
  {
13051
13967
  this._currentBranch.content.appendChild( element );
13052
- this._currentBranch.widgets.push( widget );
13968
+ this._currentBranch.components.push( component );
13053
13969
  }
13054
13970
  else
13055
13971
  {
@@ -13064,8 +13980,8 @@ class Panel {
13064
13980
  */
13065
13981
 
13066
13982
  addBlank( width, height ) {
13067
- const widget = new LX.Blank( width, height );
13068
- return this._attachWidget( widget );
13983
+ const component = new LX.Blank( width, height );
13984
+ return this._attachComponent( component );
13069
13985
  }
13070
13986
 
13071
13987
  /**
@@ -13080,18 +13996,18 @@ class Panel {
13080
13996
  */
13081
13997
 
13082
13998
  addTitle( name, options = {} ) {
13083
- const widget = new LX.Title( name, options );
13084
- return this._attachWidget( widget );
13999
+ const component = new LX.Title( name, options );
14000
+ return this._attachComponent( component );
13085
14001
  }
13086
14002
 
13087
14003
  /**
13088
14004
  * @method addText
13089
- * @param {String} name Widget name
14005
+ * @param {String} name Component name
13090
14006
  * @param {String} value Text value
13091
14007
  * @param {Function} callback Callback function on change
13092
14008
  * @param {Object} options:
13093
14009
  * hideName: Don't use name as label [false]
13094
- * disabled: Make the widget disabled [false]
14010
+ * disabled: Make the component disabled [false]
13095
14011
  * required: Make the input required
13096
14012
  * placeholder: Add input placeholder
13097
14013
  * icon: Icon (if any) to append at the input start
@@ -13106,18 +14022,18 @@ class Panel {
13106
14022
  */
13107
14023
 
13108
14024
  addText( name, value, callback, options = {} ) {
13109
- const widget = new LX.TextInput( name, value, callback, options );
13110
- return this._attachWidget( widget );
14025
+ const component = new LX.TextInput( name, value, callback, options );
14026
+ return this._attachComponent( component );
13111
14027
  }
13112
14028
 
13113
14029
  /**
13114
14030
  * @method addTextArea
13115
- * @param {String} name Widget name
14031
+ * @param {String} name Component name
13116
14032
  * @param {String} value Text Area value
13117
14033
  * @param {Function} callback Callback function on change
13118
14034
  * @param {Object} options:
13119
14035
  * hideName: Don't use name as label [false]
13120
- * disabled: Make the widget disabled [false]
14036
+ * disabled: Make the component disabled [false]
13121
14037
  * placeholder: Add input placeholder
13122
14038
  * resize: Allow resize [true]
13123
14039
  * trigger: Choose onchange trigger (default, input) [default]
@@ -13128,8 +14044,8 @@ class Panel {
13128
14044
  */
13129
14045
 
13130
14046
  addTextArea( name, value, callback, options = {} ) {
13131
- const widget = new LX.TextArea( name, value, callback, options );
13132
- return this._attachWidget( widget );
14047
+ const component = new LX.TextArea( name, value, callback, options );
14048
+ return this._attachComponent( component );
13133
14049
  }
13134
14050
 
13135
14051
  /**
@@ -13141,19 +14057,19 @@ class Panel {
13141
14057
  addLabel( value, options = {} ) {
13142
14058
  options.disabled = true;
13143
14059
  options.inputClass = ( options.inputClass ?? "" ) + " nobg";
13144
- const widget = this.addText( null, value, null, options );
13145
- widget.type = LX.Widget.LABEL;
13146
- return widget;
14060
+ const component = this.addText( null, value, null, options );
14061
+ component.type = LX.BaseComponent.LABEL;
14062
+ return component;
13147
14063
  }
13148
14064
 
13149
14065
  /**
13150
14066
  * @method addButton
13151
- * @param {String} name Widget name
14067
+ * @param {String} name Component name
13152
14068
  * @param {String} value Button name
13153
14069
  * @param {Function} callback Callback function on click
13154
14070
  * @param {Object} options:
13155
14071
  * hideName: Don't use name as label [false]
13156
- * disabled: Make the widget disabled [false]
14072
+ * disabled: Make the component disabled [false]
13157
14073
  * icon: Icon class to show as button value
13158
14074
  * iconPosition: Icon position (cover|start|end)
13159
14075
  * fileInput: Button click requests a file
@@ -13161,16 +14077,17 @@ class Panel {
13161
14077
  * img: Path to image to show as button value
13162
14078
  * title: Text to show in native Element title
13163
14079
  * buttonClass: Class to add to the native button element
14080
+ * mustConfirm: User must confirm trigger in a popover
13164
14081
  */
13165
14082
 
13166
14083
  addButton( name, value, callback, options = {} ) {
13167
- const widget = new LX.Button( name, value, callback, options );
13168
- return this._attachWidget( widget );
14084
+ const component = new LX.Button( name, value, callback, options );
14085
+ return this._attachComponent( component );
13169
14086
  }
13170
14087
 
13171
14088
  /**
13172
14089
  * @method addComboButtons
13173
- * @param {String} name Widget name
14090
+ * @param {String} name Component name
13174
14091
  * @param {Array} values Each of the {value, callback, selected, disabled} items
13175
14092
  * @param {Object} options:
13176
14093
  * hideName: Don't use name as label [false]
@@ -13181,8 +14098,8 @@ class Panel {
13181
14098
  */
13182
14099
 
13183
14100
  addComboButtons( name, values, options = {} ) {
13184
- const widget = new LX.ComboButtons( name, values, options );
13185
- return this._attachWidget( widget );
14101
+ const component = new LX.ComboButtons( name, values, options );
14102
+ return this._attachComponent( component );
13186
14103
  }
13187
14104
 
13188
14105
  /**
@@ -13197,13 +14114,13 @@ class Panel {
13197
14114
  */
13198
14115
 
13199
14116
  addCard( name, options = {} ) {
13200
- const widget = new LX.Card( name, options );
13201
- return this._attachWidget( widget );
14117
+ const component = new LX.Card( name, options );
14118
+ return this._attachComponent( component );
13202
14119
  }
13203
14120
 
13204
14121
  /**
13205
14122
  * @method addForm
13206
- * @param {String} name Widget name
14123
+ * @param {String} name Component name
13207
14124
  * @param {Object} data Form data
13208
14125
  * @param {Function} callback Callback function on submit form
13209
14126
  * @param {Object} options:
@@ -13216,13 +14133,13 @@ class Panel {
13216
14133
  */
13217
14134
 
13218
14135
  addForm( name, data, callback, options = {} ) {
13219
- const widget = new LX.Form( name, data, callback, options );
13220
- return this._attachWidget( widget );
14136
+ const component = new LX.Form( name, data, callback, options );
14137
+ return this._attachComponent( component );
13221
14138
  }
13222
14139
 
13223
14140
  /**
13224
14141
  * @method addContent
13225
- * @param {String} name Widget name
14142
+ * @param {String} name Component name
13226
14143
  * @param {HTMLElement/String} element
13227
14144
  * @param {Object} options
13228
14145
  */
@@ -13248,15 +14165,15 @@ class Panel {
13248
14165
 
13249
14166
  options.hideName = true;
13250
14167
 
13251
- let widget = new LX.Widget( LX.Widget.CONTENT, name, null, options );
13252
- widget.root.appendChild( element );
14168
+ let component = new LX.BaseComponent( LX.BaseComponent.CONTENT, name, null, options );
14169
+ component.root.appendChild( element );
13253
14170
 
13254
- return this._attachWidget( widget );
14171
+ return this._attachComponent( component );
13255
14172
  }
13256
14173
 
13257
14174
  /**
13258
14175
  * @method addImage
13259
- * @param {String} name Widget name
14176
+ * @param {String} name Component name
13260
14177
  * @param {String} url Image Url
13261
14178
  * @param {Object} options
13262
14179
  * hideName: Don't use name as label [false]
@@ -13275,43 +14192,43 @@ class Panel {
13275
14192
  Object.assign( img.style, options.style ?? {} );
13276
14193
  container.appendChild( img );
13277
14194
 
13278
- let widget = new LX.Widget( LX.Widget.IMAGE, name, null, options );
13279
- widget.root.appendChild( container );
14195
+ let component = new LX.BaseComponent( LX.BaseComponent.IMAGE, name, null, options );
14196
+ component.root.appendChild( container );
13280
14197
 
13281
14198
  // await img.decode();
13282
14199
  img.decode();
13283
14200
 
13284
- return this._attachWidget( widget );
14201
+ return this._attachComponent( component );
13285
14202
  }
13286
14203
 
13287
14204
  /**
13288
14205
  * @method addSelect
13289
- * @param {String} name Widget name
13290
- * @param {Array} values Posible options of the select widget -> String (for default select) or Object = {value, url} (for images, gifs..)
14206
+ * @param {String} name Component name
14207
+ * @param {Array} values Posible options of the select component -> String (for default select) or Object = {value, url} (for images, gifs..)
13291
14208
  * @param {String} value Select by default option
13292
14209
  * @param {Function} callback Callback function on change
13293
14210
  * @param {Object} options:
13294
14211
  * hideName: Don't use name as label [false]
13295
- * filter: Add a search bar to the widget [false]
13296
- * disabled: Make the widget disabled [false]
14212
+ * filter: Add a search bar to the component [false]
14213
+ * disabled: Make the component disabled [false]
13297
14214
  * skipReset: Don't add the reset value button when value changes
13298
14215
  * placeholder: Placeholder for the filter input
13299
14216
  * emptyMsg: Custom message to show when no filtered results
13300
14217
  */
13301
14218
 
13302
14219
  addSelect( name, values, value, callback, options = {} ) {
13303
- const widget = new LX.Select( name, values, value, callback, options );
13304
- return this._attachWidget( widget );
14220
+ const component = new LX.Select( name, values, value, callback, options );
14221
+ return this._attachComponent( component );
13305
14222
  }
13306
14223
 
13307
14224
  /**
13308
14225
  * @method addCurve
13309
- * @param {String} name Widget name
14226
+ * @param {String} name Component name
13310
14227
  * @param {Array of Array} values Array of 2N Arrays of each value of the curve
13311
14228
  * @param {Function} callback Callback function on change
13312
14229
  * @param {Object} options:
13313
14230
  * skipReset: Don't add the reset value button when value changes
13314
- * bgColor: Widget background color
14231
+ * bgColor: Component background color
13315
14232
  * pointsColor: Curve points color
13316
14233
  * lineColor: Curve line color
13317
14234
  * noOverlap: Points do not overlap, replacing themselves if necessary
@@ -13321,18 +14238,18 @@ class Panel {
13321
14238
  */
13322
14239
 
13323
14240
  addCurve( name, values, callback, options = {} ) {
13324
- const widget = new LX.Curve( name, values, callback, options );
13325
- return this._attachWidget( widget );
14241
+ const component = new LX.Curve( name, values, callback, options );
14242
+ return this._attachComponent( component );
13326
14243
  }
13327
14244
 
13328
14245
  /**
13329
14246
  * @method addDial
13330
- * @param {String} name Widget name
14247
+ * @param {String} name Component name
13331
14248
  * @param {Array of Array} values Array of 2N Arrays of each value of the dial
13332
14249
  * @param {Function} callback Callback function on change
13333
14250
  * @param {Object} options:
13334
14251
  * skipReset: Don't add the reset value button when value changes
13335
- * bgColor: Widget background color
14252
+ * bgColor: Component background color
13336
14253
  * pointsColor: Curve points color
13337
14254
  * lineColor: Curve line color
13338
14255
  * noOverlap: Points do not overlap, replacing themselves if necessary
@@ -13342,26 +14259,26 @@ class Panel {
13342
14259
  */
13343
14260
 
13344
14261
  addDial( name, values, callback, options = {} ) {
13345
- const widget = new LX.Dial( name, values, callback, options );
13346
- return this._attachWidget( widget );
14262
+ const component = new LX.Dial( name, values, callback, options );
14263
+ return this._attachComponent( component );
13347
14264
  }
13348
14265
 
13349
14266
  /**
13350
14267
  * @method addLayers
13351
- * @param {String} name Widget name
14268
+ * @param {String} name Component name
13352
14269
  * @param {Number} value Flag value by default option
13353
14270
  * @param {Function} callback Callback function on change
13354
14271
  * @param {Object} options:
13355
14272
  */
13356
14273
 
13357
14274
  addLayers( name, value, callback, options = {} ) {
13358
- const widget = new LX.Layers( name, value, callback, options );
13359
- return this._attachWidget( widget );
14275
+ const component = new LX.Layers( name, value, callback, options );
14276
+ return this._attachComponent( component );
13360
14277
  }
13361
14278
 
13362
14279
  /**
13363
14280
  * @method addArray
13364
- * @param {String} name Widget name
14281
+ * @param {String} name Component name
13365
14282
  * @param {Array} values By default values in the array
13366
14283
  * @param {Function} callback Callback function on change
13367
14284
  * @param {Object} options:
@@ -13369,13 +14286,13 @@ class Panel {
13369
14286
  */
13370
14287
 
13371
14288
  addArray( name, values = [], callback, options = {} ) {
13372
- const widget = new LX.ItemArray( name, values, callback, options );
13373
- return this._attachWidget( widget );
14289
+ const component = new LX.ItemArray( name, values, callback, options );
14290
+ return this._attachComponent( component );
13374
14291
  }
13375
14292
 
13376
14293
  /**
13377
14294
  * @method addList
13378
- * @param {String} name Widget name
14295
+ * @param {String} name Component name
13379
14296
  * @param {Array} values List values
13380
14297
  * @param {String} value Selected list value
13381
14298
  * @param {Function} callback Callback function on change
@@ -13384,13 +14301,13 @@ class Panel {
13384
14301
  */
13385
14302
 
13386
14303
  addList( name, values, value, callback, options = {} ) {
13387
- const widget = new LX.List( name, values, value, callback, options );
13388
- return this._attachWidget( widget );
14304
+ const component = new LX.List( name, values, value, callback, options );
14305
+ return this._attachComponent( component );
13389
14306
  }
13390
14307
 
13391
14308
  /**
13392
14309
  * @method addTags
13393
- * @param {String} name Widget name
14310
+ * @param {String} name Component name
13394
14311
  * @param {String} value Comma separated tags
13395
14312
  * @param {Function} callback Callback function on change
13396
14313
  * @param {Object} options:
@@ -13398,85 +14315,85 @@ class Panel {
13398
14315
  */
13399
14316
 
13400
14317
  addTags( name, value, callback, options = {} ) {
13401
- const widget = new LX.Tags( name, value, callback, options );
13402
- return this._attachWidget( widget );
14318
+ const component = new LX.Tags( name, value, callback, options );
14319
+ return this._attachComponent( component );
13403
14320
  }
13404
14321
 
13405
14322
  /**
13406
14323
  * @method addCheckbox
13407
- * @param {String} name Widget name
14324
+ * @param {String} name Component name
13408
14325
  * @param {Boolean} value Value of the checkbox
13409
14326
  * @param {Function} callback Callback function on change
13410
14327
  * @param {Object} options:
13411
- * disabled: Make the widget disabled [false]
14328
+ * disabled: Make the component disabled [false]
13412
14329
  * label: Checkbox label
13413
- * suboptions: Callback to add widgets in case of TRUE value
14330
+ * suboptions: Callback to add components in case of TRUE value
13414
14331
  * className: Extra classes to customize style
13415
14332
  */
13416
14333
 
13417
14334
  addCheckbox( name, value, callback, options = {} ) {
13418
- const widget = new LX.Checkbox( name, value, callback, options );
13419
- return this._attachWidget( widget );
14335
+ const component = new LX.Checkbox( name, value, callback, options );
14336
+ return this._attachComponent( component );
13420
14337
  }
13421
14338
 
13422
14339
  /**
13423
14340
  * @method addToggle
13424
- * @param {String} name Widget name
14341
+ * @param {String} name Component name
13425
14342
  * @param {Boolean} value Value of the checkbox
13426
14343
  * @param {Function} callback Callback function on change
13427
14344
  * @param {Object} options:
13428
- * disabled: Make the widget disabled [false]
14345
+ * disabled: Make the component disabled [false]
13429
14346
  * label: Toggle label
13430
- * suboptions: Callback to add widgets in case of TRUE value
14347
+ * suboptions: Callback to add components in case of TRUE value
13431
14348
  * className: Customize colors
13432
14349
  */
13433
14350
 
13434
14351
  addToggle( name, value, callback, options = {} ) {
13435
- const widget = new LX.Toggle( name, value, callback, options );
13436
- return this._attachWidget( widget );
14352
+ const component = new LX.Toggle( name, value, callback, options );
14353
+ return this._attachComponent( component );
13437
14354
  }
13438
14355
 
13439
14356
  /**
13440
14357
  * @method addRadioGroup
13441
- * @param {String} name Widget name
14358
+ * @param {String} name Component name
13442
14359
  * @param {String} label Radio label
13443
14360
  * @param {Array} values Radio options
13444
14361
  * @param {Function} callback Callback function on change
13445
14362
  * @param {Object} options:
13446
- * disabled: Make the widget disabled [false]
14363
+ * disabled: Make the component disabled [false]
13447
14364
  * className: Customize colors
13448
14365
  * selected: Index of the default selected option
13449
14366
  */
13450
14367
 
13451
14368
  addRadioGroup( name, label, values, callback, options = {} ) {
13452
- const widget = new LX.RadioGroup( name, label, values, callback, options );
13453
- return this._attachWidget( widget );
14369
+ const component = new LX.RadioGroup( name, label, values, callback, options );
14370
+ return this._attachComponent( component );
13454
14371
  }
13455
14372
 
13456
14373
  /**
13457
14374
  * @method addColor
13458
- * @param {String} name Widget name
14375
+ * @param {String} name Component name
13459
14376
  * @param {String} value Default color (hex)
13460
14377
  * @param {Function} callback Callback function on change
13461
14378
  * @param {Object} options:
13462
- * disabled: Make the widget disabled [false]
14379
+ * disabled: Make the component disabled [false]
13463
14380
  * useRGB: The callback returns color as Array (r, g, b) and not hex [false]
13464
14381
  */
13465
14382
 
13466
14383
  addColor( name, value, callback, options = {} ) {
13467
- const widget = new LX.ColorInput( name, value, callback, options );
13468
- return this._attachWidget( widget );
14384
+ const component = new LX.ColorInput( name, value, callback, options );
14385
+ return this._attachComponent( component );
13469
14386
  }
13470
14387
 
13471
14388
  /**
13472
14389
  * @method addRange
13473
- * @param {String} name Widget name
14390
+ * @param {String} name Component name
13474
14391
  * @param {Number} value Default number value
13475
14392
  * @param {Function} callback Callback function on change
13476
14393
  * @param {Object} options:
13477
14394
  * hideName: Don't use name as label [false]
13478
14395
  * className: Extra classes to customize style
13479
- * disabled: Make the widget disabled [false]
14396
+ * disabled: Make the component disabled [false]
13480
14397
  * left: The slider goes to the left instead of the right
13481
14398
  * fill: Fill slider progress [true]
13482
14399
  * step: Step of the input
@@ -13484,18 +14401,18 @@ class Panel {
13484
14401
  */
13485
14402
 
13486
14403
  addRange( name, value, callback, options = {} ) {
13487
- const widget = new LX.RangeInput( name, value, callback, options );
13488
- return this._attachWidget( widget );
14404
+ const component = new LX.RangeInput( name, value, callback, options );
14405
+ return this._attachComponent( component );
13489
14406
  }
13490
14407
 
13491
14408
  /**
13492
14409
  * @method addNumber
13493
- * @param {String} name Widget name
14410
+ * @param {String} name Component name
13494
14411
  * @param {Number} value Default number value
13495
14412
  * @param {Function} callback Callback function on change
13496
14413
  * @param {Object} options:
13497
14414
  * hideName: Don't use name as label [false]
13498
- * disabled: Make the widget disabled [false]
14415
+ * disabled: Make the component disabled [false]
13499
14416
  * step: Step of the input
13500
14417
  * precision: The number of digits to appear after the decimal point
13501
14418
  * min, max: Min and Max values for the input
@@ -13506,24 +14423,24 @@ class Panel {
13506
14423
  */
13507
14424
 
13508
14425
  addNumber( name, value, callback, options = {} ) {
13509
- const widget = new LX.NumberInput( name, value, callback, options );
13510
- return this._attachWidget( widget );
14426
+ const component = new LX.NumberInput( name, value, callback, options );
14427
+ return this._attachComponent( component );
13511
14428
  }
13512
14429
 
13513
14430
  static VECTOR_COMPONENTS = { 0: 'x', 1: 'y', 2: 'z', 3: 'w' };
13514
14431
 
13515
14432
  _addVector( numComponents, name, value, callback, options = {} ) {
13516
- const widget = new LX.Vector( numComponents, name, value, callback, options );
13517
- return this._attachWidget( widget );
14433
+ const component = new LX.Vector( numComponents, name, value, callback, options );
14434
+ return this._attachComponent( component );
13518
14435
  }
13519
14436
 
13520
14437
  /**
13521
14438
  * @method addVector N (2, 3, 4)
13522
- * @param {String} name Widget name
14439
+ * @param {String} name Component name
13523
14440
  * @param {Array} value Array of N components
13524
14441
  * @param {Function} callback Callback function on change
13525
14442
  * @param {Object} options:
13526
- * disabled: Make the widget disabled [false]
14443
+ * disabled: Make the component disabled [false]
13527
14444
  * step: Step of the inputs
13528
14445
  * min, max: Min and Max values for the inputs
13529
14446
  * onPress: Callback function on mouse down
@@ -13544,43 +14461,43 @@ class Panel {
13544
14461
 
13545
14462
  /**
13546
14463
  * @method addSize
13547
- * @param {String} name Widget name
14464
+ * @param {String} name Component name
13548
14465
  * @param {Number} value Default number value
13549
14466
  * @param {Function} callback Callback function on change
13550
14467
  * @param {Object} options:
13551
14468
  * hideName: Don't use name as label [false]
13552
- * disabled: Make the widget disabled [false]
14469
+ * disabled: Make the component disabled [false]
13553
14470
  * units: Unit as string added to the end of the value
13554
14471
  */
13555
14472
 
13556
14473
  addSize( name, value, callback, options = {} ) {
13557
- const widget = new LX.SizeInput( name, value, callback, options );
13558
- return this._attachWidget( widget );
14474
+ const component = new LX.SizeInput( name, value, callback, options );
14475
+ return this._attachComponent( component );
13559
14476
  }
13560
14477
 
13561
14478
  /**
13562
14479
  * @method addOTP
13563
- * @param {String} name Widget name
14480
+ * @param {String} name Component name
13564
14481
  * @param {String} value Default numeric value in string format
13565
14482
  * @param {Function} callback Callback function on change
13566
14483
  * @param {Object} options:
13567
14484
  * hideName: Don't use name as label [false]
13568
- * disabled: Make the widget disabled [false]
14485
+ * disabled: Make the component disabled [false]
13569
14486
  * pattern: OTP numeric pattern
13570
14487
  */
13571
14488
 
13572
14489
  addOTP( name, value, callback, options = {} ) {
13573
- const widget = new LX.OTPInput( name, value, callback, options );
13574
- return this._attachWidget( widget );
14490
+ const component = new LX.OTPInput( name, value, callback, options );
14491
+ return this._attachComponent( component );
13575
14492
  }
13576
14493
 
13577
14494
  /**
13578
14495
  * @method addPad
13579
- * @param {String} name Widget name
14496
+ * @param {String} name Component name
13580
14497
  * @param {Array} value Pad value
13581
14498
  * @param {Function} callback Callback function on change
13582
14499
  * @param {Object} options:
13583
- * disabled: Make the widget disabled [false]
14500
+ * disabled: Make the component disabled [false]
13584
14501
  * min, max: Min and Max values
13585
14502
  * padSize: Size of the pad (css)
13586
14503
  * onPress: Callback function on mouse down
@@ -13588,13 +14505,13 @@ class Panel {
13588
14505
  */
13589
14506
 
13590
14507
  addPad( name, value, callback, options = {} ) {
13591
- const widget = new LX.Pad( name, value, callback, options );
13592
- return this._attachWidget( widget );
14508
+ const component = new LX.Pad( name, value, callback, options );
14509
+ return this._attachComponent( component );
13593
14510
  }
13594
14511
 
13595
14512
  /**
13596
14513
  * @method addProgress
13597
- * @param {String} name Widget name
14514
+ * @param {String} name Component name
13598
14515
  * @param {Number} value Progress value
13599
14516
  * @param {Object} options:
13600
14517
  * min, max: Min and Max values
@@ -13605,29 +14522,29 @@ class Panel {
13605
14522
  */
13606
14523
 
13607
14524
  addProgress( name, value, options = {} ) {
13608
- const widget = new LX.Progress( name, value, options );
13609
- return this._attachWidget( widget );
14525
+ const component = new LX.Progress( name, value, options );
14526
+ return this._attachComponent( component );
13610
14527
  }
13611
14528
 
13612
14529
  /**
13613
14530
  * @method addFile
13614
- * @param {String} name Widget name
14531
+ * @param {String} name Component name
13615
14532
  * @param {Function} callback Callback function on change
13616
14533
  * @param {Object} options:
13617
14534
  * local: Ask for local file
13618
- * disabled: Make the widget disabled [false]
14535
+ * disabled: Make the component disabled [false]
13619
14536
  * read: Return the file itself (False) or the contents (True)
13620
14537
  * type: type to read as [text (Default), buffer, bin, url]
13621
14538
  */
13622
14539
 
13623
14540
  addFile( name, callback, options = { } ) {
13624
- const widget = new LX.FileInput( name, callback, options );
13625
- return this._attachWidget( widget );
14541
+ const component = new LX.FileInput( name, callback, options );
14542
+ return this._attachComponent( component );
13626
14543
  }
13627
14544
 
13628
14545
  /**
13629
14546
  * @method addTree
13630
- * @param {String} name Widget name
14547
+ * @param {String} name Component name
13631
14548
  * @param {Object} data Data of the tree
13632
14549
  * @param {Object} options:
13633
14550
  * icons: Array of objects with icon button information {name, icon, callback}
@@ -13637,13 +14554,13 @@ class Panel {
13637
14554
  */
13638
14555
 
13639
14556
  addTree( name, data, options = {} ) {
13640
- const widget = new LX.Tree( name, data, options );
13641
- return this._attachWidget( widget );
14557
+ const component = new LX.Tree( name, data, options );
14558
+ return this._attachComponent( component );
13642
14559
  }
13643
14560
 
13644
14561
  /**
13645
14562
  * @method addTabSections
13646
- * @param {String} name Widget name
14563
+ * @param {String} name Component name
13647
14564
  * @param {Array} tabs Contains objects with {
13648
14565
  * name: Name of the tab (if icon, use as title)
13649
14566
  * icon: Icon to be used as the tab icon (optional)
@@ -13658,30 +14575,30 @@ class Panel {
13658
14575
  */
13659
14576
 
13660
14577
  addTabSections( name, tabs, options = {} ) {
13661
- const widget = new LX.TabSections( name, tabs, options );
13662
- return this._attachWidget( widget );
14578
+ const component = new LX.TabSections( name, tabs, options );
14579
+ return this._attachComponent( component );
13663
14580
  }
13664
14581
 
13665
14582
  /**
13666
14583
  * @method addCounter
13667
- * @param {String} name Widget name
14584
+ * @param {String} name Component name
13668
14585
  * @param {Number} value Counter value
13669
14586
  * @param {Function} callback Callback function on change
13670
14587
  * @param {Object} options:
13671
- * disabled: Make the widget disabled [false]
14588
+ * disabled: Make the component disabled [false]
13672
14589
  * min, max: Min and Max values
13673
14590
  * step: Step for adding/substracting
13674
14591
  * label: Text to show below the counter
13675
14592
  */
13676
14593
 
13677
14594
  addCounter( name, value, callback, options = { } ) {
13678
- const widget = new LX.Counter( name, value, callback, options );
13679
- return this._attachWidget( widget );
14595
+ const component = new LX.Counter( name, value, callback, options );
14596
+ return this._attachComponent( component );
13680
14597
  }
13681
14598
 
13682
14599
  /**
13683
14600
  * @method addTable
13684
- * @param {String} name Widget name
14601
+ * @param {String} name Component name
13685
14602
  * @param {Number} data Table data
13686
14603
  * @param {Object} options:
13687
14604
  * hideName: Don't use name as label [false]
@@ -13699,14 +14616,14 @@ class Panel {
13699
14616
  */
13700
14617
 
13701
14618
  addTable( name, data, options = { } ) {
13702
- const widget = new LX.Table( name, data, options );
13703
- return this._attachWidget( widget );
14619
+ const component = new LX.Table( name, data, options );
14620
+ return this._attachComponent( component );
13704
14621
  }
13705
14622
 
13706
14623
  /**
13707
14624
  * @method addDate
13708
- * @param {String} name Widget name
13709
- * @param {String} dateString
14625
+ * @param {String} name Component name
14626
+ * @param {String} dateValue
13710
14627
  * @param {Function} callback
13711
14628
  * @param {Object} options:
13712
14629
  * hideName: Don't use name as label [false]
@@ -13715,22 +14632,35 @@ class Panel {
13715
14632
  * fromToday: Allow dates only from current day
13716
14633
  */
13717
14634
 
13718
- addDate( name, dateString, callback, options = { } ) {
13719
- const widget = new LX.DatePicker( name, dateString, callback, options );
13720
- return this._attachWidget( widget );
14635
+ addDate( name, dateValue, callback, options = { } ) {
14636
+ const component = new LX.DatePicker( name, dateValue, callback, options );
14637
+ return this._attachComponent( component );
13721
14638
  }
13722
14639
 
13723
14640
  /**
13724
14641
  * @method addMap2D
13725
- * @param {String} name Widget name
14642
+ * @param {String} name Component name
13726
14643
  * @param {Array} points
13727
14644
  * @param {Function} callback
13728
14645
  * @param {Object} options:
13729
14646
  */
13730
14647
 
13731
14648
  addMap2D( name, points, callback, options = { } ) {
13732
- const widget = new LX.Map2D( name, points, callback, options );
13733
- return this._attachWidget( widget );
14649
+ const component = new LX.Map2D( name, points, callback, options );
14650
+ return this._attachComponent( component );
14651
+ }
14652
+
14653
+ /**
14654
+ * @method addRate
14655
+ * @param {String} name Component name
14656
+ * @param {Number} value
14657
+ * @param {Function} callback
14658
+ * @param {Object} options:
14659
+ */
14660
+
14661
+ addRate( name, value, callback, options = { } ) {
14662
+ const component = new LX.Rate( name, value, callback, options );
14663
+ return this._attachComponent( component );
13734
14664
  }
13735
14665
  }
13736
14666
 
@@ -13765,7 +14695,7 @@ class Branch {
13765
14695
 
13766
14696
  this.closed = options.closed ?? false;
13767
14697
  this.root = root;
13768
- this.widgets = [];
14698
+ this.components = [];
13769
14699
 
13770
14700
  // Create element
13771
14701
  var title = document.createElement( 'div' );
@@ -13836,8 +14766,8 @@ class Branch {
13836
14766
  _onMakeFloating() {
13837
14767
 
13838
14768
  const dialog = new LX.Dialog( this.name, p => {
13839
- // add widgets
13840
- for( let w of this.widgets )
14769
+ // Add components
14770
+ for( let w of this.components )
13841
14771
  {
13842
14772
  p.root.appendChild( w.root );
13843
14773
  }
@@ -13848,7 +14778,7 @@ class Branch {
13848
14778
 
13849
14779
  dialog.branchData = {
13850
14780
  name: this.name,
13851
- widgets: this.widgets,
14781
+ components: this.components,
13852
14782
  closed: this.closed,
13853
14783
  panel: this.panel,
13854
14784
  childIndex
@@ -13860,7 +14790,7 @@ class Branch {
13860
14790
  _addBranchSeparator() {
13861
14791
 
13862
14792
  const element = document.createElement('div');
13863
- element.className = "lexwidgetseparator";
14793
+ element.className = "lexcomponentseparator";
13864
14794
  element.style.width = "100%";
13865
14795
  element.style.background = "none";
13866
14796
 
@@ -13913,7 +14843,7 @@ class Branch {
13913
14843
 
13914
14844
  function innerMouseUp(e)
13915
14845
  {
13916
- that._updateWidgets();
14846
+ that._updateComponents();
13917
14847
 
13918
14848
  line.style.height = "0px";
13919
14849
 
@@ -13926,15 +14856,15 @@ class Branch {
13926
14856
  this.content.appendChild( element );
13927
14857
  }
13928
14858
 
13929
- _updateWidgets() {
14859
+ _updateComponents() {
13930
14860
 
13931
14861
  var size = this.grabber.style.marginLeft;
13932
14862
 
13933
- // Update sizes of widgets inside
13934
- for( let i = 0; i < this.widgets.length; i++ )
14863
+ // Update sizes of components inside
14864
+ for( let i = 0; i < this.components.length; i++ )
13935
14865
  {
13936
- let widget = this.widgets[ i ];
13937
- const element = widget.root;
14866
+ let component = this.components[ i ];
14867
+ const element = component.root;
13938
14868
 
13939
14869
  if( element.children.length < 2 )
13940
14870
  {
@@ -13947,10 +14877,10 @@ class Branch {
13947
14877
  name.style.width = size;
13948
14878
  name.style.minWidth = size;
13949
14879
 
13950
- switch( widget.type )
14880
+ switch( component.type )
13951
14881
  {
13952
- case LX.Widget.CUSTOM:
13953
- case LX.Widget.ARRAY:
14882
+ case LX.BaseComponent.CUSTOM:
14883
+ case LX.BaseComponent.ARRAY:
13954
14884
  continue;
13955
14885
  }
13956
14886
  value.style.width = "-moz-calc( 100% - " + size + " )";
@@ -14264,7 +15194,15 @@ class Menubar {
14264
15194
  {
14265
15195
  const data = buttons[ i ];
14266
15196
  const title = data.title;
14267
- 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" } );
15197
+ const button = new LX.Button( title, data.label, data.callback, {
15198
+ title,
15199
+ buttonClass: "bg-none",
15200
+ disabled: data.disabled,
15201
+ icon: data.icon,
15202
+ hideName: true,
15203
+ swap: data.swap,
15204
+ iconPosition: "start"
15205
+ } );
14268
15206
  this.buttonContainer.appendChild( button.root );
14269
15207
 
14270
15208
  if( title )
@@ -14838,9 +15776,21 @@ class Sidebar {
14838
15776
  LX.asTooltip( itemDom, key, { side: "right", offset: 16, active: false } );
14839
15777
  }
14840
15778
 
14841
- let itemName = document.createElement( 'a' );
14842
- itemName.innerHTML = key;
14843
- itemDom.appendChild( itemName );
15779
+ LX.makeElement( 'a', "grid-column-start-2", key, itemDom );
15780
+
15781
+ if( options.swap )
15782
+ {
15783
+ itemDom.classList.add( "swap", "inline-grid" );
15784
+ itemDom.querySelector( "a" ).classList.add( "swap-off" );
15785
+
15786
+ const input = document.createElement( "input" );
15787
+ input.className = "p-0 border-0";
15788
+ input.type = "checkbox";
15789
+ itemDom.prepend( input );
15790
+
15791
+ const swapIcon = LX.makeIcon( options.swap, { iconClass: "lexsidebarentryicon swap-on" } );
15792
+ itemDom.appendChild( swapIcon );
15793
+ }
14844
15794
 
14845
15795
  if( options.content )
14846
15796
  {
@@ -14869,8 +15819,14 @@ class Sidebar {
14869
15819
  item.checkbox.set( item.value, true );
14870
15820
  }
14871
15821
 
15822
+ if( options.swap && !( e.target instanceof HTMLInputElement ) )
15823
+ {
15824
+ const swapInput = itemDom.querySelector( "input" );
15825
+ swapInput.checked = !swapInput.checked;
15826
+ }
15827
+
14872
15828
  // Manage selected
14873
- if( this.displaySelected )
15829
+ if( this.displaySelected && !options.skipSelection )
14874
15830
  {
14875
15831
  this.root.querySelectorAll(".lexsidebarentry").forEach( e => e.classList.remove( 'selected' ) );
14876
15832
  entry.classList.add( "selected" );
@@ -14952,6 +15908,14 @@ class Sidebar {
14952
15908
 
14953
15909
  subentry.className = "lexsidebarentry";
14954
15910
  subentry.id = subkey;
15911
+
15912
+ if( suboptions.content )
15913
+ {
15914
+ const parentContainer = LX.makeElement( "div" );
15915
+ parentContainer.appendChild( suboptions.content );
15916
+ subentry.appendChild( parentContainer );
15917
+ }
15918
+
14955
15919
  subentryContainer.appendChild( subentry );
14956
15920
 
14957
15921
  subentry.addEventListener("click", (e) => {
@@ -14960,10 +15924,10 @@ class Sidebar {
14960
15924
  if( f ) f.call( this, subkey, subentry, e );
14961
15925
 
14962
15926
  // Manage selected
14963
- if( this.displaySelected )
15927
+ if( this.displaySelected && !suboptions.skipSelection )
14964
15928
  {
14965
15929
  this.root.querySelectorAll(".lexsidebarentry").forEach( e => e.classList.remove( 'selected' ) );
14966
- entry.classList.add( "selected" );
15930
+ subentry.classList.add( "selected" );
14967
15931
  }
14968
15932
  });
14969
15933
  }
@@ -14982,8 +15946,8 @@ class AssetViewEvent {
14982
15946
  static ASSET_RENAMED = 3;
14983
15947
  static ASSET_CLONED = 4;
14984
15948
  static ASSET_DBLCLICKED = 5;
14985
- static ENTER_FOLDER = 6;
14986
- static ASSET_CHECKED = 7;
15949
+ static ASSET_CHECKED = 6;
15950
+ static ENTER_FOLDER = 7;
14987
15951
 
14988
15952
  constructor( type, item, value ) {
14989
15953
  this.type = type || LX.TreeEvent.NONE;
@@ -15001,8 +15965,8 @@ class AssetViewEvent {
15001
15965
  case AssetViewEvent.ASSET_RENAMED: return "assetview_event_renamed";
15002
15966
  case AssetViewEvent.ASSET_CLONED: return "assetview_event_cloned";
15003
15967
  case AssetViewEvent.ASSET_DBLCLICKED: return "assetview_event_dblclicked";
15004
- case AssetViewEvent.ENTER_FOLDER: return "assetview_event_enter_folder";
15005
15968
  case AssetViewEvent.ASSET_CHECKED: return "assetview_event_checked";
15969
+ case AssetViewEvent.ENTER_FOLDER: return "assetview_event_enter_folder";
15006
15970
  }
15007
15971
  }
15008
15972
  }
@@ -15015,8 +15979,12 @@ LX.AssetViewEvent = AssetViewEvent;
15015
15979
 
15016
15980
  class AssetView {
15017
15981
 
15018
- static LAYOUT_CONTENT = 0;
15982
+ static LAYOUT_GRID = 0;
15019
15983
  static LAYOUT_LIST = 1;
15984
+
15985
+ static CONTENT_SORT_ASC = 0;
15986
+ static CONTENT_SORT_DESC = 1;
15987
+
15020
15988
  static MAX_PAGE_ELEMENTS = 50;
15021
15989
 
15022
15990
  /**
@@ -15025,7 +15993,8 @@ class AssetView {
15025
15993
  constructor( options = {} ) {
15026
15994
 
15027
15995
  this.rootPath = "https://raw.githubusercontent.com/jxarco/lexgui.js/master/";
15028
- this.layout = options.layout ?? AssetView.LAYOUT_CONTENT;
15996
+ this.layout = options.layout ?? AssetView.LAYOUT_GRID;
15997
+ this.sortMode = options.sortMode ?? AssetView.CONTENT_SORT_ASC;
15029
15998
  this.contentPage = 1;
15030
15999
 
15031
16000
  if( options.rootPath )
@@ -15143,9 +16112,9 @@ class AssetView {
15143
16112
  this.leftPanel.clear();
15144
16113
  }
15145
16114
 
15146
- if( this.rightPanel )
16115
+ if( this.toolsPanel )
15147
16116
  {
15148
- this.rightPanel.clear();
16117
+ this.toolsPanel.clear();
15149
16118
  }
15150
16119
  }
15151
16120
 
@@ -15257,7 +16226,7 @@ class AssetView {
15257
16226
  _setContentLayout( layoutMode ) {
15258
16227
 
15259
16228
  this.layout = layoutMode;
15260
-
16229
+ this.toolsPanel.refresh();
15261
16230
  this._refreshContent();
15262
16231
  }
15263
16232
 
@@ -15267,42 +16236,34 @@ class AssetView {
15267
16236
 
15268
16237
  _createContentPanel( area ) {
15269
16238
 
15270
- if( this.rightPanel )
16239
+ if( this.toolsPanel )
15271
16240
  {
15272
- this.rightPanel.clear();
16241
+ this.contentPanel.clear();
15273
16242
  }
15274
16243
  else
15275
16244
  {
15276
- this.rightPanel = area.addPanel({ className: 'lexassetcontentpanel flex flex-col overflow-hidden' });
16245
+ this.toolsPanel = area.addPanel({ className: 'flex flex-col overflow-hidden' });
16246
+ this.contentPanel = area.addPanel({ className: 'lexassetcontentpanel flex flex-col overflow-hidden' });
15277
16247
  }
15278
16248
 
15279
- const on_sort = ( value, event ) => {
15280
- const cmenu = LX.addContextMenu( "Sort by", event, c => {
15281
- c.add("Name", () => this._sortData('id') );
15282
- c.add("Type", () => this._sortData('type') );
15283
- c.add("");
15284
- c.add("Ascending", () => this._sortData() );
15285
- c.add("Descending", () => this._sortData(null, true) );
15286
- } );
15287
- const parent = this.parent.root.parentElement;
15288
- if( parent.classList.contains('lexdialog') )
15289
- {
15290
- cmenu.root.style.zIndex = (+getComputedStyle( parent ).zIndex) + 1;
15291
- }
16249
+ const _onSort = ( value, event ) => {
16250
+ new LX.DropdownMenu( event.target, [
16251
+ { name: "Name", icon: "ALargeSmall", callback: () => this._sortData( "id" ) },
16252
+ { name: "Type", icon: "Type", callback: () => this._sortData( "type" ) },
16253
+ null,
16254
+ { name: "Ascending", icon: "SortAsc", callback: () => this._sortData( null, AssetView.CONTENT_SORT_ASC ) },
16255
+ { name: "Descending", icon: "SortDesc", callback: () => this._sortData( null, AssetView.CONTENT_SORT_DESC ) }
16256
+ ], { side: "right", align: "start" });
15292
16257
  };
15293
16258
 
15294
- const on_change_view = ( value, event ) => {
15295
- const cmenu = LX.addContextMenu( "Layout", event, c => {
15296
- c.add("Content", () => this._setContentLayout( AssetView.LAYOUT_CONTENT ) );
15297
- c.add("");
15298
- c.add("List", () => this._setContentLayout( AssetView.LAYOUT_LIST ) );
15299
- } );
15300
- const parent = this.parent.root.parentElement;
15301
- if( parent.classList.contains('lexdialog') )
15302
- cmenu.root.style.zIndex = (+getComputedStyle( parent ).zIndex) + 1;
16259
+ const _onChangeView = ( value, event ) => {
16260
+ new LX.DropdownMenu( event.target, [
16261
+ { name: "Grid", icon: "LayoutGrid", callback: () => this._setContentLayout( AssetView.LAYOUT_GRID ) },
16262
+ { name: "List", icon: "LayoutList", callback: () => this._setContentLayout( AssetView.LAYOUT_LIST ) }
16263
+ ], { side: "right", align: "start" });
15303
16264
  };
15304
16265
 
15305
- const on_change_page = ( value, event ) => {
16266
+ const _onChangePage = ( value, event ) => {
15306
16267
  if( !this.allowNextPage )
15307
16268
  {
15308
16269
  return;
@@ -15318,64 +16279,71 @@ class AssetView {
15318
16279
  }
15319
16280
  };
15320
16281
 
15321
- this.rightPanel.sameLine();
15322
- this.rightPanel.addSelect( "Filter", this.allowedTypes, this.allowedTypes[ 0 ], v => this._refreshContent.call(this, null, v), { width: "30%", minWidth: "128px" } );
15323
- this.rightPanel.addText( null, this.searchValue ?? "", v => this._refreshContent.call(this, v, null), { placeholder: "Search assets.." } );
15324
- this.rightPanel.addButton( null, "", on_sort.bind(this), { title: "Sort", icon: "ArrowUpNarrowWide" } );
15325
- this.rightPanel.addButton( null, "", on_change_view.bind(this), { title: "View", icon: "GripHorizontal" } );
15326
- // Content Pages
15327
- this.rightPanel.addButton( null, "", on_change_page.bind(this, -1), { title: "Previous Page", icon: "ChevronsLeft", className: "ml-auto" } );
15328
- this.rightPanel.addButton( null, "", on_change_page.bind(this, 1), { title: "Next Page", icon: "ChevronsRight" } );
15329
- const textString = "Page " + this.contentPage + " / " + ((((this.currentData.length - 1) / AssetView.MAX_PAGE_ELEMENTS )|0) + 1);
15330
- this.rightPanel.addText(null, textString, null, {
15331
- inputClass: "nobg", disabled: true, signal: "@on_page_change", maxWidth: "16ch" }
15332
- );
15333
- this.rightPanel.endLine();
16282
+ this.toolsPanel.refresh = () => {
16283
+ this.toolsPanel.clear();
16284
+ this.toolsPanel.sameLine();
16285
+ this.toolsPanel.addSelect( "Filter", this.allowedTypes, this.filter ?? this.allowedTypes[ 0 ], v => {
16286
+ this._refreshContent( null, v );
16287
+ }, { width: "30%", minWidth: "128px" } );
16288
+ this.toolsPanel.addText( null, this.searchValue ?? "", v => this._refreshContent.call(this, v, null), { placeholder: "Search assets.." } );
16289
+ this.toolsPanel.addButton( null, "", _onSort.bind(this), { title: "Sort", tooltip: true, icon: ( this.sortMode === AssetView.CONTENT_SORT_ASC ) ? "SortAsc" : "SortDesc" } );
16290
+ this.toolsPanel.addButton( null, "", _onChangeView.bind(this), { title: "View", tooltip: true, icon: ( this.layout === AssetView.LAYOUT_GRID ) ? "LayoutGrid" : "LayoutList" } );
16291
+ // Content Pages
16292
+ this.toolsPanel.addButton( null, "", _onChangePage.bind(this, -1), { title: "Previous Page", icon: "ChevronsLeft", className: "ml-auto" } );
16293
+ this.toolsPanel.addButton( null, "", _onChangePage.bind(this, 1), { title: "Next Page", icon: "ChevronsRight" } );
16294
+ const textString = "Page " + this.contentPage + " / " + ((((this.currentData.length - 1) / AssetView.MAX_PAGE_ELEMENTS )|0) + 1);
16295
+ this.toolsPanel.addText(null, textString, null, {
16296
+ inputClass: "nobg", disabled: true, signal: "@on_page_change", maxWidth: "16ch" }
16297
+ );
15334
16298
 
15335
- if( !this.skipBrowser )
15336
- {
15337
- this.rightPanel.sameLine();
15338
- this.rightPanel.addComboButtons( null, [
15339
- {
15340
- value: "Left",
15341
- icon: "ArrowLeft",
15342
- callback: domEl => {
15343
- if(!this.prevData.length) return;
15344
- this.nextData.push( this.currentData );
15345
- this.currentData = this.prevData.pop();
15346
- this._refreshContent();
15347
- this._updatePath( this.currentData );
15348
- }
15349
- },
15350
- {
15351
- value: "Right",
15352
- icon: "ArrowRight",
15353
- callback: domEl => {
15354
- if(!this.nextData.length) return;
15355
- this.prevData.push( this.currentData );
15356
- this.currentData = this.nextData.pop();
15357
- this._refreshContent();
15358
- this._updatePath( this.currentData );
16299
+ if( !this.skipBrowser )
16300
+ {
16301
+ this.toolsPanel.addComboButtons( null, [
16302
+ {
16303
+ value: "Left",
16304
+ icon: "ArrowLeft",
16305
+ callback: domEl => {
16306
+ if(!this.prevData.length) return;
16307
+ this.nextData.push( this.currentData );
16308
+ this.currentData = this.prevData.pop();
16309
+ this._refreshContent();
16310
+ this._updatePath( this.currentData );
16311
+ }
16312
+ },
16313
+ {
16314
+ value: "Right",
16315
+ icon: "ArrowRight",
16316
+ callback: domEl => {
16317
+ if(!this.nextData.length) return;
16318
+ this.prevData.push( this.currentData );
16319
+ this.currentData = this.nextData.pop();
16320
+ this._refreshContent();
16321
+ this._updatePath( this.currentData );
16322
+ }
16323
+ },
16324
+ {
16325
+ value: "Refresh",
16326
+ icon: "Refresh",
16327
+ callback: domEl => { this._refreshContent(); }
15359
16328
  }
15360
- },
15361
- {
15362
- value: "Refresh",
15363
- icon: "Refresh",
15364
- callback: domEl => { this._refreshContent(); }
15365
- }
15366
- ], { noSelection: true } );
16329
+ ], { noSelection: true } );
15367
16330
 
15368
- this.rightPanel.addText(null, this.path.join('/'), null, {
15369
- inputClass: "nobg", disabled: true, signal: "@on_folder_change",
15370
- style: { fontWeight: "600", fontSize: "15px" }
15371
- });
16331
+ this.toolsPanel.addText(null, this.path.join('/'), null, {
16332
+ inputClass: "nobg", disabled: true, signal: "@on_folder_change",
16333
+ style: { fontWeight: "600", fontSize: "15px" }
16334
+ });
16335
+ }
15372
16336
 
15373
- this.rightPanel.endLine();
15374
- }
16337
+ this.toolsPanel.endLine();
16338
+ };
16339
+
16340
+ this.toolsPanel.refresh();
16341
+
16342
+ // Start content panel
15375
16343
 
15376
16344
  this.content = document.createElement('ul');
15377
16345
  this.content.className = "lexassetscontent";
15378
- this.rightPanel.root.appendChild(this.content);
16346
+ this.contentPanel.attach( this.content );
15379
16347
 
15380
16348
  this.content.addEventListener('dragenter', function( e ) {
15381
16349
  e.preventDefault();
@@ -15398,12 +16366,12 @@ class AssetView {
15398
16366
 
15399
16367
  _refreshContent( searchValue, filter ) {
15400
16368
 
15401
- const isContentLayout = ( this.layout == AssetView.LAYOUT_CONTENT ); // default
16369
+ const isGridLayout = ( this.layout == AssetView.LAYOUT_GRID ); // default
15402
16370
 
15403
16371
  this.filter = filter ?? ( this.filter ?? "None" );
15404
16372
  this.searchValue = searchValue ?? (this.searchValue ?? "");
15405
16373
  this.content.innerHTML = "";
15406
- this.content.className = (isContentLayout ? "lexassetscontent" : "lexassetscontent list");
16374
+ this.content.className = (isGridLayout ? "lexassetscontent" : "lexassetscontent list");
15407
16375
  let that = this;
15408
16376
 
15409
16377
  const _addItem = function(item) {
@@ -15426,7 +16394,7 @@ class AssetView {
15426
16394
 
15427
16395
  itemEl.addEventListener("mousemove", e => {
15428
16396
 
15429
- if( !isContentLayout )
16397
+ if( !isGridLayout )
15430
16398
  {
15431
16399
  return;
15432
16400
  }
@@ -15455,14 +16423,14 @@ class AssetView {
15455
16423
  });
15456
16424
 
15457
16425
  itemEl.addEventListener("mouseenter", () => {
15458
- if( isContentLayout )
16426
+ if( isGridLayout )
15459
16427
  {
15460
16428
  desc.style.display = "unset";
15461
16429
  }
15462
16430
  });
15463
16431
 
15464
16432
  itemEl.addEventListener("mouseleave", () => {
15465
- if( isContentLayout )
16433
+ if( isGridLayout )
15466
16434
  {
15467
16435
  setTimeout( () => {
15468
16436
  desc.style.display = "none";
@@ -15506,11 +16474,11 @@ class AssetView {
15506
16474
  let preview = null;
15507
16475
  const hasImage = item.src && (['png', 'jpg'].indexOf( LX.getExtension( item.src ) ) > -1 || item.src.includes("data:image/") ); // Support b64 image as src
15508
16476
 
15509
- if( hasImage || isFolder || !isContentLayout)
16477
+ if( hasImage || isFolder || !isGridLayout)
15510
16478
  {
15511
16479
  preview = document.createElement('img');
15512
16480
  let real_src = item.unknown_extension ? that.rootPath + "images/file.png" : (isFolder ? that.rootPath + "images/folder.png" : item.src);
15513
- preview.src = (isContentLayout || isFolder ? real_src : that.rootPath + "images/file.png");
16481
+ preview.src = (isGridLayout || isFolder ? real_src : that.rootPath + "images/file.png");
15514
16482
  itemEl.appendChild( preview );
15515
16483
  }
15516
16484
  else
@@ -15771,16 +16739,20 @@ class AssetView {
15771
16739
  }
15772
16740
  }
15773
16741
 
15774
- _sortData( sort_by, sort_descending = false ) {
16742
+ _sortData( sortBy, sortMode ) {
15775
16743
 
15776
- sort_by = sort_by ?? (this._lastSortBy ?? 'id');
15777
- this.currentData = this.currentData.sort( (a, b) => {
15778
- var r = sort_descending ? b[sort_by].localeCompare(a[sort_by]) : a[sort_by].localeCompare(b[sort_by]);
15779
- if(r == 0) r = sort_descending ? b['id'].localeCompare(a['id']) : a['id'].localeCompare(b['id']);
16744
+ sortBy = sortBy ?? ( this._lastSortBy ?? 'id' );
16745
+ sortMode = sortMode ?? this.sortMode;
16746
+ const sortDesc = ( sortMode === AssetView.CONTENT_SORT_DESC );
16747
+ this.currentData = this.currentData.sort( ( a, b ) => {
16748
+ var r = sortDesc ? b[ sortBy ].localeCompare( a[ sortBy ] ) : a[ sortBy ].localeCompare( b[ sortBy ] );
16749
+ if( r == 0 ) r = sortDesc ? b['id'].localeCompare( a['id'] ) : a[ 'id' ].localeCompare( b[ 'id' ] );
15780
16750
  return r;
15781
16751
  } );
15782
16752
 
15783
- this._lastSortBy = sort_by;
16753
+ this._lastSortBy = sortBy;
16754
+ this.sortMode = sortMode;
16755
+ this.toolsPanel.refresh();
15784
16756
  this._refreshContent();
15785
16757
  }
15786
16758
 
@@ -15855,6 +16827,8 @@ LX.AssetView = AssetView;
15855
16827
 
15856
16828
  class Tour {
15857
16829
 
16830
+ static ACTIVE_TOURS = [];
16831
+
15858
16832
  /**
15859
16833
  * @constructor Tour
15860
16834
  * @param {Array} steps
@@ -15877,11 +16851,21 @@ class Tour {
15877
16851
  this.verticalOffset = options.verticalOffset;
15878
16852
  this.radius = options.radius ?? 12;
15879
16853
 
15880
- this.tourContainer = LX.makeContainer( ["100%", "100%"], "tour-container" );
15881
- this.tourContainer.style.display = "none";
15882
- document.body.appendChild( this.tourContainer );
16854
+ this.tourContainer = document.querySelector( ".tour-container" );
16855
+ if( !this.tourContainer )
16856
+ {
16857
+ this.tourContainer = LX.makeContainer( ["100%", "100%"], "tour-container" );
16858
+ this.tourContainer.style.display = "none";
16859
+ document.body.appendChild( this.tourContainer );
15883
16860
 
15884
- this.tourMask = LX.makeContainer( ["100%", "100%"], "tour-mask" );
16861
+ window.addEventListener( "resize", () => {
16862
+
16863
+ for( const tour of Tour.ACTIVE_TOURS )
16864
+ {
16865
+ tour._showStep( 0 );
16866
+ }
16867
+ } );
16868
+ }
15885
16869
  }
15886
16870
 
15887
16871
  /**
@@ -15892,14 +16876,10 @@ class Tour {
15892
16876
 
15893
16877
  this.currentStep = 0;
15894
16878
 
15895
- if ( this.useModal )
15896
- {
15897
- this.tourMask.style.display = "block";
15898
- this.tourContainer.appendChild( this.tourMask );
15899
- }
15900
-
15901
16879
  this.tourContainer.style.display = "block";
15902
16880
 
16881
+ Tour.ACTIVE_TOURS.push( this );
16882
+
15903
16883
  this._showStep( 0 );
15904
16884
  }
15905
16885
 
@@ -15911,12 +16891,19 @@ class Tour {
15911
16891
 
15912
16892
  if( this.useModal )
15913
16893
  {
15914
- this.tourMask.style.display = "none";
15915
- this.tourContainer.removeChild( this.tourMask );
16894
+ this.tourMask.remove();
16895
+ this.tourMask = undefined;
15916
16896
  }
15917
16897
 
15918
16898
  this._popover?.destroy();
15919
16899
 
16900
+ const index = Tour.ACTIVE_TOURS.indexOf( this );
16901
+ if( index !== -1 )
16902
+ {
16903
+ Tour.ACTIVE_TOURS.splice( index, 1 );
16904
+ }
16905
+
16906
+ this.tourContainer.innerHTML = "";
15920
16907
  this.tourContainer.style.display = "none";
15921
16908
  }
15922
16909
 
@@ -15934,7 +16921,11 @@ class Tour {
15934
16921
  const prevStep = this.steps[ this.currentStep - 1 ];
15935
16922
  const nextStep = this.steps[ this.currentStep + 1 ];
15936
16923
 
15937
- this._generateMask( step.reference );
16924
+ if( this.useModal )
16925
+ {
16926
+ this._generateMask( step.reference );
16927
+ }
16928
+
15938
16929
  this._createHighlight( step, prevStep, nextStep );
15939
16930
  }
15940
16931
 
@@ -15942,7 +16933,10 @@ class Tour {
15942
16933
  // using a fullscreen SVG with "rect" elements
15943
16934
  _generateMask( reference ) {
15944
16935
 
15945
- this.tourMask.innerHTML = ""; // Clear previous content
16936
+ this.tourContainer.innerHTML = ""; // Clear previous content
16937
+
16938
+ this.tourMask = LX.makeContainer( ["100%", "100%"], "tour-mask" );
16939
+ this.tourContainer.appendChild( this.tourMask );
15946
16940
 
15947
16941
  const svg = document.createElementNS( "http://www.w3.org/2000/svg", "svg" );
15948
16942
  svg.style.width = "100%";
@@ -15972,7 +16966,7 @@ class Tour {
15972
16966
  const rect = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
15973
16967
  rect.setAttribute( "x", 0 );
15974
16968
  rect.setAttribute( "y", 0 );
15975
- rect.setAttribute( "width", boundingX - hOffset );
16969
+ rect.setAttribute( "width", Math.max( 0, boundingX - hOffset ) );
15976
16970
  rect.setAttribute( "height", window.innerHeight );
15977
16971
  rect.setAttribute( "stroke", "none" );
15978
16972
  clipPath.appendChild( rect );
@@ -15983,8 +16977,8 @@ class Tour {
15983
16977
  const rect = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
15984
16978
  rect.setAttribute( "x", boundingX - hOffset );
15985
16979
  rect.setAttribute( "y", 0 );
15986
- rect.setAttribute( "width", boundingWidth + hOffset * 2 );
15987
- rect.setAttribute( "height", boundingY - vOffset );
16980
+ rect.setAttribute( "width", Math.max( 0, boundingWidth + hOffset * 2 ) );
16981
+ rect.setAttribute( "height", Math.max( 0, boundingY - vOffset ) );
15988
16982
  rect.setAttribute( "stroke", "none" );
15989
16983
  clipPath.appendChild( rect );
15990
16984
  }
@@ -15994,8 +16988,8 @@ class Tour {
15994
16988
  const rect = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
15995
16989
  rect.setAttribute( "x", boundingX - hOffset );
15996
16990
  rect.setAttribute( "y", boundingY + boundingHeight + vOffset );
15997
- rect.setAttribute( "width", boundingWidth + hOffset * 2 );
15998
- rect.setAttribute( "height", window.innerHeight - boundingY - boundingHeight - vOffset );
16991
+ rect.setAttribute( "width", Math.max( 0, boundingWidth + hOffset * 2 ) );
16992
+ rect.setAttribute( "height", Math.max( 0, window.innerHeight - boundingY - boundingHeight - vOffset ) );
15999
16993
  rect.setAttribute( "stroke", "none" );
16000
16994
  clipPath.appendChild( rect );
16001
16995
  }
@@ -16005,8 +16999,8 @@ class Tour {
16005
16999
  const rect = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
16006
17000
  rect.setAttribute( "x", boundingX + boundingWidth + hOffset );
16007
17001
  rect.setAttribute( "y", 0 );
16008
- rect.setAttribute( "width", window.innerWidth - boundingX - boundingWidth );
16009
- rect.setAttribute( "height", window.innerHeight );
17002
+ rect.setAttribute( "width", Math.max( 0, window.innerWidth - boundingX - boundingWidth ) );
17003
+ rect.setAttribute( "height", Math.max( 0, window.innerHeight ) );
16010
17004
  rect.setAttribute( "stroke", "none" );
16011
17005
  clipPath.appendChild( rect );
16012
17006
  }