lexgui 0.6.9 → 0.6.11

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.
@@ -1,4 +1,4 @@
1
- // This is a generated file. Do not edit.
1
+ // This is a generated file. Do not edit.
2
2
  // Lexgui.js @jxarco
3
3
 
4
4
  /**
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  const LX = {
10
- version: "0.6.9",
10
+ version: "0.6.11",
11
11
  ready: false,
12
12
  components: [], // Specific pre-build components
13
13
  signals: {}, // Events and triggers
@@ -311,7 +311,7 @@ function _createCommandbar( root )
311
311
 
312
312
  const _propagateAdd = ( item, filter, path, skipPropagation ) => {
313
313
 
314
- if( !item )
314
+ if( !item || ( item.constructor != Object ) )
315
315
  {
316
316
  return;
317
317
  }
@@ -718,8 +718,6 @@ class Popover {
718
718
 
719
719
  constructor( trigger, content, options = {} ) {
720
720
 
721
- console.assert( trigger, "Popover needs a DOM element as trigger!" );
722
-
723
721
  if( Popover.activeElement )
724
722
  {
725
723
  Popover.activeElement.destroy();
@@ -727,13 +725,20 @@ class Popover {
727
725
  }
728
726
 
729
727
  this._trigger = trigger;
730
- trigger.classList.add( "triggered" );
731
- trigger.active = this;
728
+
729
+ if( trigger )
730
+ {
731
+ trigger.classList.add( "triggered" );
732
+ trigger.active = this;
733
+ }
732
734
 
733
735
  this._windowPadding = 4;
734
736
  this.side = options.side ?? "bottom";
735
737
  this.align = options.align ?? "center";
738
+ this.sideOffset = options.sideOffset ?? 0;
739
+ this.alignOffset = options.alignOffset ?? 0;
736
740
  this.avoidCollisions = options.avoidCollisions ?? true;
741
+ this.reference = options.reference;
737
742
 
738
743
  this.root = document.createElement( "div" );
739
744
  this.root.dataset["side"] = this.side;
@@ -768,29 +773,35 @@ class Popover {
768
773
  LX.doAsync( () => {
769
774
  this._adjustPosition();
770
775
 
771
- this.root.focus();
776
+ if( this._trigger )
777
+ {
778
+ this.root.focus();
772
779
 
773
- this._onClick = e => {
774
- if( e.target && ( this.root.contains( e.target ) || e.target == this._trigger ) )
775
- {
776
- return;
777
- }
778
- this.destroy();
779
- };
780
+ this._onClick = e => {
781
+ if( e.target && ( this.root.contains( e.target ) || e.target == this._trigger ) )
782
+ {
783
+ return;
784
+ }
785
+ this.destroy();
786
+ };
787
+
788
+ document.body.addEventListener( "mousedown", this._onClick, true );
789
+ document.body.addEventListener( "focusin", this._onClick, true );
790
+ }
780
791
 
781
- document.body.addEventListener( "mousedown", this._onClick, true );
782
- document.body.addEventListener( "focusin", this._onClick, true );
783
792
  }, 10 );
784
793
  }
785
794
 
786
795
  destroy() {
787
796
 
788
- this._trigger.classList.remove( "triggered" );
789
-
790
- delete this._trigger.active;
797
+ if( this._trigger )
798
+ {
799
+ this._trigger.classList.remove( "triggered" );
800
+ delete this._trigger.active;
791
801
 
792
- document.body.removeEventListener( "mousedown", this._onClick, true );
793
- document.body.removeEventListener( "focusin", this._onClick, true );
802
+ document.body.removeEventListener( "mousedown", this._onClick, true );
803
+ document.body.removeEventListener( "focusin", this._onClick, true );
804
+ }
794
805
 
795
806
  this.root.remove();
796
807
 
@@ -803,26 +814,28 @@ class Popover {
803
814
 
804
815
  // Place menu using trigger position and user options
805
816
  {
806
- const rect = this._trigger.getBoundingClientRect();
817
+ const el = this.reference ?? this._trigger;
818
+ console.assert( el, "Popover needs a trigger or reference element!" );
819
+ const rect = el.getBoundingClientRect();
807
820
 
808
821
  let alignWidth = true;
809
822
 
810
823
  switch( this.side )
811
824
  {
812
825
  case "left":
813
- position[ 0 ] += ( rect.x - this.root.offsetWidth );
826
+ position[ 0 ] += ( rect.x - this.root.offsetWidth - this.sideOffset );
814
827
  alignWidth = false;
815
828
  break;
816
829
  case "right":
817
- position[ 0 ] += ( rect.x + rect.width );
830
+ position[ 0 ] += ( rect.x + rect.width + this.sideOffset );
818
831
  alignWidth = false;
819
832
  break;
820
833
  case "top":
821
- position[ 1 ] += ( rect.y - this.root.offsetHeight );
834
+ position[ 1 ] += ( rect.y - this.root.offsetHeight - this.sideOffset );
822
835
  alignWidth = true;
823
836
  break;
824
837
  case "bottom":
825
- position[ 1 ] += ( rect.y + rect.height );
838
+ position[ 1 ] += ( rect.y + rect.height + this.sideOffset );
826
839
  alignWidth = true;
827
840
  break;
828
841
  }
@@ -842,6 +855,9 @@ class Popover {
842
855
  else { position[ 1 ] += rect.y - this.root.offsetHeight + rect.height; }
843
856
  break;
844
857
  }
858
+
859
+ if( alignWidth ) { position[ 0 ] += this.alignOffset; }
860
+ else { position[ 1 ] += this.alignOffset; }
845
861
  }
846
862
 
847
863
  if( this.avoidCollisions )
@@ -978,6 +994,8 @@ class DropdownMenu {
978
994
  this._windowPadding = 4;
979
995
  this.side = options.side ?? "bottom";
980
996
  this.align = options.align ?? "center";
997
+ this.sideOffset = options.sideOffset ?? 0;
998
+ this.alignOffset = options.alignOffset ?? 0;
981
999
  this.avoidCollisions = options.avoidCollisions ?? true;
982
1000
  this.onBlur = options.onBlur;
983
1001
  this.inPlace = false;
@@ -1220,19 +1238,19 @@ class DropdownMenu {
1220
1238
  switch( this.side )
1221
1239
  {
1222
1240
  case "left":
1223
- position[ 0 ] += ( rect.x - this.root.offsetWidth );
1241
+ position[ 0 ] += ( rect.x - this.root.offsetWidth - this.sideOffset );
1224
1242
  alignWidth = false;
1225
1243
  break;
1226
1244
  case "right":
1227
- position[ 0 ] += ( rect.x + rect.width );
1245
+ position[ 0 ] += ( rect.x + rect.width + this.sideOffset );
1228
1246
  alignWidth = false;
1229
1247
  break;
1230
1248
  case "top":
1231
- position[ 1 ] += ( rect.y - this.root.offsetHeight );
1249
+ position[ 1 ] += ( rect.y - this.root.offsetHeight - this.sideOffset );
1232
1250
  alignWidth = true;
1233
1251
  break;
1234
1252
  case "bottom":
1235
- position[ 1 ] += ( rect.y + rect.height );
1253
+ position[ 1 ] += ( rect.y + rect.height + this.sideOffset );
1236
1254
  alignWidth = true;
1237
1255
  break;
1238
1256
  }
@@ -1252,6 +1270,9 @@ class DropdownMenu {
1252
1270
  else { position[ 1 ] += rect.y - this.root.offsetHeight + rect.height; }
1253
1271
  break;
1254
1272
  }
1273
+
1274
+ if( alignWidth ) { position[ 0 ] += this.alignOffset; }
1275
+ else { position[ 1 ] += this.alignOffset; }
1255
1276
  }
1256
1277
 
1257
1278
  if( this.avoidCollisions )
@@ -1530,7 +1551,7 @@ class ColorPicker {
1530
1551
  copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "none";
1531
1552
 
1532
1553
  LX.doAsync( () => {
1533
- copyButtonWidget.root.swap( true );
1554
+ copyButtonWidget.swap( true );
1534
1555
  copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "auto";
1535
1556
  }, 3000 );
1536
1557
 
@@ -4113,8 +4134,8 @@ Element.prototype.ignore = function( eventName, callbackName ) {
4113
4134
  callbackName = callbackName ?? ( "_on" + eventName );
4114
4135
  const callback = this[ callbackName ];
4115
4136
  this.removeEventListener( eventName, callback );
4116
- };
4117
-
4137
+ };
4138
+
4118
4139
  // icons.js @jxarco
4119
4140
 
4120
4141
  const RAW_ICONS = {
@@ -4292,8 +4313,8 @@ LX.LucideIconAlias = {
4292
4313
  "RotateRight": "RotateCw",
4293
4314
  "RotateBack": "RotateCcw",
4294
4315
  "RotateLeft": "RotateCcw",
4295
- };
4296
-
4316
+ };
4317
+
4297
4318
  // utils.js @jxarco
4298
4319
 
4299
4320
  function clamp( num, min, max ) { return Math.min( Math.max( num, min ), max ); }
@@ -6009,10 +6030,180 @@ function drawSpline( ctx, pts, t )
6009
6030
  ctx.restore();
6010
6031
  }
6011
6032
 
6012
- LX.drawSpline = drawSpline;
6013
-
6033
+ LX.drawSpline = drawSpline;
6034
+
6014
6035
  // area.js @jxarco
6015
6036
 
6037
+ class AreaOverlayButtons {
6038
+
6039
+ /**
6040
+ * @constructor AreaOverlayButtons
6041
+ */
6042
+
6043
+ constructor( area, buttonsArray, options = {} ) {
6044
+
6045
+ this.area = area;
6046
+ this.options = options;
6047
+
6048
+ this.buttons = {};
6049
+
6050
+ this._buildButtons( buttonsArray, options );
6051
+ }
6052
+
6053
+ _buildButtons( buttonsArray, options ) {
6054
+
6055
+ options.className = "lexoverlaybuttons";
6056
+
6057
+ let overlayPanel = this.area.addPanel( options );
6058
+ let overlayGroup = null;
6059
+
6060
+ const container = document.createElement( "div" );
6061
+ container.className = "lexoverlaybuttonscontainer";
6062
+ container.appendChild( overlayPanel.root );
6063
+ this.area.attach( container );
6064
+
6065
+ const float = options.float;
6066
+ let floatClass = "";
6067
+
6068
+ if( float )
6069
+ {
6070
+ for( let i = 0; i < float.length; i++ )
6071
+ {
6072
+ const t = float[ i ];
6073
+ switch( t )
6074
+ {
6075
+ case 'h': break;
6076
+ case 'v': floatClass += " vertical"; break;
6077
+ case 't': break;
6078
+ case 'm': floatClass += " middle"; break;
6079
+ case 'b': floatClass += " bottom"; break;
6080
+ case 'l': break;
6081
+ case 'c': floatClass += " center"; break;
6082
+ case 'r': floatClass += " right"; break;
6083
+ }
6084
+ }
6085
+
6086
+ container.className += ` ${ floatClass }`;
6087
+ }
6088
+
6089
+ const _addButton = ( b, group, last ) => {
6090
+
6091
+ const _options = {
6092
+ width: "auto",
6093
+ selectable: b.selectable,
6094
+ selected: b.selected,
6095
+ icon: b.icon,
6096
+ img: b.img,
6097
+ className: b.class ?? "",
6098
+ title: b.name,
6099
+ overflowContainerX: overlayPanel.root,
6100
+ swap: b.swap
6101
+ };
6102
+
6103
+ if( group )
6104
+ {
6105
+ if( !overlayGroup )
6106
+ {
6107
+ overlayGroup = document.createElement('div');
6108
+ overlayGroup.className = "lexoverlaygroup";
6109
+ overlayPanel.queuedContainer = overlayGroup;
6110
+ }
6111
+
6112
+ _options.parent = overlayGroup;
6113
+ }
6114
+
6115
+ let callback = b.callback;
6116
+ let widget = null;
6117
+
6118
+ if( b.options )
6119
+ {
6120
+ widget = overlayPanel.addSelect( null, b.options, b.value ?? b.name, callback, _options );
6121
+ }
6122
+ else
6123
+ {
6124
+ widget = overlayPanel.addButton( null, b.name, function( value, event ) {
6125
+ if( b.selectable )
6126
+ {
6127
+ if( b.group )
6128
+ {
6129
+ let _prev = b.selected;
6130
+ b.group.forEach( sub => sub.selected = false );
6131
+ b.selected = !_prev;
6132
+ }
6133
+ else
6134
+ {
6135
+ b.selected = !b.selected;
6136
+ }
6137
+ }
6138
+
6139
+ if( callback )
6140
+ {
6141
+ callback( value, event, widget.root );
6142
+ }
6143
+
6144
+ }, _options );
6145
+ }
6146
+
6147
+ this.buttons[ b.name ] = widget;
6148
+
6149
+ // ends the group
6150
+ if( overlayGroup && last )
6151
+ {
6152
+ overlayPanel.root.appendChild( overlayGroup );
6153
+ overlayGroup = null;
6154
+ overlayPanel.clearQueue();
6155
+ }
6156
+ };
6157
+
6158
+ const _refreshPanel = function() {
6159
+
6160
+ overlayPanel.clear();
6161
+
6162
+ for( let b of buttonsArray )
6163
+ {
6164
+ if( b === null )
6165
+ {
6166
+ // Add a separator
6167
+ const separator = document.createElement("div");
6168
+ separator.className = "lexoverlayseparator" + floatClass;
6169
+ overlayPanel.root.appendChild( separator );
6170
+ continue;
6171
+ }
6172
+
6173
+ if( b.constructor === Array )
6174
+ {
6175
+ for( let i = 0; i < b.length; ++i )
6176
+ {
6177
+ let sub = b[ i ];
6178
+ sub.group = b;
6179
+ _addButton( sub, true, i == ( b.length - 1 ) );
6180
+ }
6181
+ }
6182
+ else
6183
+ {
6184
+ _addButton( b );
6185
+ }
6186
+ }
6187
+
6188
+ // Add floating info
6189
+ if( float )
6190
+ {
6191
+ var height = 0;
6192
+ overlayPanel.root.childNodes.forEach( c => { height += c.offsetHeight; } );
6193
+
6194
+ if( container.className.includes( "middle" ) )
6195
+ {
6196
+ container.style.top = "-moz-calc( 50% - " + (height * 0.5) + "px )";
6197
+ container.style.top = "-webkit-calc( 50% - " + (height * 0.5) + "px )";
6198
+ container.style.top = "calc( 50% - " + (height * 0.5) + "px )";
6199
+ }
6200
+ }
6201
+ };
6202
+
6203
+ _refreshPanel();
6204
+ }
6205
+ }
6206
+
6016
6207
  class Area {
6017
6208
 
6018
6209
  /**
@@ -6758,181 +6949,38 @@ class Area {
6758
6949
  bar.attach( sidebar );
6759
6950
  bar.isSidebar = true;
6760
6951
 
6761
- if( options.parentClass )
6762
- {
6763
- bar.root.className += ` ${ options.parentClass }`;
6764
- }
6765
-
6766
- return sidebar;
6767
- }
6768
-
6769
- /**
6770
- * @method addOverlayButtons
6771
- * @param {Array} buttons Buttons info
6772
- * @param {Object} options:
6773
- * float: Where to put the buttons (h: horizontal, v: vertical, t: top, m: middle, b: bottom, l: left, c: center, r: right) [htc]
6774
- */
6775
-
6776
- addOverlayButtons( buttons, options = {} ) {
6777
-
6778
- // Add to last split section if area has been split
6779
- if( this.sections.length )
6780
- {
6781
- this.sections[ 1 ].addOverlayButtons( buttons, options );
6782
- return;
6783
- }
6784
-
6785
- console.assert( buttons.constructor == Array && buttons.length );
6786
-
6787
- // Set area to relative to use local position
6788
- this.root.style.position = "relative";
6789
-
6790
- options.className = "lexoverlaybuttons";
6791
-
6792
- let overlayPanel = this.addPanel( options );
6793
- let overlayGroup = null;
6794
-
6795
- const container = document.createElement("div");
6796
- container.className = "lexoverlaybuttonscontainer";
6797
- container.appendChild( overlayPanel.root );
6798
- this.attach( container );
6799
-
6800
- const float = options.float;
6801
- let floatClass = "";
6802
-
6803
- if( float )
6804
- {
6805
- for( let i = 0; i < float.length; i++ )
6806
- {
6807
- const t = float[ i ];
6808
- switch( t )
6809
- {
6810
- case 'h': break;
6811
- case 'v': floatClass += " vertical"; break;
6812
- case 't': break;
6813
- case 'm': floatClass += " middle"; break;
6814
- case 'b': floatClass += " bottom"; break;
6815
- case 'l': break;
6816
- case 'c': floatClass += " center"; break;
6817
- case 'r': floatClass += " right"; break;
6818
- }
6819
- }
6820
-
6821
- container.className += ` ${ floatClass }`;
6822
- }
6823
-
6824
- const _addButton = function( b, group, last ) {
6825
-
6826
- const _options = {
6827
- width: "auto",
6828
- selectable: b.selectable,
6829
- selected: b.selected,
6830
- icon: b.icon,
6831
- img: b.img,
6832
- className: b.class ?? "",
6833
- title: b.name,
6834
- overflowContainerX: overlayPanel.root,
6835
- swap: b.swap
6836
- };
6837
-
6838
- if( group )
6839
- {
6840
- if( !overlayGroup )
6841
- {
6842
- overlayGroup = document.createElement('div');
6843
- overlayGroup.className = "lexoverlaygroup";
6844
- overlayPanel.queuedContainer = overlayGroup;
6845
- }
6846
-
6847
- _options.parent = overlayGroup;
6848
- }
6849
-
6850
- let callback = b.callback;
6851
-
6852
- if( b.options )
6853
- {
6854
- overlayPanel.addSelect( null, b.options, b.name, callback, _options );
6855
- }
6856
- else
6857
- {
6858
- const button = overlayPanel.addButton( null, b.name, function( value, event ) {
6859
- if( b.selectable )
6860
- {
6861
- if( b.group )
6862
- {
6863
- let _prev = b.selected;
6864
- b.group.forEach( sub => sub.selected = false );
6865
- b.selected = !_prev;
6866
- }
6867
- else
6868
- {
6869
- b.selected = !b.selected;
6870
- }
6871
- }
6872
-
6873
- if( callback )
6874
- {
6875
- callback( value, event, button.root );
6876
- }
6877
-
6878
- }, _options );
6879
- }
6880
-
6881
- // ends the group
6882
- if( overlayGroup && last )
6883
- {
6884
- overlayPanel.root.appendChild( overlayGroup );
6885
- overlayGroup = null;
6886
- overlayPanel.clearQueue();
6887
- }
6888
- };
6952
+ if( options.parentClass )
6953
+ {
6954
+ bar.root.className += ` ${ options.parentClass }`;
6955
+ }
6889
6956
 
6890
- const _refreshPanel = function() {
6957
+ return sidebar;
6958
+ }
6891
6959
 
6892
- overlayPanel.clear();
6960
+ /**
6961
+ * @method addOverlayButtons
6962
+ * @param {Array} buttons Buttons info
6963
+ * @param {Object} options:
6964
+ * float: Where to put the buttons (h: horizontal, v: vertical, t: top, m: middle, b: bottom, l: left, c: center, r: right) [htc]
6965
+ */
6893
6966
 
6894
- for( let b of buttons )
6895
- {
6896
- if( b === null )
6897
- {
6898
- // Add a separator
6899
- const separator = document.createElement("div");
6900
- separator.className = "lexoverlayseparator" + floatClass;
6901
- overlayPanel.root.appendChild( separator );
6902
- continue;
6903
- }
6967
+ addOverlayButtons( buttons, options = {} ) {
6904
6968
 
6905
- if( b.constructor === Array )
6906
- {
6907
- for( let i = 0; i < b.length; ++i )
6908
- {
6909
- let sub = b[ i ];
6910
- sub.group = b;
6911
- _addButton(sub, true, i == ( b.length - 1 ));
6912
- }
6913
- }
6914
- else
6915
- {
6916
- _addButton( b );
6917
- }
6918
- }
6969
+ // Add to last split section if area has been split
6970
+ if( this.sections.length )
6971
+ {
6972
+ return this.sections[ 1 ].addOverlayButtons( buttons, options );
6973
+ }
6919
6974
 
6920
- // Add floating info
6921
- if( float )
6922
- {
6923
- var height = 0;
6924
- overlayPanel.root.childNodes.forEach( c => { height += c.offsetHeight; } );
6975
+ console.assert( buttons.constructor == Array && buttons.length );
6925
6976
 
6926
- if( container.className.includes( "middle" ) )
6927
- {
6928
- container.style.top = "-moz-calc( 50% - " + (height * 0.5) + "px )";
6929
- container.style.top = "-webkit-calc( 50% - " + (height * 0.5) + "px )";
6930
- container.style.top = "calc( 50% - " + (height * 0.5) + "px )";
6931
- }
6932
- }
6933
- };
6977
+ // Set area to relative to use local position
6978
+ this.root.style.position = "relative";
6934
6979
 
6935
- _refreshPanel();
6980
+ // Reset if already exists
6981
+ this.overlayButtons = new AreaOverlayButtons( this, buttons, options );
6982
+
6983
+ return this.overlayButtons;
6936
6984
  }
6937
6985
 
6938
6986
  /**
@@ -7077,8 +7125,8 @@ class Area {
7077
7125
  }
7078
7126
  }
7079
7127
  }
7080
- LX.Area = Area;
7081
-
7128
+ LX.Area = Area;
7129
+
7082
7130
  // widget.js @jxarco
7083
7131
 
7084
7132
  /**
@@ -8455,14 +8503,15 @@ class Button extends Widget {
8455
8503
  super( Widget.BUTTON, name, null, options );
8456
8504
 
8457
8505
  this.onGetValue = () => {
8458
- return wValue.querySelector( "input" )?.checked;
8506
+ const swapInput = wValue.querySelector( "input" );
8507
+ return swapInput ? swapInput.checked : value
8459
8508
  };
8460
8509
 
8461
8510
  this.onSetValue = ( newValue, skipCallback, event ) => {
8462
8511
 
8463
8512
  if( ( options.swap ?? false ) )
8464
8513
  {
8465
- this.root.setState( newValue, skipCallback );
8514
+ this.setState( newValue, skipCallback );
8466
8515
  return;
8467
8516
  }
8468
8517
 
@@ -8492,6 +8541,30 @@ class Button extends Widget {
8492
8541
  wValue.style.width = `calc( 100% - ${ realNameWidth })`;
8493
8542
  };
8494
8543
 
8544
+ // In case of swap, set if a change has to be performed
8545
+ this.setState = function( v, skipCallback ) {
8546
+ const swapInput = wValue.querySelector( "input" );
8547
+
8548
+ if( swapInput )
8549
+ {
8550
+ swapInput.checked = v;
8551
+ }
8552
+ else if( options.selectable )
8553
+ {
8554
+ if( options.parent )
8555
+ {
8556
+ options.parent.querySelectorAll(".lexbutton.selected").forEach( b => { if( b == wValue ) return; b.classList.remove( "selected" ); } );
8557
+ }
8558
+
8559
+ wValue.classList.toggle( "selected", v );
8560
+ }
8561
+
8562
+ if( !skipCallback )
8563
+ {
8564
+ this._trigger( new LX.IEvent( name, swapInput ? swapInput.checked : ( options.selectable ? v : value ), null ), callback );
8565
+ }
8566
+ };
8567
+
8495
8568
  var wValue = document.createElement( 'button' );
8496
8569
  wValue.title = options.tooltip ? "" : ( options.title ?? "" );
8497
8570
  wValue.className = "lexbutton p-1 " + ( options.buttonClass ?? "" );
@@ -8511,7 +8584,7 @@ class Button extends Widget {
8511
8584
  }
8512
8585
  else if( options.icon )
8513
8586
  {
8514
- const icon = LX.makeIcon( options.icon );
8587
+ const icon = LX.makeIcon( options.icon, { iconClass: options.iconClass, svgClass: options.svgClass } );
8515
8588
  const iconPosition = options.iconPosition ?? "cover";
8516
8589
 
8517
8590
  // Default
@@ -8582,7 +8655,7 @@ class Button extends Widget {
8582
8655
  const swapIcon = LX.makeIcon( options.swap, { iconClass: "swap-on" } );
8583
8656
  wValue.appendChild( swapIcon );
8584
8657
 
8585
- this.root.swap = function( skipCallback ) {
8658
+ this.swap = function( skipCallback ) {
8586
8659
  const swapInput = wValue.querySelector( "input" );
8587
8660
  swapInput.checked = !swapInput.checked;
8588
8661
  if( !skipCallback )
@@ -8590,19 +8663,10 @@ class Button extends Widget {
8590
8663
  trigger.click();
8591
8664
  }
8592
8665
  };
8593
-
8594
- // Set if swap has to be performed
8595
- this.root.setState = function( v, skipCallback ) {
8596
- const swapInput = wValue.querySelector( "input" );
8597
- swapInput.checked = v;
8598
- if( !skipCallback )
8599
- {
8600
- trigger.click();
8601
- }
8602
- };
8603
8666
  }
8604
8667
 
8605
8668
  trigger.addEventListener( "click", e => {
8669
+ let isSelected;
8606
8670
  if( options.selectable )
8607
8671
  {
8608
8672
  if( options.parent )
@@ -8610,7 +8674,7 @@ class Button extends Widget {
8610
8674
  options.parent.querySelectorAll(".lexbutton.selected").forEach( b => { if( b == wValue ) return; b.classList.remove( "selected" ); } );
8611
8675
  }
8612
8676
 
8613
- wValue.classList.toggle('selected');
8677
+ isSelected = wValue.classList.toggle('selected');
8614
8678
  }
8615
8679
 
8616
8680
  if( options.fileInput )
@@ -8620,7 +8684,7 @@ class Button extends Widget {
8620
8684
  else
8621
8685
  {
8622
8686
  const swapInput = wValue.querySelector( "input" );
8623
- this._trigger( new LX.IEvent( name, swapInput?.checked ?? value, e ), callback );
8687
+ this._trigger( new LX.IEvent( name, swapInput?.checked ?? ( options.selectable ? isSelected : value ), e ), callback );
8624
8688
  }
8625
8689
  });
8626
8690
 
@@ -8912,16 +8976,17 @@ class Form extends Widget {
8912
8976
 
8913
8977
  if( entryData.constructor != Object )
8914
8978
  {
8915
- entryData = { };
8979
+ const oldValue = JSON.parse( JSON.stringify( entryData ) );
8980
+ entryData = { value: oldValue };
8916
8981
  data[ entry ] = entryData;
8917
8982
  }
8918
8983
 
8919
- entryData.placeholder = entryData.placeholder ?? entry;
8984
+ entryData.placeholder = entryData.placeholder ?? ( entryData.label ?? `Enter ${ entry }` );
8920
8985
  entryData.width = "100%";
8921
8986
 
8922
8987
  if( !( options.skipLabels ?? false ) )
8923
8988
  {
8924
- const label = new LX.TextInput( null, entry, null, { disabled: true, inputClass: "formlabel nobg" } );
8989
+ const label = new LX.TextInput( null, entryData.label ?? entry, null, { disabled: true, inputClass: "formlabel nobg" } );
8925
8990
  container.appendChild( label.root );
8926
8991
  }
8927
8992
 
@@ -9026,6 +9091,7 @@ class Select extends Widget {
9026
9091
  }
9027
9092
 
9028
9093
  this.root.dataset["opened"] = ( !!suboptionsFunc );
9094
+ list.style.height = ""; // set auto height by default
9029
9095
 
9030
9096
  if( !skipCallback )
9031
9097
  {
@@ -9047,7 +9113,7 @@ class Select extends Widget {
9047
9113
  wValue.name = name;
9048
9114
  wValue.iValue = value;
9049
9115
 
9050
- if( options.overflowContainer )
9116
+ if( options.overflowContainer !== undefined )
9051
9117
  {
9052
9118
  options.overflowContainerX = options.overflowContainerY = options.overflowContainer;
9053
9119
  }
@@ -9060,7 +9126,7 @@ class Select extends Widget {
9060
9126
 
9061
9127
  // Manage vertical aspect
9062
9128
  {
9063
- const overflowContainer = options.overflowContainerY ?? parent.getParentArea();
9129
+ const overflowContainer = options.overflowContainerY !== undefined ? options.overflowContainerY : parent.getParentArea();
9064
9130
  const listHeight = parent.offsetHeight;
9065
9131
  let topPosition = rect.y;
9066
9132
 
@@ -9080,18 +9146,25 @@ class Select extends Widget {
9080
9146
  }
9081
9147
 
9082
9148
  parent.style.top = ( topPosition + selectRoot.offsetHeight ) + 'px';
9149
+ list.style.height = ""; // set auto height by default
9083
9150
 
9084
- const showAbove = ( topPosition + listHeight ) > maxY;
9085
- if( showAbove )
9151
+ const failBelow = ( topPosition + listHeight ) > maxY;
9152
+ const failAbove = ( topPosition - listHeight ) < 0;
9153
+ if( failBelow && !failAbove )
9086
9154
  {
9087
9155
  parent.style.top = ( topPosition - listHeight ) + 'px';
9088
9156
  parent.classList.add( "place-above" );
9089
9157
  }
9158
+ // If does not fit in any direction, put it below but limit height..
9159
+ else if( failBelow && failAbove )
9160
+ {
9161
+ list.style.height = `${ maxY - topPosition - 32 }px`; // 32px margin
9162
+ }
9090
9163
  }
9091
9164
 
9092
9165
  // Manage horizontal aspect
9093
9166
  {
9094
- const overflowContainer = options.overflowContainerX ?? parent.getParentArea();
9167
+ const overflowContainer = options.overflowContainerX !== undefined ? options.overflowContainerX : parent.getParentArea();
9095
9168
  const listWidth = parent.offsetWidth;
9096
9169
  let leftPosition = rect.x;
9097
9170
 
@@ -9519,10 +9592,12 @@ class Layers extends Widget {
9519
9592
  container.style.width = `calc( 100% - ${ realNameWidth })`;
9520
9593
  };
9521
9594
 
9522
- var container = document.createElement( "div" );
9595
+ const container = document.createElement( "div" );
9523
9596
  container.className = "lexlayers";
9524
9597
  this.root.appendChild( container );
9525
9598
 
9599
+ const maxBits = options.maxBits ?? 16;
9600
+
9526
9601
  this.setLayers = ( val ) => {
9527
9602
 
9528
9603
  container.innerHTML = "";
@@ -9531,19 +9606,19 @@ class Layers extends Widget {
9531
9606
  let nbits = binary.length;
9532
9607
 
9533
9608
  // fill zeros
9534
- for( let i = 0; i < ( 16 - nbits ); ++i )
9609
+ for( let i = 0; i < ( maxBits - nbits ); ++i )
9535
9610
  {
9536
9611
  binary = '0' + binary;
9537
9612
  }
9538
9613
 
9539
- for( let bit = 0; bit < 16; ++bit )
9614
+ for( let bit = 0; bit < maxBits; ++bit )
9540
9615
  {
9541
9616
  let layer = document.createElement( "div" );
9542
9617
  layer.className = "lexlayer";
9543
9618
 
9544
9619
  if( val != undefined )
9545
9620
  {
9546
- const valueBit = binary[ 16 - bit - 1 ];
9621
+ const valueBit = binary[ maxBits - bit - 1 ];
9547
9622
  if( valueBit != undefined && valueBit == '1' )
9548
9623
  {
9549
9624
  layer.classList.add( "selected" );
@@ -9551,7 +9626,7 @@ class Layers extends Widget {
9551
9626
  }
9552
9627
 
9553
9628
  layer.innerText = bit + 1;
9554
- layer.title = "Bit " + bit + ", value " + (1 << bit);
9629
+ layer.title = "Bit " + bit + ", value " + ( 1 << bit );
9555
9630
  container.appendChild( layer );
9556
9631
 
9557
9632
  layer.addEventListener( "click", e => {
@@ -10198,9 +10273,7 @@ class ColorInput extends Widget {
10198
10273
  colorModel: options.useRGB ? "RGB" : "Hex",
10199
10274
  useAlpha,
10200
10275
  onChange: ( color ) => {
10201
- this._fromColorPicker = true;
10202
10276
  this.set( color.hex );
10203
- delete this._fromColorPicker;
10204
10277
  }
10205
10278
  } );
10206
10279
 
@@ -10238,6 +10311,7 @@ class ColorInput extends Widget {
10238
10311
  this._skipTextUpdate = true;
10239
10312
  this.set( v );
10240
10313
  delete this._skipTextUpdate;
10314
+ this.picker.fromHexColor( v );
10241
10315
  }, { width: "calc( 100% - 24px )", disabled: options.disabled });
10242
10316
 
10243
10317
  textWidget.root.style.marginLeft = "6px";
@@ -10607,15 +10681,15 @@ class Vector extends Widget {
10607
10681
 
10608
10682
  for( let i = 0; i < vectorInputs.length; ++i )
10609
10683
  {
10610
- let value = newValue[ i ];
10611
- value = LX.clamp( value, +vectorInputs[ i ].min, +vectorInputs[ i ].max );
10612
- value = LX.round( value, options.precision ) ?? 0;
10613
- vectorInputs[ i ].value = newValue[ i ] = value;
10684
+ let vecValue = newValue[ i ];
10685
+ vecValue = LX.clamp( vecValue, +vectorInputs[ i ].min, +vectorInputs[ i ].max );
10686
+ vecValue = LX.round( vecValue, options.precision ) ?? 0;
10687
+ vectorInputs[ i ].value = value[ i ] = vecValue;
10614
10688
  }
10615
10689
 
10616
10690
  if( !skipCallback )
10617
10691
  {
10618
- this._trigger( new LX.IEvent( name, newValue, event ), callback );
10692
+ this._trigger( new LX.IEvent( name, value, event ), callback );
10619
10693
  }
10620
10694
  };
10621
10695
 
@@ -11205,12 +11279,18 @@ class Progress extends Widget {
11205
11279
  };
11206
11280
 
11207
11281
  this.onSetValue = ( newValue, skipCallback, event ) => {
11282
+ newValue = LX.clamp( newValue, progress.min, progress.max );
11208
11283
  this.root.querySelector("meter").value = newValue;
11209
11284
  _updateColor();
11210
11285
  if( this.root.querySelector("span") )
11211
11286
  {
11212
11287
  this.root.querySelector("span").innerText = newValue;
11213
11288
  }
11289
+
11290
+ if( !skipCallback )
11291
+ {
11292
+ this._trigger( new LX.IEvent( name, newValue, event ), options.callback );
11293
+ }
11214
11294
  };
11215
11295
 
11216
11296
  this.onResize = ( rect ) => {
@@ -11294,11 +11374,6 @@ class Progress extends Widget {
11294
11374
  const rect = progress.getBoundingClientRect();
11295
11375
  const newValue = LX.round( LX.remapRange( e.offsetX - rect.x, 0, rect.width, progress.min, progress.max ) );
11296
11376
  this.set( newValue, false, e );
11297
-
11298
- if( options.callback )
11299
- {
11300
- options.callback( newValue, e );
11301
- }
11302
11377
  }
11303
11378
 
11304
11379
  e.stopPropagation();
@@ -11510,9 +11585,16 @@ class TabSections extends Widget {
11510
11585
  throw( "Param @tabs must be an Array!" );
11511
11586
  }
11512
11587
 
11588
+ if( !tabs.length )
11589
+ {
11590
+ throw( "Tab list cannot be empty!" );
11591
+ }
11592
+
11513
11593
  const vertical = options.vertical ?? true;
11514
11594
  const showNames = !vertical && ( options.showNames ?? false );
11515
11595
 
11596
+ this.tabDOMs = {};
11597
+
11516
11598
  let container = document.createElement( 'div' );
11517
11599
  container.className = "lextabscontainer";
11518
11600
  if( !vertical )
@@ -11525,25 +11607,25 @@ class TabSections extends Widget {
11525
11607
  container.appendChild( tabContainer );
11526
11608
  this.root.appendChild( container );
11527
11609
 
11528
- for( let i = 0; i < tabs.length; ++i )
11610
+ // Check at least 1 is selected
11611
+ if( tabs.findIndex( e => e.selected === true ) < 0 )
11612
+ {
11613
+ tabs[ 0 ].selected = true;
11614
+ }
11615
+
11616
+ for( let tab of tabs )
11529
11617
  {
11530
- const tab = tabs[ i ];
11531
11618
  console.assert( tab.name );
11532
- const isSelected = ( i == 0 );
11533
11619
  let tabEl = document.createElement( "div" );
11534
- tabEl.className = "lextab " + (i == tabs.length - 1 ? "last" : "") + ( isSelected ? "selected" : "" );
11620
+ tabEl.className = "lextab " + ( ( tab.selected ?? false ) ? "selected" : "" );
11535
11621
  tabEl.innerHTML = ( showNames ? tab.name : "" );
11536
11622
  tabEl.appendChild( LX.makeIcon( tab.icon ?? "Hash", { title: tab.name, iconClass: tab.iconClass, svgClass: tab.svgClass } ) );
11623
+ this.tabDOMs[ tab.name ] = tabEl;
11537
11624
 
11538
11625
  let infoContainer = document.createElement( "div" );
11539
11626
  infoContainer.id = tab.name.replace( /\s/g, '' );
11540
11627
  infoContainer.className = "widgets";
11541
-
11542
- if( !isSelected )
11543
- {
11544
- infoContainer.toggleAttribute( "hidden", true );
11545
- }
11546
-
11628
+ infoContainer.toggleAttribute( "hidden", !( tab.selected ?? false ) );
11547
11629
  container.appendChild( infoContainer );
11548
11630
 
11549
11631
  tabEl.addEventListener( "click", e => {
@@ -11573,6 +11655,20 @@ class TabSections extends Widget {
11573
11655
  creationPanel.clearQueue();
11574
11656
  }
11575
11657
  }
11658
+
11659
+ this.tabs = tabs;
11660
+ }
11661
+
11662
+ select( name ) {
11663
+
11664
+ const tabEl = this.tabDOMs[ name ];
11665
+
11666
+ if( !tabEl )
11667
+ {
11668
+ return;
11669
+ }
11670
+
11671
+ tabEl.click();
11576
11672
  }
11577
11673
  }
11578
11674
 
@@ -12445,8 +12541,8 @@ class Map2D extends Widget {
12445
12541
  }
12446
12542
  }
12447
12543
 
12448
- LX.Map2D = Map2D;
12449
-
12544
+ LX.Map2D = Map2D;
12545
+
12450
12546
  // panel.js @jxarco
12451
12547
 
12452
12548
  /**
@@ -13631,8 +13727,8 @@ class Panel {
13631
13727
  }
13632
13728
  }
13633
13729
 
13634
- LX.Panel = Panel;
13635
-
13730
+ LX.Panel = Panel;
13731
+
13636
13732
  // branch.js @jxarco
13637
13733
 
13638
13734
  /**
@@ -13856,8 +13952,8 @@ class Branch {
13856
13952
  }
13857
13953
  }
13858
13954
  }
13859
- LX.Branch = Branch;
13860
-
13955
+ LX.Branch = Branch;
13956
+
13861
13957
  // menubar.js @jxarco
13862
13958
 
13863
13959
  /**
@@ -14024,46 +14120,43 @@ class Menubar {
14024
14120
  }
14025
14121
 
14026
14122
  let button = this.buttons[ name ];
14123
+ // If the button already exists, delete it
14124
+ // since only one button of this type can exist
14027
14125
  if( button )
14028
14126
  {
14029
- button.innerHTML = "";
14030
- button.appendChild( LX.makeIcon( icon, { svgClass: "xl" } ) );
14031
- return;
14127
+ delete this.buttons[ name ];
14128
+ LX.deleteElement( button.root );
14032
14129
  }
14033
14130
 
14034
14131
  // Otherwise, create it
14035
- button = document.createElement('div');
14036
- const disabled = options.disabled ?? false;
14037
- button.className = "lexmenubutton main" + (disabled ? " disabled" : "");
14038
- button.title = name;
14039
- button.appendChild( LX.makeIcon( icon, { svgClass: "xl" } ) );
14132
+ button = new LX.Button( name, null, callback, {
14133
+ title: name,
14134
+ buttonClass: "lexmenubutton main bg-none",
14135
+ disabled: options.disabled,
14136
+ icon,
14137
+ svgClass: "xl",
14138
+ hideName: true,
14139
+ swap: options.swap
14140
+ } );
14040
14141
 
14041
14142
  if( options.float == "right" )
14042
14143
  {
14043
- button.right = true;
14144
+ button.root.right = true;
14044
14145
  }
14045
14146
 
14046
14147
  if( this.root.lastChild && this.root.lastChild.right )
14047
14148
  {
14048
- this.root.lastChild.before( button );
14149
+ this.root.lastChild.before( button.root );
14049
14150
  }
14050
14151
  else if( options.float == "left" )
14051
14152
  {
14052
- this.root.prepend( button );
14153
+ this.root.prepend( button.root );
14053
14154
  }
14054
14155
  else
14055
14156
  {
14056
- this.root.appendChild( button );
14157
+ this.root.appendChild( button.root );
14057
14158
  }
14058
14159
 
14059
- const _b = button.querySelector('a');
14060
- _b.addEventListener("click", (e) => {
14061
- if( callback && !disabled )
14062
- {
14063
- callback.call( this, _b, e );
14064
- }
14065
- });
14066
-
14067
14160
  this.buttons[ name ] = button;
14068
14161
  }
14069
14162
 
@@ -14169,13 +14262,13 @@ class Menubar {
14169
14262
 
14170
14263
  if( title )
14171
14264
  {
14172
- this.buttons[ title ] = button.root;
14265
+ this.buttons[ title ] = button;
14173
14266
  }
14174
14267
  }
14175
14268
  }
14176
14269
  }
14177
- LX.Menubar = Menubar;
14178
-
14270
+ LX.Menubar = Menubar;
14271
+
14179
14272
  // sidebar.js @jxarco
14180
14273
 
14181
14274
  /**
@@ -14870,8 +14963,8 @@ class Sidebar {
14870
14963
  }
14871
14964
  }
14872
14965
  }
14873
- LX.Sidebar = Sidebar;
14874
-
14966
+ LX.Sidebar = Sidebar;
14967
+
14875
14968
  // asset_view.js @jxarco
14876
14969
 
14877
14970
  class AssetViewEvent {
@@ -14930,10 +15023,14 @@ class AssetView {
14930
15023
 
14931
15024
  if( options.rootPath )
14932
15025
  {
14933
- if(options.rootPath.constructor !== String)
14934
- console.warn("Asset Root Path must be a String (now is " + path.constructor.name + ")");
15026
+ if( options.rootPath.constructor !== String )
15027
+ {
15028
+ console.warn( `Asset Root Path must be a String (now is a ${ path.constructor.name })` );
15029
+ }
14935
15030
  else
15031
+ {
14936
15032
  this.rootPath = options.rootPath;
15033
+ }
14937
15034
  }
14938
15035
 
14939
15036
  let div = document.createElement('div');
@@ -15302,7 +15399,7 @@ class AssetView {
15302
15399
  this.content.className = (isContentLayout ? "lexassetscontent" : "lexassetscontent list");
15303
15400
  let that = this;
15304
15401
 
15305
- const add_item = function(item) {
15402
+ const _addItem = function(item) {
15306
15403
 
15307
15404
  const type = item.type.charAt( 0 ).toUpperCase() + item.type.slice( 1 );
15308
15405
  const extension = LX.getExtension( item.id );
@@ -15327,20 +15424,27 @@ class AssetView {
15327
15424
  return;
15328
15425
  }
15329
15426
 
15427
+ const dialog = itemEl.closest('dialog');
15330
15428
  const rect = itemEl.getBoundingClientRect();
15331
15429
  const targetRect = e.target.getBoundingClientRect();
15332
- const parentRect = desc.parentElement.getBoundingClientRect();
15333
15430
 
15334
- let localOffsetX = targetRect.x - parentRect.x - ( targetRect.x - rect.x );
15335
- let localOffsetY = targetRect.y - parentRect.y - ( targetRect.y - rect.y );
15431
+ let localOffsetX = rect.x + e.offsetX;
15432
+ let localOffsetY = rect.y + e.offsetY;
15433
+
15434
+ if( dialog )
15435
+ {
15436
+ const dialogRect = dialog.getBoundingClientRect();
15437
+ localOffsetX -= dialogRect.x;
15438
+ localOffsetY -= dialogRect.y;
15439
+ }
15336
15440
 
15337
15441
  if( e.target.classList.contains( "lexassettitle" ) )
15338
15442
  {
15339
15443
  localOffsetY += ( targetRect.y - rect.y );
15340
15444
  }
15341
15445
 
15342
- desc.style.left = (localOffsetX + e.offsetX + 12) + "px";
15343
- desc.style.top = (localOffsetY + e.offsetY) + "px";
15446
+ desc.style.left = ( localOffsetX ) + "px";
15447
+ desc.style.top = ( localOffsetY - 36 ) + "px";
15344
15448
  });
15345
15449
 
15346
15450
  itemEl.addEventListener("mouseenter", () => {
@@ -15538,7 +15642,7 @@ class AssetView {
15538
15642
  } });
15539
15643
  }else
15540
15644
  {
15541
- item.domEl = add_item( item );
15645
+ item.domEl = _addItem( item );
15542
15646
  }
15543
15647
  }
15544
15648
 
@@ -15738,6 +15842,283 @@ class AssetView {
15738
15842
  }
15739
15843
  }
15740
15844
 
15741
- LX.AssetView = AssetView;
15845
+ LX.AssetView = AssetView;
15846
+
15847
+ // tour.js @jxarco
15848
+
15849
+ class Tour {
15850
+
15851
+ /**
15852
+ * @constructor Tour
15853
+ * @param {Array} steps
15854
+ * @param {Object} options
15855
+ * useModal: Use a modal to highlight the tour step [true]
15856
+ * offset: Horizontal and vertical margin offset [0]
15857
+ * horizontalOffset: Horizontal offset [0]
15858
+ * verticalOffset: Vertical offset [0]
15859
+ * radius: Radius for the tour step highlight [8]
15860
+ */
15861
+
15862
+ constructor( steps, options = {} ) {
15863
+
15864
+ this.steps = steps || [];
15865
+ this.currentStep = 0;
15866
+
15867
+ this.useModal = options.useModal ?? true;
15868
+ this.offset = options.offset ?? 8;
15869
+ this.horizontalOffset = options.horizontalOffset;
15870
+ this.verticalOffset = options.verticalOffset;
15871
+ this.radius = options.radius ?? 12;
15872
+
15873
+ this.tourContainer = LX.makeContainer( ["100%", "100%"], "tour-container" );
15874
+ this.tourContainer.style.display = "none";
15875
+ document.body.appendChild( this.tourContainer );
15876
+
15877
+ this.tourMask = LX.makeContainer( ["100%", "100%"], "tour-mask" );
15878
+ }
15879
+
15880
+ /**
15881
+ * @method begin
15882
+ */
15883
+
15884
+ begin() {
15885
+
15886
+ this.currentStep = 0;
15887
+
15888
+ if ( this.useModal )
15889
+ {
15890
+ this.tourMask.style.display = "block";
15891
+ this.tourContainer.appendChild( this.tourMask );
15892
+ }
15893
+
15894
+ this.tourContainer.style.display = "block";
15895
+
15896
+ this._showStep( 0 );
15897
+ }
15898
+
15899
+ /**
15900
+ * @method stop
15901
+ */
15902
+
15903
+ stop() {
15904
+
15905
+ if( this.useModal )
15906
+ {
15907
+ this.tourMask.style.display = "none";
15908
+ this.tourContainer.removeChild( this.tourMask );
15909
+ }
15910
+
15911
+ this._popover?.destroy();
15912
+
15913
+ this.tourContainer.style.display = "none";
15914
+ }
15915
+
15916
+ // Show the current step of the tour
15917
+ _showStep( stepOffset = 1 ) {
15918
+
15919
+ this.currentStep += stepOffset;
15920
+
15921
+ const step = this.steps[ this.currentStep ];
15922
+ if ( !step ) {
15923
+ this.stop();
15924
+ return;
15925
+ }
15926
+
15927
+ const prevStep = this.steps[ this.currentStep - 1 ];
15928
+ const nextStep = this.steps[ this.currentStep + 1 ];
15929
+
15930
+ this._generateMask( step.reference );
15931
+ this._createHighlight( step, prevStep, nextStep );
15932
+ }
15933
+
15934
+ // Generate mask for the specific step reference
15935
+ // using a fullscreen SVG with "rect" elements
15936
+ _generateMask( reference ) {
15742
15937
 
15743
- export { ADD_CUSTOM_WIDGET, Area, AssetView, AssetViewEvent, Branch, LX, Menubar, Panel, Sidebar, Widget };
15938
+ this.tourMask.innerHTML = ""; // Clear previous content
15939
+
15940
+ const svg = document.createElementNS( "http://www.w3.org/2000/svg", "svg" );
15941
+ svg.style.width = "100%";
15942
+ svg.style.height = "100%";
15943
+ this.tourMask.appendChild( svg );
15944
+
15945
+ const clipPath = document.createElementNS( "http://www.w3.org/2000/svg", "clipPath" );
15946
+ clipPath.setAttribute( "id", "svgTourClipPath" );
15947
+ svg.appendChild( clipPath );
15948
+
15949
+ function ceilAndShiftRect( p, s ) {
15950
+ const cp = Math.ceil( p );
15951
+ const delta = cp - p;
15952
+ const ds = s - delta;
15953
+ return [ cp, ds ];
15954
+ }
15955
+
15956
+ const refBounding = reference.getBoundingClientRect();
15957
+ const [ boundingX, boundingWidth ] = ceilAndShiftRect( refBounding.x, refBounding.width );
15958
+ const [ boundingY, boundingHeight ] = ceilAndShiftRect( refBounding.y, refBounding.height );
15959
+
15960
+ const vOffset = this.verticalOffset ?? this.offset;
15961
+ const hOffset = this.horizontalOffset ?? this.offset;
15962
+
15963
+ // Left
15964
+ {
15965
+ const rect = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
15966
+ rect.setAttribute( "x", 0 );
15967
+ rect.setAttribute( "y", 0 );
15968
+ rect.setAttribute( "width", boundingX - hOffset );
15969
+ rect.setAttribute( "height", window.innerHeight );
15970
+ rect.setAttribute( "stroke", "none" );
15971
+ clipPath.appendChild( rect );
15972
+ }
15973
+
15974
+ // Top
15975
+ {
15976
+ const rect = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
15977
+ rect.setAttribute( "x", boundingX - hOffset );
15978
+ rect.setAttribute( "y", 0 );
15979
+ rect.setAttribute( "width", boundingWidth + hOffset * 2 );
15980
+ rect.setAttribute( "height", boundingY - vOffset );
15981
+ rect.setAttribute( "stroke", "none" );
15982
+ clipPath.appendChild( rect );
15983
+ }
15984
+
15985
+ // Bottom
15986
+ {
15987
+ const rect = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
15988
+ rect.setAttribute( "x", boundingX - hOffset );
15989
+ rect.setAttribute( "y", boundingY + boundingHeight + vOffset );
15990
+ rect.setAttribute( "width", boundingWidth + hOffset * 2 );
15991
+ rect.setAttribute( "height", window.innerHeight - boundingY - boundingHeight - vOffset );
15992
+ rect.setAttribute( "stroke", "none" );
15993
+ clipPath.appendChild( rect );
15994
+ }
15995
+
15996
+ // Right
15997
+ {
15998
+ const rect = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
15999
+ rect.setAttribute( "x", boundingX + boundingWidth + hOffset );
16000
+ rect.setAttribute( "y", 0 );
16001
+ rect.setAttribute( "width", window.innerWidth - boundingX - boundingWidth );
16002
+ rect.setAttribute( "height", window.innerHeight );
16003
+ rect.setAttribute( "stroke", "none" );
16004
+ clipPath.appendChild( rect );
16005
+ }
16006
+
16007
+ // Reference Highlight
16008
+ const refContainer = LX.makeContainer( ["0", "0"], "tour-ref-mask" );
16009
+ refContainer.style.left = `${ boundingX - hOffset - 1 }px`;
16010
+ refContainer.style.top = `${ boundingY - vOffset - 1 }px`;
16011
+ refContainer.style.width = `${ boundingWidth + hOffset * 2 + 2 }px`;
16012
+ refContainer.style.height = `${ boundingHeight + vOffset * 2 + 2}px`;
16013
+ this.tourContainer.appendChild( refContainer );
16014
+
16015
+ const referenceMask = document.createElementNS( "http://www.w3.org/2000/svg", "mask" );
16016
+ referenceMask.setAttribute( "id", "svgTourReferenceMask" );
16017
+ svg.appendChild( referenceMask );
16018
+
16019
+ // Reference Mask
16020
+ {
16021
+ const rectWhite = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
16022
+ rectWhite.setAttribute( "width", boundingWidth + hOffset * 2 + 2 );
16023
+ rectWhite.setAttribute( "height", boundingHeight + vOffset * 2 + 2);
16024
+ rectWhite.setAttribute( "stroke", "none" );
16025
+ rectWhite.setAttribute( "fill", "white" );
16026
+ referenceMask.appendChild( rectWhite );
16027
+
16028
+ const rectBlack = document.createElementNS( "http://www.w3.org/2000/svg", "rect" );
16029
+ rectBlack.setAttribute( "rx", this.radius );
16030
+ rectBlack.setAttribute( "width", boundingWidth + hOffset * 2 + 2);
16031
+ rectBlack.setAttribute( "height", boundingHeight + vOffset * 2 + 2);
16032
+ rectBlack.setAttribute( "stroke", "none" );
16033
+ rectBlack.setAttribute( "fill", "black" );
16034
+ referenceMask.appendChild( rectBlack );
16035
+ }
16036
+ }
16037
+
16038
+ // Create the container with the user hints
16039
+ _createHighlight( step, previousStep, nextStep ) {
16040
+
16041
+ const popoverContainer = LX.makeContainer( ["auto", "auto"], "tour-step-container" );
16042
+
16043
+ {
16044
+ const header = LX.makeContainer( ["100%", "auto"], "flex flex-row", "", popoverContainer );
16045
+ LX.makeContainer( ["70%", "auto"], "p-2 font-medium", step.title, header );
16046
+ const closer = LX.makeContainer( ["30%", "auto"], "flex flex-row p-2 justify-end", "", header );
16047
+ const closeIcon = LX.makeIcon( "X" );
16048
+ closer.appendChild( closeIcon );
16049
+
16050
+ closeIcon.listen( "click", () => {
16051
+ this.stop();
16052
+ } );
16053
+ }
16054
+
16055
+ LX.makeContainer( ["100%", "auto"], "p-2 text-md", step.content, popoverContainer, { maxWidth: "400px" } );
16056
+ const footer = LX.makeContainer( ["100%", "auto"], "flex flex-row text-md", "", popoverContainer );
16057
+
16058
+ {
16059
+ const footerSteps = LX.makeContainer( ["50%", "auto"], "p-2 gap-1 self-center flex flex-row text-md", "", footer );
16060
+ for( let i = 0; i < this.steps.length; i++ )
16061
+ {
16062
+ const stepIndicator = document.createElement( "span" );
16063
+ stepIndicator.className = "tour-step-indicator";
16064
+ if( i === this.currentStep )
16065
+ {
16066
+ stepIndicator.classList.add( "active" );
16067
+ }
16068
+ footerSteps.appendChild( stepIndicator );
16069
+ }
16070
+ }
16071
+
16072
+ const footerButtons = LX.makeContainer( ["50%", "auto"], "text-md", "", footer );
16073
+ const footerPanel = new LX.Panel();
16074
+
16075
+ let numButtons = 1;
16076
+
16077
+ if( previousStep )
16078
+ {
16079
+ numButtons++;
16080
+ }
16081
+
16082
+ if( numButtons > 1 )
16083
+ {
16084
+ footerPanel.sameLine( 2, "justify-end" );
16085
+ }
16086
+
16087
+ if( previousStep )
16088
+ {
16089
+ footerPanel.addButton( null, "Previous", () => {
16090
+ this._showStep( -1 );
16091
+ }, { buttonClass: "contrast" } );
16092
+ }
16093
+
16094
+ if( nextStep )
16095
+ {
16096
+ footerPanel.addButton( null, "Next", () => {
16097
+ this._showStep( 1 );
16098
+ }, { buttonClass: "accent" } );
16099
+ }
16100
+ else
16101
+ {
16102
+ footerPanel.addButton( null, "Finish", () => {
16103
+ this.stop();
16104
+ } );
16105
+ }
16106
+
16107
+ footerButtons.appendChild( footerPanel.root );
16108
+
16109
+ const sideOffset = ( step.side === "left" || step.side === "right" ? this.horizontalOffset : this.verticalOffset ) ?? this.offset;
16110
+ const alignOffset = ( step.align === "start" || step.align === "end" ? sideOffset : 0 );
16111
+
16112
+ this._popover?.destroy();
16113
+ this._popover = new LX.Popover( null, [ popoverContainer ], {
16114
+ reference: step.reference,
16115
+ side: step.side,
16116
+ align: step.align,
16117
+ sideOffset,
16118
+ alignOffset: step.align === "start" ? -alignOffset : alignOffset,
16119
+ } );
16120
+ }
16121
+ }
16122
+ LX.Tour = Tour;
16123
+
16124
+ export { ADD_CUSTOM_WIDGET, Area, AssetView, AssetViewEvent, Branch, LX, Menubar, Panel, Sidebar, Tour, Widget };