lexgui 0.6.10 → 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.
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  const LX = {
10
- version: "0.6.10",
10
+ version: "0.6.11",
11
11
  ready: false,
12
12
  components: [], // Specific pre-build components
13
13
  signals: {}, // Events and triggers
@@ -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 )
@@ -8955,16 +8976,17 @@ class Form extends Widget {
8955
8976
 
8956
8977
  if( entryData.constructor != Object )
8957
8978
  {
8958
- entryData = { };
8979
+ const oldValue = JSON.parse( JSON.stringify( entryData ) );
8980
+ entryData = { value: oldValue };
8959
8981
  data[ entry ] = entryData;
8960
8982
  }
8961
8983
 
8962
- entryData.placeholder = entryData.placeholder ?? entry;
8984
+ entryData.placeholder = entryData.placeholder ?? ( entryData.label ?? `Enter ${ entry }` );
8963
8985
  entryData.width = "100%";
8964
8986
 
8965
8987
  if( !( options.skipLabels ?? false ) )
8966
8988
  {
8967
- 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" } );
8968
8990
  container.appendChild( label.root );
8969
8991
  }
8970
8992
 
@@ -10659,15 +10681,15 @@ class Vector extends Widget {
10659
10681
 
10660
10682
  for( let i = 0; i < vectorInputs.length; ++i )
10661
10683
  {
10662
- let value = newValue[ i ];
10663
- value = LX.clamp( value, +vectorInputs[ i ].min, +vectorInputs[ i ].max );
10664
- value = LX.round( value, options.precision ) ?? 0;
10665
- 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;
10666
10688
  }
10667
10689
 
10668
10690
  if( !skipCallback )
10669
10691
  {
10670
- this._trigger( new LX.IEvent( name, newValue, event ), callback );
10692
+ this._trigger( new LX.IEvent( name, value, event ), callback );
10671
10693
  }
10672
10694
  };
10673
10695
 
@@ -11257,12 +11279,18 @@ class Progress extends Widget {
11257
11279
  };
11258
11280
 
11259
11281
  this.onSetValue = ( newValue, skipCallback, event ) => {
11282
+ newValue = LX.clamp( newValue, progress.min, progress.max );
11260
11283
  this.root.querySelector("meter").value = newValue;
11261
11284
  _updateColor();
11262
11285
  if( this.root.querySelector("span") )
11263
11286
  {
11264
11287
  this.root.querySelector("span").innerText = newValue;
11265
11288
  }
11289
+
11290
+ if( !skipCallback )
11291
+ {
11292
+ this._trigger( new LX.IEvent( name, newValue, event ), options.callback );
11293
+ }
11266
11294
  };
11267
11295
 
11268
11296
  this.onResize = ( rect ) => {
@@ -11346,11 +11374,6 @@ class Progress extends Widget {
11346
11374
  const rect = progress.getBoundingClientRect();
11347
11375
  const newValue = LX.round( LX.remapRange( e.offsetX - rect.x, 0, rect.width, progress.min, progress.max ) );
11348
11376
  this.set( newValue, false, e );
11349
-
11350
- if( options.callback )
11351
- {
11352
- options.callback( newValue, e );
11353
- }
11354
11377
  }
11355
11378
 
11356
11379
  e.stopPropagation();
@@ -15821,4 +15844,281 @@ class AssetView {
15821
15844
 
15822
15845
  LX.AssetView = AssetView;
15823
15846
 
15824
- export { ADD_CUSTOM_WIDGET, Area, AssetView, AssetViewEvent, Branch, LX, Menubar, Panel, Sidebar, Widget };
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 ) {
15937
+
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 };