lexgui 0.7.11 → 0.7.13

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.css CHANGED
@@ -2753,7 +2753,6 @@ input[type=number] {
2753
2753
  font-size: var(--global-font-size);
2754
2754
  font-family: var(--global-font);
2755
2755
  line-height: 23px;
2756
- margin-left: -2px;
2757
2756
  }
2758
2757
 
2759
2758
  .lextree .lextreeitem {
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.7.11",
17
+ version: "0.7.13",
18
18
  ready: false,
19
19
  extensions: [], // Store extensions used
20
20
  signals: {}, // Events and triggers
@@ -5329,11 +5329,12 @@ LX.guidGenerator = guidGenerator;
5329
5329
  function buildTextPattern( options = {} )
5330
5330
  {
5331
5331
  let patterns = [];
5332
- if ( options.lowercase ) patterns.push("(?=.*[a-z])");
5333
- if ( options.uppercase ) patterns.push("(?=.*[A-Z])");
5334
- if ( options.digit ) patterns.push("(?=.*\\d)");
5335
- if ( options.specialChar ) patterns.push("(?=.*[@#$%^&+=!])");
5336
- if ( options.noSpaces ) patterns.push("(?!.*\\s)");
5332
+ if( options.lowercase ) patterns.push("(?=.*[a-z])");
5333
+ if( options.uppercase ) patterns.push("(?=.*[A-Z])");
5334
+ if( options.digit ) patterns.push("(?=.*\\d)");
5335
+ if( options.specialChar ) patterns.push("(?=.*[@#$%^&+=!])");
5336
+ if( options.noSpaces ) patterns.push("(?!.*\\s)");
5337
+ if( options.email ) patterns.push("(^[^\s@]+@[^\s@]+\.[^\s@]+$)");
5337
5338
 
5338
5339
  let minLength = options.minLength || 0;
5339
5340
  let maxLength = options.maxLength || ""; // Empty means no max length restriction
@@ -5344,6 +5345,45 @@ function buildTextPattern( options = {} )
5344
5345
 
5345
5346
  LX.buildTextPattern = buildTextPattern;
5346
5347
 
5348
+ /**
5349
+ * Checks a value against a set of pattern requirements and returns an array
5350
+ * of specific error messages for all criteria that failed.
5351
+ * @param { String } value The string to validate.
5352
+ * @param { Object } pattern The pattern options
5353
+ * @returns { Array } An array of error messages for failed criteria.
5354
+ */
5355
+ function validateValueAtPattern( value, pattern = {}, ...args )
5356
+ {
5357
+ const errors = [];
5358
+ const minLength = pattern.minLength || 0;
5359
+ const maxLength = pattern.maxLength; // undefined means no max limit
5360
+
5361
+ // Length requirements
5362
+ if( value.length < minLength ) errors.push(`Must be at least ${ minLength } characters long.`);
5363
+ else if( maxLength !== undefined && value.length > maxLength ) errors.push(`Must be no more than ${ maxLength } characters long.`);
5364
+
5365
+ // Check for Lowercase, Uppercase, Digits
5366
+ if( pattern.lowercase && !/[a-z]/.test( value ) ) errors.push( "Must contain at least one lowercase letter (a-z)." );
5367
+ if( pattern.uppercase && !/[A-Z]/.test( value ) ) errors.push( "Must contain at least one uppercase letter (A-Z)." );
5368
+ if( pattern.digit && !/\d/.test( value ) ) errors.push( "Must contain at least one number (0-9)." );
5369
+
5370
+ // Check for No Spaces (The original regex was (?!.*\s), meaning 'not followed by any character and a space')
5371
+ if( pattern.noSpaces && /\s/.test(value)) errors.push("Must NOT contain any spaces.");
5372
+
5373
+ // Check for Special Character (using the same set as buildTextPattern)
5374
+ if( pattern.specialChar && !/[@#$%^&+=!]/.test( value ) ) errors.push("Must contain at least one special character (e.g., @, #, $, %, ^, &, +, =, !).");
5375
+
5376
+ // Check email formatting
5377
+ if( pattern.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test( value ) ) errors.push("Must have a valid email format.");
5378
+
5379
+ // Check match to any other text word
5380
+ if( pattern.fieldMatchName && value !== ( args[ 0 ] ) ) errors.push(`Must match ${ pattern.fieldMatchName } field.`);
5381
+
5382
+ return errors;
5383
+ }
5384
+
5385
+ LX.validateValueAtPattern = validateValueAtPattern;
5386
+
5347
5387
  /**
5348
5388
  * @method makeDraggable
5349
5389
  * @description Allows an element to be dragged
@@ -9062,7 +9102,14 @@ class TextInput extends BaseComponent
9062
9102
 
9063
9103
  this.onSetValue = ( newValue, skipCallback, event ) => {
9064
9104
 
9065
- if( !this.valid( newValue ) || ( this._lastValueTriggered == newValue ) )
9105
+ let skipTrigger = ( this._lastValueTriggered == newValue );
9106
+
9107
+ if( !options.ignoreValidation )
9108
+ {
9109
+ skipTrigger |= ( !this.valid( newValue ) );
9110
+ }
9111
+
9112
+ if( skipTrigger )
9066
9113
  {
9067
9114
  return;
9068
9115
  }
@@ -9082,11 +9129,11 @@ class TextInput extends BaseComponent
9082
9129
  container.style.width = options.inputWidth ?? `calc( 100% - ${ realNameWidth })`;
9083
9130
  };
9084
9131
 
9085
- this.valid = ( v ) => {
9132
+ this.valid = ( v, matchField ) => {
9086
9133
  v = v ?? this.value();
9087
- if( ( wValue.pattern ?? "" ) == "" ) return true;
9088
- const regexp = new RegExp( wValue.pattern );
9089
- return regexp.test( v );
9134
+ if( !options.pattern ) return true;
9135
+ const errs = LX.validateValueAtPattern( v, options.pattern, matchField );
9136
+ return ( errs.length == 0 );
9090
9137
  };
9091
9138
 
9092
9139
  let container = document.createElement( 'div' );
@@ -9115,7 +9162,7 @@ class TextInput extends BaseComponent
9115
9162
 
9116
9163
  if( options.pattern )
9117
9164
  {
9118
- wValue.setAttribute( "pattern", options.pattern );
9165
+ wValue.setAttribute( "pattern", LX.buildTextPattern( options.pattern ) );
9119
9166
  }
9120
9167
 
9121
9168
  const trigger = options.trigger ?? "default";
@@ -9785,13 +9832,14 @@ class Form extends BaseComponent
9785
9832
 
9786
9833
  if( entryData.constructor != Object )
9787
9834
  {
9788
- const oldValue = JSON.parse( JSON.stringify( entryData ) );
9835
+ const oldValue = LX.deepCopy( entryData );
9789
9836
  entryData = { value: oldValue };
9790
9837
  data[ entry ] = entryData;
9791
9838
  }
9792
9839
 
9793
- entryData.placeholder = entryData.placeholder ?? ( entryData.label ?? `Enter ${ entry }` );
9794
9840
  entryData.width = "100%";
9841
+ entryData.placeholder = entryData.placeholder ?? ( entryData.label ?? `Enter ${ entry }` );
9842
+ entryData.ignoreValidation = true;
9795
9843
 
9796
9844
  if( !( options.skipLabels ?? false ) )
9797
9845
  {
@@ -9807,14 +9855,14 @@ class Form extends BaseComponent
9807
9855
  container.formData[ entry ] = entryData.constructor == Object ? entryData.value : entryData;
9808
9856
  }
9809
9857
 
9810
- const buttonContainer = LX.makeContainer( ["100%", "auto"], "flex flex-row", "", container );
9858
+ const buttonContainer = LX.makeContainer( ["100%", "auto"], "flex flex-row mt-2", "", container );
9811
9859
 
9812
9860
  if( options.secondaryActionName || options.secondaryActionCallback )
9813
9861
  {
9814
9862
  const secondaryButton = new LX.Button( null, options.secondaryActionName ?? "Cancel", ( value, event ) => {
9815
- if( callback )
9863
+ if( options.secondaryActionCallback )
9816
9864
  {
9817
- callback( container.formData, event );
9865
+ options.secondaryActionCallback( container.formData, event );
9818
9866
  }
9819
9867
  }, { width: "100%", minWidth: "0", buttonClass: options.secondaryButtonClass ?? "primary" } );
9820
9868
 
@@ -9829,9 +9877,22 @@ class Form extends BaseComponent
9829
9877
  {
9830
9878
  let entryData = data[ entry ];
9831
9879
 
9832
- if( !entryData.textComponent.valid() )
9880
+ const pattern = entryData.pattern;
9881
+ const matchField = pattern?.fieldMatchName ? container.formData[ pattern.fieldMatchName ] : undefined;
9882
+
9883
+ if( !entryData.textComponent.valid( undefined, matchField ) )
9833
9884
  {
9834
- errors.push( { type: "input_not_valid", entry } );
9885
+ const err = { entry, type: "input_not_valid" };
9886
+ err.messages = [];
9887
+ if( pattern )
9888
+ {
9889
+ err.messages = LX.validateValueAtPattern(
9890
+ container.formData[ entry ],
9891
+ pattern,
9892
+ matchField
9893
+ );
9894
+ }
9895
+ errors.push( err );
9835
9896
  }
9836
9897
  }
9837
9898
 
@@ -12705,6 +12766,7 @@ class Table extends BaseComponent
12705
12766
  this.filter = options.filter ?? false;
12706
12767
  this.customFilters = options.customFilters ?? false;
12707
12768
  this._toggleColumns = options.toggleColumns ?? false;
12769
+ this._sortColumns = options.sortColumns ?? true;
12708
12770
  this._currentFilter = options.filterValue;
12709
12771
 
12710
12772
  data.head = data.head ?? [];
@@ -12971,28 +13033,62 @@ class Table extends BaseComponent
12971
13033
  th.classList.add( "centered" );
12972
13034
  }
12973
13035
 
12974
- const menuOptions = [
12975
- { name: "Asc", icon: "ArrowUpAZ", callback: sortFn.bind( this, idx, 1 ) },
12976
- { name: "Desc", icon: "ArrowDownAZ", callback: sortFn.bind( this, idx, -1 ) }
12977
- ];
13036
+ const menuOptions = [];
12978
13037
 
12979
- if( this._toggleColumns )
13038
+ if( options.columnActions )
12980
13039
  {
12981
- menuOptions.push(
12982
- null,
13040
+ for( let action of options.columnActions )
13041
+ {
13042
+ if( !action.name )
12983
13043
  {
12984
- name: "Hide", icon: "EyeOff", callback: () => {
12985
- data.colVisibilityMap[ idx ] = false;
12986
- const cells = table.querySelectorAll(`tr > *:nth-child(${idx + this.rowOffsetCount + 1})`);
12987
- cells.forEach( cell => {
12988
- cell.style.display = ( cell.style.display === "none" ) ? "" : "none";
12989
- } );
12990
- }
13044
+ console.warn( "Invalid column action (missing name):", action );
13045
+ continue;
12991
13046
  }
13047
+
13048
+ menuOptions.push( { name: action.name, icon: action.icon, className: action.className, callback: () => {
13049
+ const colRows = this.data.body.map( row => [ row[ idx ] ] );
13050
+ const mustRefresh = action.callback( colRows, table );
13051
+ if( mustRefresh )
13052
+ {
13053
+ this.refresh();
13054
+ }
13055
+ } } );
13056
+ }
13057
+ }
13058
+
13059
+ if( this._sortColumns )
13060
+ {
13061
+ if( menuOptions.length > 0 )
13062
+ {
13063
+ menuOptions.push( null );
13064
+ }
13065
+
13066
+ menuOptions.push(
13067
+ { name: "Asc", icon: "ArrowUpAZ", callback: sortFn.bind( this, idx, 1 ) },
13068
+ { name: "Desc", icon: "ArrowDownAZ", callback: sortFn.bind( this, idx, -1 ) }
12992
13069
  );
12993
13070
  }
12994
13071
 
13072
+ if( this._toggleColumns )
13073
+ {
13074
+ if( menuOptions.length > 0 )
13075
+ {
13076
+ menuOptions.push( null );
13077
+ }
13078
+
13079
+ menuOptions.push( {
13080
+ name: "Hide", icon: "EyeOff", callback: () => {
13081
+ data.colVisibilityMap[ idx ] = false;
13082
+ const cells = table.querySelectorAll(`tr > *:nth-child(${idx + this.rowOffsetCount + 1})`);
13083
+ cells.forEach( cell => {
13084
+ cell.style.display = ( cell.style.display === "none" ) ? "" : "none";
13085
+ } );
13086
+ }
13087
+ } );
13088
+ }
13089
+
12995
13090
  th.addEventListener( 'click', event => {
13091
+ if( menuOptions.length === 0 ) return;
12996
13092
  new LX.DropdownMenu( event.target, menuOptions, { side: "bottom", align: "start" });
12997
13093
  });
12998
13094