lexgui 0.6.8 → 0.6.10

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.
package/build/lexgui.js CHANGED
@@ -14,7 +14,7 @@ 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.8",
17
+ version: "0.6.10",
18
18
  ready: false,
19
19
  components: [], // Specific pre-build components
20
20
  signals: {}, // Events and triggers
@@ -318,7 +318,7 @@ function _createCommandbar( root )
318
318
 
319
319
  const _propagateAdd = ( item, filter, path, skipPropagation ) => {
320
320
 
321
- if( !item )
321
+ if( !item || ( item.constructor != Object ) )
322
322
  {
323
323
  return;
324
324
  }
@@ -1537,7 +1537,7 @@ class ColorPicker {
1537
1537
  copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "none";
1538
1538
 
1539
1539
  LX.doAsync( () => {
1540
- copyButtonWidget.root.swap( true );
1540
+ copyButtonWidget.swap( true );
1541
1541
  copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "auto";
1542
1542
  }, 3000 );
1543
1543
 
@@ -5077,7 +5077,7 @@ LX.makeCodeSnippet = makeCodeSnippet;
5077
5077
  * @param {Array} keys
5078
5078
  * @param {String} extraClass
5079
5079
  */
5080
- function makeKbd( keys, extraClass = "" )
5080
+ function makeKbd( keys, useSpecialKeys = true, extraClass = "" )
5081
5081
  {
5082
5082
  const specialKeys = {
5083
5083
  "Ctrl": '⌃',
@@ -5099,7 +5099,7 @@ function makeKbd( keys, extraClass = "" )
5099
5099
 
5100
5100
  for( const k of keys )
5101
5101
  {
5102
- LX.makeContainer( ["auto", "auto"], "self-center text-xs fg-secondary select-none", specialKeys[ k ] ?? k, kbd );
5102
+ LX.makeContainer( ["auto", "auto"], "self-center text-xs fg-secondary select-none " + extraClass, useSpecialKeys ? specialKeys[ k ] ?? k : k, kbd );
5103
5103
  }
5104
5104
 
5105
5105
  return kbd;
@@ -6020,6 +6020,176 @@ LX.drawSpline = drawSpline;
6020
6020
 
6021
6021
  // area.js @jxarco
6022
6022
 
6023
+ class AreaOverlayButtons {
6024
+
6025
+ /**
6026
+ * @constructor AreaOverlayButtons
6027
+ */
6028
+
6029
+ constructor( area, buttonsArray, options = {} ) {
6030
+
6031
+ this.area = area;
6032
+ this.options = options;
6033
+
6034
+ this.buttons = {};
6035
+
6036
+ this._buildButtons( buttonsArray, options );
6037
+ }
6038
+
6039
+ _buildButtons( buttonsArray, options ) {
6040
+
6041
+ options.className = "lexoverlaybuttons";
6042
+
6043
+ let overlayPanel = this.area.addPanel( options );
6044
+ let overlayGroup = null;
6045
+
6046
+ const container = document.createElement( "div" );
6047
+ container.className = "lexoverlaybuttonscontainer";
6048
+ container.appendChild( overlayPanel.root );
6049
+ this.area.attach( container );
6050
+
6051
+ const float = options.float;
6052
+ let floatClass = "";
6053
+
6054
+ if( float )
6055
+ {
6056
+ for( let i = 0; i < float.length; i++ )
6057
+ {
6058
+ const t = float[ i ];
6059
+ switch( t )
6060
+ {
6061
+ case 'h': break;
6062
+ case 'v': floatClass += " vertical"; break;
6063
+ case 't': break;
6064
+ case 'm': floatClass += " middle"; break;
6065
+ case 'b': floatClass += " bottom"; break;
6066
+ case 'l': break;
6067
+ case 'c': floatClass += " center"; break;
6068
+ case 'r': floatClass += " right"; break;
6069
+ }
6070
+ }
6071
+
6072
+ container.className += ` ${ floatClass }`;
6073
+ }
6074
+
6075
+ const _addButton = ( b, group, last ) => {
6076
+
6077
+ const _options = {
6078
+ width: "auto",
6079
+ selectable: b.selectable,
6080
+ selected: b.selected,
6081
+ icon: b.icon,
6082
+ img: b.img,
6083
+ className: b.class ?? "",
6084
+ title: b.name,
6085
+ overflowContainerX: overlayPanel.root,
6086
+ swap: b.swap
6087
+ };
6088
+
6089
+ if( group )
6090
+ {
6091
+ if( !overlayGroup )
6092
+ {
6093
+ overlayGroup = document.createElement('div');
6094
+ overlayGroup.className = "lexoverlaygroup";
6095
+ overlayPanel.queuedContainer = overlayGroup;
6096
+ }
6097
+
6098
+ _options.parent = overlayGroup;
6099
+ }
6100
+
6101
+ let callback = b.callback;
6102
+ let widget = null;
6103
+
6104
+ if( b.options )
6105
+ {
6106
+ widget = overlayPanel.addSelect( null, b.options, b.value ?? b.name, callback, _options );
6107
+ }
6108
+ else
6109
+ {
6110
+ widget = overlayPanel.addButton( null, b.name, function( value, event ) {
6111
+ if( b.selectable )
6112
+ {
6113
+ if( b.group )
6114
+ {
6115
+ let _prev = b.selected;
6116
+ b.group.forEach( sub => sub.selected = false );
6117
+ b.selected = !_prev;
6118
+ }
6119
+ else
6120
+ {
6121
+ b.selected = !b.selected;
6122
+ }
6123
+ }
6124
+
6125
+ if( callback )
6126
+ {
6127
+ callback( value, event, widget.root );
6128
+ }
6129
+
6130
+ }, _options );
6131
+ }
6132
+
6133
+ this.buttons[ b.name ] = widget;
6134
+
6135
+ // ends the group
6136
+ if( overlayGroup && last )
6137
+ {
6138
+ overlayPanel.root.appendChild( overlayGroup );
6139
+ overlayGroup = null;
6140
+ overlayPanel.clearQueue();
6141
+ }
6142
+ };
6143
+
6144
+ const _refreshPanel = function() {
6145
+
6146
+ overlayPanel.clear();
6147
+
6148
+ for( let b of buttonsArray )
6149
+ {
6150
+ if( b === null )
6151
+ {
6152
+ // Add a separator
6153
+ const separator = document.createElement("div");
6154
+ separator.className = "lexoverlayseparator" + floatClass;
6155
+ overlayPanel.root.appendChild( separator );
6156
+ continue;
6157
+ }
6158
+
6159
+ if( b.constructor === Array )
6160
+ {
6161
+ for( let i = 0; i < b.length; ++i )
6162
+ {
6163
+ let sub = b[ i ];
6164
+ sub.group = b;
6165
+ _addButton( sub, true, i == ( b.length - 1 ) );
6166
+ }
6167
+ }
6168
+ else
6169
+ {
6170
+ _addButton( b );
6171
+ }
6172
+ }
6173
+
6174
+ // Add floating info
6175
+ if( float )
6176
+ {
6177
+ var height = 0;
6178
+ overlayPanel.root.childNodes.forEach( c => { height += c.offsetHeight; } );
6179
+
6180
+ if( container.className.includes( "middle" ) )
6181
+ {
6182
+ container.style.top = "-moz-calc( 50% - " + (height * 0.5) + "px )";
6183
+ container.style.top = "-webkit-calc( 50% - " + (height * 0.5) + "px )";
6184
+ container.style.top = "calc( 50% - " + (height * 0.5) + "px )";
6185
+ }
6186
+ }
6187
+ };
6188
+
6189
+ _refreshPanel();
6190
+ }
6191
+ }
6192
+
6023
6193
  class Area {
6024
6194
 
6025
6195
  /**
@@ -6785,8 +6955,7 @@ class Area {
6785
6955
  // Add to last split section if area has been split
6786
6956
  if( this.sections.length )
6787
6957
  {
6788
- this.sections[ 1 ].addOverlayButtons( buttons, options );
6789
- return;
6958
+ return this.sections[ 1 ].addOverlayButtons( buttons, options );
6790
6959
  }
6791
6960
 
6792
6961
  console.assert( buttons.constructor == Array && buttons.length );
@@ -6794,140 +6963,10 @@ class Area {
6794
6963
  // Set area to relative to use local position
6795
6964
  this.root.style.position = "relative";
6796
6965
 
6797
- options.className = "lexoverlaybuttons";
6798
-
6799
- let overlayPanel = this.addPanel( options );
6800
- let overlayGroup = null;
6801
-
6802
- const container = document.createElement("div");
6803
- container.className = "lexoverlaybuttonscontainer";
6804
- container.appendChild( overlayPanel.root );
6805
- this.attach( container );
6806
-
6807
- const float = options.float;
6966
+ // Reset if already exists
6967
+ this.overlayButtons = new AreaOverlayButtons( this, buttons, options );
6808
6968
 
6809
- if( float )
6810
- {
6811
- for( let i = 0; i < float.length; i++ )
6812
- {
6813
- const t = float[ i ];
6814
- switch( t )
6815
- {
6816
- case 'h': break;
6817
- case 'v': container.className += " vertical"; break;
6818
- case 't': break;
6819
- case 'm': container.className += " middle"; break;
6820
- case 'b': container.className += " bottom"; break;
6821
- case 'l': break;
6822
- case 'c': container.className += " center"; break;
6823
- case 'r': container.className += " right"; break;
6824
- }
6825
- }
6826
- }
6827
-
6828
- const _addButton = function( b, group, last ) {
6829
-
6830
- const _options = {
6831
- width: "auto",
6832
- selectable: b.selectable,
6833
- selected: b.selected,
6834
- icon: b.icon,
6835
- img: b.img,
6836
- className: b.class ?? "",
6837
- title: b.name,
6838
- overflowContainerX: overlayPanel.root,
6839
- swap: b.swap
6840
- };
6841
-
6842
- if( group )
6843
- {
6844
- if( !overlayGroup )
6845
- {
6846
- overlayGroup = document.createElement('div');
6847
- overlayGroup.className = "lexoverlaygroup";
6848
- overlayPanel.queuedContainer = overlayGroup;
6849
- }
6850
-
6851
- _options.parent = overlayGroup;
6852
- }
6853
-
6854
- let callback = b.callback;
6855
-
6856
- if( b.options )
6857
- {
6858
- overlayPanel.addSelect( null, b.options, b.name, callback, _options );
6859
- }
6860
- else
6861
- {
6862
- const button = overlayPanel.addButton( null, b.name, function( value, event ) {
6863
- if( b.selectable )
6864
- {
6865
- if( b.group )
6866
- {
6867
- let _prev = b.selected;
6868
- b.group.forEach( sub => sub.selected = false );
6869
- b.selected = !_prev;
6870
- }
6871
- else
6872
- {
6873
- b.selected = !b.selected;
6874
- }
6875
- }
6876
-
6877
- if( callback )
6878
- {
6879
- callback( value, event, button.root );
6880
- }
6881
-
6882
- }, _options );
6883
- }
6884
-
6885
- // ends the group
6886
- if( overlayGroup && last )
6887
- {
6888
- overlayPanel.root.appendChild( overlayGroup );
6889
- overlayGroup = null;
6890
- overlayPanel.clearQueue();
6891
- }
6892
- };
6893
-
6894
- const _refreshPanel = function() {
6895
-
6896
- overlayPanel.clear();
6897
-
6898
- for( let b of buttons )
6899
- {
6900
- if( b.constructor === Array )
6901
- {
6902
- for( let i = 0; i < b.length; ++i )
6903
- {
6904
- let sub = b[ i ];
6905
- sub.group = b;
6906
- _addButton(sub, true, i == ( b.length - 1 ));
6907
- }
6908
- }
6909
- else
6910
- {
6911
- _addButton( b );
6912
- }
6913
- }
6914
-
6915
- // Add floating info
6916
- if( float )
6917
- {
6918
- var height = 0;
6919
- overlayPanel.root.childNodes.forEach( c => { height += c.offsetHeight; } );
6920
-
6921
- if( container.className.includes( "middle" ) )
6922
- {
6923
- container.style.top = "-moz-calc( 50% - " + (height * 0.5) + "px )";
6924
- container.style.top = "-webkit-calc( 50% - " + (height * 0.5) + "px )";
6925
- container.style.top = "calc( 50% - " + (height * 0.5) + "px )";
6926
- }
6927
- }
6928
- };
6929
-
6930
- _refreshPanel();
6969
+ return this.overlayButtons;
6931
6970
  }
6932
6971
 
6933
6972
  /**
@@ -7428,8 +7467,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7428
7467
  if( customWidgetsDom ) customWidgetsDom.remove();
7429
7468
 
7430
7469
  container = document.createElement('div');
7431
- container.className = "lexcustomcontainer";
7432
- container.style.width = "100%";
7470
+ container.className = "lexcustomcontainer w-full";
7433
7471
  element.appendChild( container );
7434
7472
  element.dataset["opened"] = false;
7435
7473
 
@@ -7623,9 +7661,9 @@ class NodeTree {
7623
7661
 
7624
7662
  if( this.options.onlyFolders )
7625
7663
  {
7626
- let has_folders = false;
7627
- node.children.forEach( c => has_folders |= (c.type == 'folder') );
7628
- isParent = !!has_folders;
7664
+ let hasFolders = false;
7665
+ node.children.forEach( c => hasFolders |= (c.type == 'folder') );
7666
+ isParent = !!hasFolders;
7629
7667
  }
7630
7668
 
7631
7669
  let item = document.createElement('li');
@@ -7790,23 +7828,15 @@ class NodeTree {
7790
7828
  } );
7791
7829
 
7792
7830
  event.panel.add( "Delete", { callback: () => {
7793
- // It's the root node
7794
- if( !node.parent )
7795
- {
7796
- return;
7797
- }
7798
7831
 
7799
- if( that.onevent )
7832
+ const ok = that.deleteNode( node );
7833
+
7834
+ if( ok && that.onevent )
7800
7835
  {
7801
7836
  const event = new LX.TreeEvent( LX.TreeEvent.NODE_DELETED, node, e );
7802
7837
  that.onevent( event );
7803
7838
  }
7804
7839
 
7805
- // Delete nodes now
7806
- let childs = node.parent.children;
7807
- const index = childs.indexOf( node );
7808
- childs.splice( index, 1 );
7809
-
7810
7840
  this.refresh();
7811
7841
  } } );
7812
7842
  }
@@ -7823,23 +7853,26 @@ class NodeTree {
7823
7853
 
7824
7854
  if( e.key == "Delete" )
7825
7855
  {
7826
- // Send event now so we have the info in selected array..
7827
- if( that.onevent )
7856
+ const nodesDeleted = [];
7857
+
7858
+ for( let _node of this.selected )
7828
7859
  {
7829
- const event = new LX.TreeEvent( LX.TreeEvent.NODE_DELETED, this.selected.length > 1 ? this.selected : node, e );
7830
- event.multiple = this.selected.length > 1;
7831
- that.onevent( event );
7860
+ if( that.deleteNode( _node ) )
7861
+ {
7862
+ nodesDeleted.push( _node );
7863
+ }
7832
7864
  }
7833
7865
 
7834
- // Delete nodes now
7835
- for( let _node of this.selected )
7866
+ // Send event now so we have the info in selected array..
7867
+ if( nodesDeleted.length && that.onevent )
7836
7868
  {
7837
- let childs = _node.parent.children;
7838
- const index = childs.indexOf( _node );
7839
- childs.splice( index, 1 );
7869
+ const event = new LX.TreeEvent( LX.TreeEvent.NODE_DELETED, nodesDeleted.length > 1 ? nodesDeleted : node, e );
7870
+ event.multiple = nodesDeleted.length > 1;
7871
+ that.onevent( event );
7840
7872
  }
7841
7873
 
7842
7874
  this.selected.length = 0;
7875
+
7843
7876
  this.refresh();
7844
7877
  }
7845
7878
  else if( e.key == "ArrowUp" || e.key == "ArrowDown" ) // Unique or zero selected
@@ -8108,6 +8141,35 @@ class NodeTree {
8108
8141
  this.selected = [ el.treeData ];
8109
8142
  el.focus();
8110
8143
  }
8144
+
8145
+ deleteNode( node ) {
8146
+
8147
+ const dataAsArray = ( this.data.constructor === Array );
8148
+
8149
+ // Can be either Array or Object type data
8150
+ if( node.parent )
8151
+ {
8152
+ let childs = node.parent.children;
8153
+ const index = childs.indexOf( node );
8154
+ childs.splice( index, 1 );
8155
+ }
8156
+ else
8157
+ {
8158
+ if( dataAsArray )
8159
+ {
8160
+ const index = this.data.indexOf( node );
8161
+ console.assert( index > -1, "NodeTree: Can't delete root node " + node.id + " from data array!" );
8162
+ this.data.splice( index, 1 );
8163
+ }
8164
+ else
8165
+ {
8166
+ console.warn( "NodeTree: Can't delete root node from object data!" );
8167
+ return false;
8168
+ }
8169
+ }
8170
+
8171
+ return true;
8172
+ }
8111
8173
  }
8112
8174
 
8113
8175
  LX.NodeTree = NodeTree;
@@ -8427,14 +8489,15 @@ class Button extends Widget {
8427
8489
  super( Widget.BUTTON, name, null, options );
8428
8490
 
8429
8491
  this.onGetValue = () => {
8430
- return wValue.querySelector( "input" )?.checked;
8492
+ const swapInput = wValue.querySelector( "input" );
8493
+ return swapInput ? swapInput.checked : value
8431
8494
  };
8432
8495
 
8433
8496
  this.onSetValue = ( newValue, skipCallback, event ) => {
8434
8497
 
8435
8498
  if( ( options.swap ?? false ) )
8436
8499
  {
8437
- this.root.setState( newValue, skipCallback );
8500
+ this.setState( newValue, skipCallback );
8438
8501
  return;
8439
8502
  }
8440
8503
 
@@ -8464,6 +8527,30 @@ class Button extends Widget {
8464
8527
  wValue.style.width = `calc( 100% - ${ realNameWidth })`;
8465
8528
  };
8466
8529
 
8530
+ // In case of swap, set if a change has to be performed
8531
+ this.setState = function( v, skipCallback ) {
8532
+ const swapInput = wValue.querySelector( "input" );
8533
+
8534
+ if( swapInput )
8535
+ {
8536
+ swapInput.checked = v;
8537
+ }
8538
+ else if( options.selectable )
8539
+ {
8540
+ if( options.parent )
8541
+ {
8542
+ options.parent.querySelectorAll(".lexbutton.selected").forEach( b => { if( b == wValue ) return; b.classList.remove( "selected" ); } );
8543
+ }
8544
+
8545
+ wValue.classList.toggle( "selected", v );
8546
+ }
8547
+
8548
+ if( !skipCallback )
8549
+ {
8550
+ this._trigger( new LX.IEvent( name, swapInput ? swapInput.checked : ( options.selectable ? v : value ), null ), callback );
8551
+ }
8552
+ };
8553
+
8467
8554
  var wValue = document.createElement( 'button' );
8468
8555
  wValue.title = options.tooltip ? "" : ( options.title ?? "" );
8469
8556
  wValue.className = "lexbutton p-1 " + ( options.buttonClass ?? "" );
@@ -8475,18 +8562,38 @@ class Button extends Widget {
8475
8562
  wValue.classList.add( "selected" );
8476
8563
  }
8477
8564
 
8478
- if( options.icon )
8479
- {
8480
- const icon = LX.makeIcon( options.icon );
8481
- wValue.prepend( icon );
8482
- wValue.classList.add( "justify-center" );
8483
- }
8484
- else if( options.img )
8565
+ if( options.img )
8485
8566
  {
8486
8567
  let img = document.createElement( 'img' );
8487
8568
  img.src = options.img;
8488
8569
  wValue.prepend( img );
8489
8570
  }
8571
+ else if( options.icon )
8572
+ {
8573
+ const icon = LX.makeIcon( options.icon, { iconClass: options.iconClass, svgClass: options.svgClass } );
8574
+ const iconPosition = options.iconPosition ?? "cover";
8575
+
8576
+ // Default
8577
+ if( iconPosition == "cover" || ( options.swap !== undefined ) )
8578
+ {
8579
+ wValue.prepend( icon );
8580
+ }
8581
+ else
8582
+ {
8583
+ wValue.innerHTML = `<span>${ ( value || "" ) }</span>`;
8584
+
8585
+ if( iconPosition == "start" )
8586
+ {
8587
+ wValue.querySelector( "span" ).prepend( icon );
8588
+ }
8589
+ else // "end"
8590
+ {
8591
+ wValue.querySelector( "span" ).appendChild( icon );
8592
+ }
8593
+ }
8594
+
8595
+ wValue.classList.add( "justify-center" );
8596
+ }
8490
8597
  else
8491
8598
  {
8492
8599
  wValue.innerHTML = `<span>${ ( value || "" ) }</span>`;
@@ -8534,7 +8641,7 @@ class Button extends Widget {
8534
8641
  const swapIcon = LX.makeIcon( options.swap, { iconClass: "swap-on" } );
8535
8642
  wValue.appendChild( swapIcon );
8536
8643
 
8537
- this.root.swap = function( skipCallback ) {
8644
+ this.swap = function( skipCallback ) {
8538
8645
  const swapInput = wValue.querySelector( "input" );
8539
8646
  swapInput.checked = !swapInput.checked;
8540
8647
  if( !skipCallback )
@@ -8542,19 +8649,10 @@ class Button extends Widget {
8542
8649
  trigger.click();
8543
8650
  }
8544
8651
  };
8545
-
8546
- // Set if swap has to be performed
8547
- this.root.setState = function( v, skipCallback ) {
8548
- const swapInput = wValue.querySelector( "input" );
8549
- swapInput.checked = v;
8550
- if( !skipCallback )
8551
- {
8552
- trigger.click();
8553
- }
8554
- };
8555
8652
  }
8556
8653
 
8557
8654
  trigger.addEventListener( "click", e => {
8655
+ let isSelected;
8558
8656
  if( options.selectable )
8559
8657
  {
8560
8658
  if( options.parent )
@@ -8562,7 +8660,7 @@ class Button extends Widget {
8562
8660
  options.parent.querySelectorAll(".lexbutton.selected").forEach( b => { if( b == wValue ) return; b.classList.remove( "selected" ); } );
8563
8661
  }
8564
8662
 
8565
- wValue.classList.toggle('selected');
8663
+ isSelected = wValue.classList.toggle('selected');
8566
8664
  }
8567
8665
 
8568
8666
  if( options.fileInput )
@@ -8572,7 +8670,7 @@ class Button extends Widget {
8572
8670
  else
8573
8671
  {
8574
8672
  const swapInput = wValue.querySelector( "input" );
8575
- this._trigger( new LX.IEvent( name, swapInput?.checked ?? value, e ), callback );
8673
+ this._trigger( new LX.IEvent( name, swapInput?.checked ?? ( options.selectable ? isSelected : value ), e ), callback );
8576
8674
  }
8577
8675
  });
8578
8676
 
@@ -8942,8 +9040,8 @@ class Select extends Widget {
8942
9040
  value = newValue;
8943
9041
 
8944
9042
  let item = null;
8945
- const options = listOptions.childNodes;
8946
- options.forEach( e => {
9043
+ const listOptionsNodes = listOptions.childNodes;
9044
+ listOptionsNodes.forEach( e => {
8947
9045
  e.classList.remove( "selected" );
8948
9046
  if( e.getAttribute( "value" ) == newValue )
8949
9047
  {
@@ -8963,6 +9061,23 @@ class Select extends Widget {
8963
9061
  list.refresh( filteredOptions );
8964
9062
  }
8965
9063
 
9064
+ // Update suboptions menu
9065
+ const suboptions = this.root.querySelector( ".lexcustomcontainer" );
9066
+ const suboptionsFunc = options[ `on_${ value }` ];
9067
+ suboptions.toggleAttribute( "hidden", !suboptionsFunc );
9068
+
9069
+ if( suboptionsFunc )
9070
+ {
9071
+ suboptions.innerHTML = "";
9072
+ const suboptionsPanel = new LX.Panel();
9073
+ suboptionsPanel.queue( suboptions );
9074
+ suboptionsFunc.call(this, suboptionsPanel);
9075
+ suboptionsPanel.clearQueue();
9076
+ }
9077
+
9078
+ this.root.dataset["opened"] = ( !!suboptionsFunc );
9079
+ list.style.height = ""; // set auto height by default
9080
+
8966
9081
  if( !skipCallback )
8967
9082
  {
8968
9083
  this._trigger( new LX.IEvent( name, value, event ), callback );
@@ -8983,7 +9098,7 @@ class Select extends Widget {
8983
9098
  wValue.name = name;
8984
9099
  wValue.iValue = value;
8985
9100
 
8986
- if( options.overflowContainer )
9101
+ if( options.overflowContainer !== undefined )
8987
9102
  {
8988
9103
  options.overflowContainerX = options.overflowContainerY = options.overflowContainer;
8989
9104
  }
@@ -8996,7 +9111,7 @@ class Select extends Widget {
8996
9111
 
8997
9112
  // Manage vertical aspect
8998
9113
  {
8999
- const overflowContainer = options.overflowContainerY ?? parent.getParentArea();
9114
+ const overflowContainer = options.overflowContainerY !== undefined ? options.overflowContainerY : parent.getParentArea();
9000
9115
  const listHeight = parent.offsetHeight;
9001
9116
  let topPosition = rect.y;
9002
9117
 
@@ -9016,18 +9131,25 @@ class Select extends Widget {
9016
9131
  }
9017
9132
 
9018
9133
  parent.style.top = ( topPosition + selectRoot.offsetHeight ) + 'px';
9134
+ list.style.height = ""; // set auto height by default
9019
9135
 
9020
- const showAbove = ( topPosition + listHeight ) > maxY;
9021
- if( showAbove )
9136
+ const failBelow = ( topPosition + listHeight ) > maxY;
9137
+ const failAbove = ( topPosition - listHeight ) < 0;
9138
+ if( failBelow && !failAbove )
9022
9139
  {
9023
9140
  parent.style.top = ( topPosition - listHeight ) + 'px';
9024
9141
  parent.classList.add( "place-above" );
9025
9142
  }
9143
+ // If does not fit in any direction, put it below but limit height..
9144
+ else if( failBelow && failAbove )
9145
+ {
9146
+ list.style.height = `${ maxY - topPosition - 32 }px`; // 32px margin
9147
+ }
9026
9148
  }
9027
9149
 
9028
9150
  // Manage horizontal aspect
9029
9151
  {
9030
- const overflowContainer = options.overflowContainerX ?? parent.getParentArea();
9152
+ const overflowContainer = options.overflowContainerX !== undefined ? options.overflowContainerX : parent.getParentArea();
9031
9153
  const listWidth = parent.offsetWidth;
9032
9154
  let leftPosition = rect.x;
9033
9155
 
@@ -9259,6 +9381,25 @@ class Select extends Widget {
9259
9381
 
9260
9382
  container.appendChild( listDialog );
9261
9383
 
9384
+ // Element suboptions
9385
+ let suboptions = document.createElement( "div" );
9386
+ suboptions.className = "lexcustomcontainer w-full";
9387
+
9388
+ const suboptionsFunc = options[ `on_${ value }` ];
9389
+ suboptions.toggleAttribute( "hidden", !suboptionsFunc );
9390
+
9391
+ if( suboptionsFunc )
9392
+ {
9393
+ suboptions.innerHTML = "";
9394
+ const suboptionsPanel = new LX.Panel();
9395
+ suboptionsPanel.queue( suboptions );
9396
+ suboptionsFunc.call( this, suboptionsPanel );
9397
+ suboptionsPanel.clearQueue();
9398
+ }
9399
+
9400
+ this.root.appendChild( suboptions );
9401
+ this.root.dataset["opened"] = ( !!suboptionsFunc );
9402
+
9262
9403
  LX.doAsync( this.onResize.bind( this ) );
9263
9404
  }
9264
9405
 
@@ -9436,10 +9577,12 @@ class Layers extends Widget {
9436
9577
  container.style.width = `calc( 100% - ${ realNameWidth })`;
9437
9578
  };
9438
9579
 
9439
- var container = document.createElement( "div" );
9580
+ const container = document.createElement( "div" );
9440
9581
  container.className = "lexlayers";
9441
9582
  this.root.appendChild( container );
9442
9583
 
9584
+ const maxBits = options.maxBits ?? 16;
9585
+
9443
9586
  this.setLayers = ( val ) => {
9444
9587
 
9445
9588
  container.innerHTML = "";
@@ -9448,19 +9591,19 @@ class Layers extends Widget {
9448
9591
  let nbits = binary.length;
9449
9592
 
9450
9593
  // fill zeros
9451
- for( let i = 0; i < ( 16 - nbits ); ++i )
9594
+ for( let i = 0; i < ( maxBits - nbits ); ++i )
9452
9595
  {
9453
9596
  binary = '0' + binary;
9454
9597
  }
9455
9598
 
9456
- for( let bit = 0; bit < 16; ++bit )
9599
+ for( let bit = 0; bit < maxBits; ++bit )
9457
9600
  {
9458
9601
  let layer = document.createElement( "div" );
9459
9602
  layer.className = "lexlayer";
9460
9603
 
9461
9604
  if( val != undefined )
9462
9605
  {
9463
- const valueBit = binary[ 16 - bit - 1 ];
9606
+ const valueBit = binary[ maxBits - bit - 1 ];
9464
9607
  if( valueBit != undefined && valueBit == '1' )
9465
9608
  {
9466
9609
  layer.classList.add( "selected" );
@@ -9468,7 +9611,7 @@ class Layers extends Widget {
9468
9611
  }
9469
9612
 
9470
9613
  layer.innerText = bit + 1;
9471
- layer.title = "Bit " + bit + ", value " + (1 << bit);
9614
+ layer.title = "Bit " + bit + ", value " + ( 1 << bit );
9472
9615
  container.appendChild( layer );
9473
9616
 
9474
9617
  layer.addEventListener( "click", e => {
@@ -10115,9 +10258,7 @@ class ColorInput extends Widget {
10115
10258
  colorModel: options.useRGB ? "RGB" : "Hex",
10116
10259
  useAlpha,
10117
10260
  onChange: ( color ) => {
10118
- this._fromColorPicker = true;
10119
10261
  this.set( color.hex );
10120
- delete this._fromColorPicker;
10121
10262
  }
10122
10263
  } );
10123
10264
 
@@ -10155,6 +10296,7 @@ class ColorInput extends Widget {
10155
10296
  this._skipTextUpdate = true;
10156
10297
  this.set( v );
10157
10298
  delete this._skipTextUpdate;
10299
+ this.picker.fromHexColor( v );
10158
10300
  }, { width: "calc( 100% - 24px )", disabled: options.disabled });
10159
10301
 
10160
10302
  textWidget.root.style.marginLeft = "6px";
@@ -10362,6 +10504,7 @@ class NumberInput extends Widget {
10362
10504
  slider.step = options.step ?? 1;
10363
10505
  slider.type = "range";
10364
10506
  slider.value = value;
10507
+ slider.disabled = this.disabled;
10365
10508
 
10366
10509
  slider.addEventListener( "input", ( e ) => {
10367
10510
  this.set( slider.valueAsNumber, false, e );
@@ -11426,9 +11569,16 @@ class TabSections extends Widget {
11426
11569
  throw( "Param @tabs must be an Array!" );
11427
11570
  }
11428
11571
 
11572
+ if( !tabs.length )
11573
+ {
11574
+ throw( "Tab list cannot be empty!" );
11575
+ }
11576
+
11429
11577
  const vertical = options.vertical ?? true;
11430
11578
  const showNames = !vertical && ( options.showNames ?? false );
11431
11579
 
11580
+ this.tabDOMs = {};
11581
+
11432
11582
  let container = document.createElement( 'div' );
11433
11583
  container.className = "lextabscontainer";
11434
11584
  if( !vertical )
@@ -11441,25 +11591,25 @@ class TabSections extends Widget {
11441
11591
  container.appendChild( tabContainer );
11442
11592
  this.root.appendChild( container );
11443
11593
 
11444
- for( let i = 0; i < tabs.length; ++i )
11594
+ // Check at least 1 is selected
11595
+ if( tabs.findIndex( e => e.selected === true ) < 0 )
11596
+ {
11597
+ tabs[ 0 ].selected = true;
11598
+ }
11599
+
11600
+ for( let tab of tabs )
11445
11601
  {
11446
- const tab = tabs[ i ];
11447
11602
  console.assert( tab.name );
11448
- const isSelected = ( i == 0 );
11449
11603
  let tabEl = document.createElement( "div" );
11450
- tabEl.className = "lextab " + (i == tabs.length - 1 ? "last" : "") + ( isSelected ? "selected" : "" );
11604
+ tabEl.className = "lextab " + ( ( tab.selected ?? false ) ? "selected" : "" );
11451
11605
  tabEl.innerHTML = ( showNames ? tab.name : "" );
11452
- tabEl.appendChild( LX.makeIcon( tab.icon ?? "Hash", { title: tab.name } ) );
11606
+ tabEl.appendChild( LX.makeIcon( tab.icon ?? "Hash", { title: tab.name, iconClass: tab.iconClass, svgClass: tab.svgClass } ) );
11607
+ this.tabDOMs[ tab.name ] = tabEl;
11453
11608
 
11454
11609
  let infoContainer = document.createElement( "div" );
11455
11610
  infoContainer.id = tab.name.replace( /\s/g, '' );
11456
11611
  infoContainer.className = "widgets";
11457
-
11458
- if( !isSelected )
11459
- {
11460
- infoContainer.toggleAttribute( "hidden", true );
11461
- }
11462
-
11612
+ infoContainer.toggleAttribute( "hidden", !( tab.selected ?? false ) );
11463
11613
  container.appendChild( infoContainer );
11464
11614
 
11465
11615
  tabEl.addEventListener( "click", e => {
@@ -11485,10 +11635,24 @@ class TabSections extends Widget {
11485
11635
  // Push to tab space
11486
11636
  const creationPanel = new LX.Panel();
11487
11637
  creationPanel.queue( infoContainer );
11488
- tab.onCreate.call(this, creationPanel);
11638
+ tab.onCreate.call( this, creationPanel, infoContainer );
11489
11639
  creationPanel.clearQueue();
11490
11640
  }
11491
11641
  }
11642
+
11643
+ this.tabs = tabs;
11644
+ }
11645
+
11646
+ select( name ) {
11647
+
11648
+ const tabEl = this.tabDOMs[ name ];
11649
+
11650
+ if( !tabEl )
11651
+ {
11652
+ return;
11653
+ }
11654
+
11655
+ tabEl.click();
11492
11656
  }
11493
11657
  }
11494
11658
 
@@ -11804,7 +11968,9 @@ class Table extends Widget {
11804
11968
  const body = table.querySelector( "tbody" );
11805
11969
  for( const el of body.childNodes )
11806
11970
  {
11807
- data.checkMap[ el.getAttribute( "rowId" ) ] = this.checked;
11971
+ const rowId = el.getAttribute( "rowId" );
11972
+ if( !rowId ) continue;
11973
+ data.checkMap[ rowId ] = this.checked;
11808
11974
  el.querySelector( "input[type='checkbox']" ).checked = this.checked;
11809
11975
  }
11810
11976
  });
@@ -12015,8 +12181,8 @@ class Table extends Widget {
12015
12181
  }
12016
12182
 
12017
12183
  const row = document.createElement( 'tr' );
12018
- const rowId = LX.getSupportedDOMName( bodyData.join( '-' ) );
12019
- row.setAttribute( "rowId", rowId.substr(0, 32) );
12184
+ const rowId = LX.getSupportedDOMName( bodyData.join( '-' ) ).substr(0, 32);
12185
+ row.setAttribute( "rowId", rowId );
12020
12186
 
12021
12187
  if( options.sortable ?? false )
12022
12188
  {
@@ -12132,7 +12298,7 @@ class Table extends Widget {
12132
12298
  }
12133
12299
  else if( action == "menu" )
12134
12300
  {
12135
- button = LX.makeIcon( "Ellipsis", { title: "Menu" } );
12301
+ button = LX.makeIcon( "EllipsisVertical", { title: "Menu" } );
12136
12302
  button.addEventListener( 'click', function( event ) {
12137
12303
  if( !options.onMenuAction )
12138
12304
  {
@@ -12171,6 +12337,17 @@ class Table extends Widget {
12171
12337
 
12172
12338
  body.appendChild( row );
12173
12339
  }
12340
+
12341
+ if( body.childNodes.length == 0 )
12342
+ {
12343
+ const row = document.createElement( 'tr' );
12344
+ const td = document.createElement( 'td' );
12345
+ td.setAttribute( "colspan", data.head.length + this.rowOffsetCount + 1 ); // +1 for rowActions
12346
+ td.className = "empty-row";
12347
+ td.innerHTML = "No results.";
12348
+ row.appendChild( td );
12349
+ body.appendChild( row );
12350
+ }
12174
12351
  }
12175
12352
 
12176
12353
  for( const v in data.colVisibilityMap )
@@ -12955,6 +13132,7 @@ class Panel {
12955
13132
  * hideName: Don't use name as label [false]
12956
13133
  * disabled: Make the widget disabled [false]
12957
13134
  * icon: Icon class to show as button value
13135
+ * iconPosition: Icon position (cover|start|end)
12958
13136
  * fileInput: Button click requests a file
12959
13137
  * fileInputType: Type of the requested file
12960
13138
  * img: Path to image to show as button value
@@ -13446,6 +13624,8 @@ class Panel {
13446
13624
  * @param {Array} tabs Contains objects with {
13447
13625
  * name: Name of the tab (if icon, use as title)
13448
13626
  * icon: Icon to be used as the tab icon (optional)
13627
+ * iconClass: Class to be added to the icon (optional)
13628
+ * svgClass: Class to be added to the inner SVG of the icon (optional)
13449
13629
  * onCreate: Func to be called at tab creation
13450
13630
  * onSelect: Func to be called on select tab (optional)
13451
13631
  * }
@@ -13924,46 +14104,43 @@ class Menubar {
13924
14104
  }
13925
14105
 
13926
14106
  let button = this.buttons[ name ];
14107
+ // If the button already exists, delete it
14108
+ // since only one button of this type can exist
13927
14109
  if( button )
13928
14110
  {
13929
- button.innerHTML = "";
13930
- button.appendChild( LX.makeIcon( icon, { svgClass: "xl" } ) );
13931
- return;
14111
+ delete this.buttons[ name ];
14112
+ LX.deleteElement( button.root );
13932
14113
  }
13933
14114
 
13934
14115
  // Otherwise, create it
13935
- button = document.createElement('div');
13936
- const disabled = options.disabled ?? false;
13937
- button.className = "lexmenubutton main" + (disabled ? " disabled" : "");
13938
- button.title = name;
13939
- button.appendChild( LX.makeIcon( icon, { svgClass: "xl" } ) );
14116
+ button = new LX.Button( name, null, callback, {
14117
+ title: name,
14118
+ buttonClass: "lexmenubutton main bg-none",
14119
+ disabled: options.disabled,
14120
+ icon,
14121
+ svgClass: "xl",
14122
+ hideName: true,
14123
+ swap: options.swap
14124
+ } );
13940
14125
 
13941
14126
  if( options.float == "right" )
13942
14127
  {
13943
- button.right = true;
14128
+ button.root.right = true;
13944
14129
  }
13945
14130
 
13946
14131
  if( this.root.lastChild && this.root.lastChild.right )
13947
14132
  {
13948
- this.root.lastChild.before( button );
14133
+ this.root.lastChild.before( button.root );
13949
14134
  }
13950
14135
  else if( options.float == "left" )
13951
14136
  {
13952
- this.root.prepend( button );
14137
+ this.root.prepend( button.root );
13953
14138
  }
13954
14139
  else
13955
14140
  {
13956
- this.root.appendChild( button );
14141
+ this.root.appendChild( button.root );
13957
14142
  }
13958
14143
 
13959
- const _b = button.querySelector('a');
13960
- _b.addEventListener("click", (e) => {
13961
- if( callback && !disabled )
13962
- {
13963
- callback.call( this, _b, e );
13964
- }
13965
- });
13966
-
13967
14144
  this.buttons[ name ] = button;
13968
14145
  }
13969
14146
 
@@ -14045,7 +14222,7 @@ class Menubar {
14045
14222
  this.buttonContainer.className = "lexmenubuttons";
14046
14223
  this.buttonContainer.classList.add( options.float ?? "center" );
14047
14224
 
14048
- if( options.position == "right" )
14225
+ if( options.float == "right" )
14049
14226
  {
14050
14227
  this.buttonContainer.right = true;
14051
14228
  }
@@ -14064,12 +14241,12 @@ class Menubar {
14064
14241
  {
14065
14242
  const data = buttons[ i ];
14066
14243
  const title = data.title;
14067
- const button = new LX.Button( title, "", data.callback, { title, buttonClass: "bg-none", disabled: data.disabled, icon: data.icon, hideName: true, swap: data.swap } );
14244
+ 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" } );
14068
14245
  this.buttonContainer.appendChild( button.root );
14069
14246
 
14070
14247
  if( title )
14071
14248
  {
14072
- this.buttons[ title ] = button.root;
14249
+ this.buttons[ title ] = button;
14073
14250
  }
14074
14251
  }
14075
14252
  }
@@ -14830,10 +15007,14 @@ class AssetView {
14830
15007
 
14831
15008
  if( options.rootPath )
14832
15009
  {
14833
- if(options.rootPath.constructor !== String)
14834
- console.warn("Asset Root Path must be a String (now is " + path.constructor.name + ")");
15010
+ if( options.rootPath.constructor !== String )
15011
+ {
15012
+ console.warn( `Asset Root Path must be a String (now is a ${ path.constructor.name })` );
15013
+ }
14835
15014
  else
15015
+ {
14836
15016
  this.rootPath = options.rootPath;
15017
+ }
14837
15018
  }
14838
15019
 
14839
15020
  let div = document.createElement('div');
@@ -15202,7 +15383,7 @@ class AssetView {
15202
15383
  this.content.className = (isContentLayout ? "lexassetscontent" : "lexassetscontent list");
15203
15384
  let that = this;
15204
15385
 
15205
- const add_item = function(item) {
15386
+ const _addItem = function(item) {
15206
15387
 
15207
15388
  const type = item.type.charAt( 0 ).toUpperCase() + item.type.slice( 1 );
15208
15389
  const extension = LX.getExtension( item.id );
@@ -15227,20 +15408,27 @@ class AssetView {
15227
15408
  return;
15228
15409
  }
15229
15410
 
15411
+ const dialog = itemEl.closest('dialog');
15230
15412
  const rect = itemEl.getBoundingClientRect();
15231
15413
  const targetRect = e.target.getBoundingClientRect();
15232
- const parentRect = desc.parentElement.getBoundingClientRect();
15233
15414
 
15234
- let localOffsetX = targetRect.x - parentRect.x - ( targetRect.x - rect.x );
15235
- let localOffsetY = targetRect.y - parentRect.y - ( targetRect.y - rect.y );
15415
+ let localOffsetX = rect.x + e.offsetX;
15416
+ let localOffsetY = rect.y + e.offsetY;
15417
+
15418
+ if( dialog )
15419
+ {
15420
+ const dialogRect = dialog.getBoundingClientRect();
15421
+ localOffsetX -= dialogRect.x;
15422
+ localOffsetY -= dialogRect.y;
15423
+ }
15236
15424
 
15237
15425
  if( e.target.classList.contains( "lexassettitle" ) )
15238
15426
  {
15239
15427
  localOffsetY += ( targetRect.y - rect.y );
15240
15428
  }
15241
15429
 
15242
- desc.style.left = (localOffsetX + e.offsetX + 12) + "px";
15243
- desc.style.top = (localOffsetY + e.offsetY) + "px";
15430
+ desc.style.left = ( localOffsetX ) + "px";
15431
+ desc.style.top = ( localOffsetY - 36 ) + "px";
15244
15432
  });
15245
15433
 
15246
15434
  itemEl.addEventListener("mouseenter", () => {
@@ -15438,7 +15626,7 @@ class AssetView {
15438
15626
  } });
15439
15627
  }else
15440
15628
  {
15441
- item.domEl = add_item( item );
15629
+ item.domEl = _addItem( item );
15442
15630
  }
15443
15631
  }
15444
15632