lexgui 0.1.45 → 0.1.46

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.
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  var LX = {
11
- version: "0.1.45",
11
+ version: "0.1.46",
12
12
  ready: false,
13
13
  components: [], // specific pre-build components
14
14
  signals: {} // events and triggers
@@ -32,34 +32,103 @@ LX.clamp = clamp;
32
32
  LX.round = round;
33
33
  LX.remapRange = remapRange;
34
34
 
35
- function getSupportedDOMName( string )
35
+ // Timer that works everywhere (from litegraph.js)
36
+ if ( typeof performance != "undefined" )
37
+ {
38
+ LX.getTime = performance.now.bind( performance );
39
+ }
40
+ else if( typeof Date != "undefined" && Date.now )
41
+ {
42
+ LX.getTime = Date.now.bind( Date );
43
+ }
44
+ else if ( typeof process != "undefined" )
45
+ {
46
+ LX.getTime = function() {
47
+ var t = process.hrtime();
48
+ return t[ 0 ] * 0.001 + t[ 1 ] * 1e-6;
49
+ };
50
+ }
51
+ else
36
52
  {
37
- return string.replace(/\s/g, '').replaceAll('@', '_').replaceAll('+', '_plus_').replaceAll('.', '');
53
+ LX.getTime = function() {
54
+ return new Date().getTime();
55
+ };
56
+ }
57
+
58
+ let ASYNC_ENABLED = true;
59
+
60
+ /**
61
+ * @method doAsync
62
+ * @description Call a function asynchronously
63
+ * @param {Function} fn Function to call
64
+ * @param {Number} ms Time to wait until calling the function (in milliseconds)
65
+ */
66
+ function doAsync( fn, ms ) {
67
+ if( ASYNC_ENABLED )
68
+ {
69
+ setTimeout( fn, ms ?? 0 );
70
+ }
71
+ else
72
+ {
73
+ fn();
74
+ }
75
+ }
76
+
77
+ LX.doAsync = doAsync;
78
+
79
+ /**
80
+ * @method getSupportedDOMName
81
+ * @description Convert a text string to a valid DOM name
82
+ * @param {String} text Original text
83
+ */
84
+ function getSupportedDOMName( text )
85
+ {
86
+ return text.replace(/\s/g, '').replaceAll('@', '_').replaceAll('+', '_plus_').replaceAll('.', '');
38
87
  }
39
88
 
40
89
  LX.getSupportedDOMName = getSupportedDOMName;
41
90
 
42
- function has( component_name )
91
+ /**
92
+ * @method has
93
+ * @description Ask if LexGUI is using a specific component
94
+ * @param {String} componentName Name of the LexGUI component
95
+ */
96
+ function has( componentName )
43
97
  {
44
- return (LX.components.indexOf( component_name ) > -1);
98
+ return ( LX.components.indexOf( componentName ) > -1 );
45
99
  }
46
100
 
47
101
  LX.has = has;
48
102
 
49
- function getExtension( s )
103
+ /**
104
+ * @method getExtension
105
+ * @description Get a extension from a path/url/filename
106
+ * @param {String} name
107
+ */
108
+ function getExtension( name )
50
109
  {
51
- return s.includes('.') ? s.split('.').pop() : null;
110
+ return name.includes('.') ? name.split('.').pop() : null;
52
111
  }
53
112
 
54
113
  LX.getExtension = getExtension;
55
114
 
56
- function deepCopy( o )
115
+ /**
116
+ * @method deepCopy
117
+ * @description Create a deep copy with no references from an object
118
+ * @param {Object} obj
119
+ */
120
+ function deepCopy( obj )
57
121
  {
58
- return JSON.parse(JSON.stringify(o))
122
+ return JSON.parse( JSON.stringify( obj ) )
59
123
  }
60
124
 
61
125
  LX.deepCopy = deepCopy;
62
126
 
127
+ /**
128
+ * @method setTheme
129
+ * @description Set dark or light theme
130
+ * @param {String} colorScheme Name of the scheme
131
+ */
63
132
  function setTheme( colorScheme )
64
133
  {
65
134
  colorScheme = ( colorScheme == "light" ) ? "light" : "dark";
@@ -69,6 +138,12 @@ function setTheme( colorScheme )
69
138
 
70
139
  LX.setTheme = setTheme;
71
140
 
141
+ /**
142
+ * @method setThemeColor
143
+ * @description Sets a new value for one of the main theme variables
144
+ * @param {String} colorName Name of the theme variable
145
+ * @param {String} color Color in rgba/hex
146
+ */
72
147
  function setThemeColor( colorName, color )
73
148
  {
74
149
  var r = document.querySelector( ':root' );
@@ -77,6 +152,11 @@ function setThemeColor( colorName, color )
77
152
 
78
153
  LX.setThemeColor = setThemeColor;
79
154
 
155
+ /**
156
+ * @method getThemeColor
157
+ * @description Get the value for one of the main theme variables
158
+ * @param {String} colorName Name of the theme variable
159
+ */
80
160
  function getThemeColor( colorName )
81
161
  {
82
162
  const r = getComputedStyle( document.querySelector( ':root' ) );
@@ -101,7 +181,13 @@ function getThemeColor( colorName )
101
181
 
102
182
  LX.getThemeColor = getThemeColor;
103
183
 
104
- function getBase64Image( img ) {
184
+ /**
185
+ * @method getBase64Image
186
+ * @description Convert an image to a base64 string
187
+ * @param {Image} img
188
+ */
189
+ function getBase64Image( img )
190
+ {
105
191
  var canvas = document.createElement( 'canvas' );
106
192
  canvas.width = img.width;
107
193
  canvas.height = img.height;
@@ -112,7 +198,13 @@ function getBase64Image( img ) {
112
198
 
113
199
  LX.getBase64Image = getBase64Image;
114
200
 
115
- function hexToRgb( hexStr ) {
201
+ /**
202
+ * @method hexToRgb
203
+ * @description Convert a hexadecimal string to a valid RGB color array
204
+ * @param {String} hexStr Hexadecimal color
205
+ */
206
+ function hexToRgb( hexStr )
207
+ {
116
208
  const red = parseInt( hexStr.substring( 1, 3 ), 16 ) / 255;
117
209
  const green = parseInt( hexStr.substring( 3, 5 ), 16 ) / 255;
118
210
  const blue = parseInt( hexStr.substring( 5, 7 ), 16 ) / 255;
@@ -121,7 +213,13 @@ function hexToRgb( hexStr ) {
121
213
 
122
214
  LX.hexToRgb = hexToRgb;
123
215
 
124
- function rgbToHex( rgb ) {
216
+ /**
217
+ * @method rgbToHex
218
+ * @description Convert a RGB color array to a hexadecimal string
219
+ * @param {Array} rgb Array containing R, G, B, A*
220
+ */
221
+ function rgbToHex( rgb )
222
+ {
125
223
  let hex = "#";
126
224
  for( let c of rgb ) {
127
225
  c = Math.floor( c * 255 );
@@ -132,7 +230,14 @@ function rgbToHex( rgb ) {
132
230
 
133
231
  LX.rgbToHex = rgbToHex;
134
232
 
135
- function measureRealWidth( value, paddingPlusMargin = 8 ) {
233
+ /**
234
+ * @method measureRealWidth
235
+ * @description Measure the pixel width of a text
236
+ * @param {Object} value Text to measure
237
+ * @param {Number} paddingPlusMargin Padding offset
238
+ */
239
+ function measureRealWidth( value, paddingPlusMargin = 8 )
240
+ {
136
241
  var i = document.createElement( "span" );
137
242
  i.className = "lexinputmeasure";
138
243
  i.innerHTML = value;
@@ -144,7 +249,12 @@ function measureRealWidth( value, paddingPlusMargin = 8 ) {
144
249
 
145
250
  LX.measureRealWidth = measureRealWidth;
146
251
 
147
- function simple_guidGenerator() {
252
+ /**
253
+ * @method simple_guidGenerator
254
+ * @description Get a random unique id
255
+ */
256
+ function simple_guidGenerator()
257
+ {
148
258
  var S4 = function() {
149
259
  return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
150
260
  };
@@ -153,7 +263,21 @@ function simple_guidGenerator() {
153
263
 
154
264
  LX.guidGenerator = simple_guidGenerator;
155
265
 
156
- function buildTextPattern( options = {} ) {
266
+ /**
267
+ * @method buildTextPattern
268
+ * @description Create a validation pattern using specific options
269
+ * @param {Object} options
270
+ * lowercase (Boolean): Text must contain a lowercase char
271
+ * uppercase (Boolean): Text must contain an uppercase char
272
+ * digit (Boolean): Text must contain a digit
273
+ * specialChar (Boolean): Text must contain a special char
274
+ * noSpaces (Boolean): Do not allow spaces in text
275
+ * minLength (Number): Text minimum length
276
+ * maxLength (Number): Text maximum length
277
+ * asRegExp (Boolean): Return pattern as Regular Expression instance
278
+ */
279
+ function buildTextPattern( options = {} )
280
+ {
157
281
  let patterns = [];
158
282
  if ( options.lowercase ) patterns.push("(?=.*[a-z])");
159
283
  if ( options.uppercase ) patterns.push("(?=.*[A-Z])");
@@ -170,67 +294,9 @@ function buildTextPattern( options = {} ) {
170
294
 
171
295
  LX.buildTextPattern = buildTextPattern;
172
296
 
173
- // Timer that works everywhere (from litegraph.js)
174
- if (typeof performance != "undefined") {
175
- LX.getTime = performance.now.bind(performance);
176
- } else if (typeof Date != "undefined" && Date.now) {
177
- LX.getTime = Date.now.bind(Date);
178
- } else if (typeof process != "undefined") {
179
- LX.getTime = function() {
180
- var t = process.hrtime();
181
- return t[0] * 0.001 + t[1] * 1e-6;
182
- };
183
- } else {
184
- LX.getTime = function getTime() {
185
- return new Date().getTime();
186
- };
187
- }
188
-
189
- let ASYNC_ENABLED = true;
190
-
191
- function doAsync( fn, ms ) {
192
- if( ASYNC_ENABLED )
193
- {
194
- setTimeout( fn, ms ?? 0 );
195
- }
196
- else
197
- {
198
- fn();
199
- }
200
- }
201
-
202
- // Math classes
203
-
204
- class vec2 {
205
-
206
- constructor( x, y ) {
207
- this.x = x ?? 0;
208
- this.y = y ?? ( x ?? 0 );
209
- }
210
-
211
- get xy() { return [ this.x, this.y ]; }
212
- get yx() { return [ this.y, this.x ]; }
213
-
214
- set ( x, y ) { this.x = x; this.y = y; }
215
- add ( v, v0 = new vec2() ) { v0.set( this.x + v.x, this.y + v.y ); return v0; }
216
- sub ( v, v0 = new vec2() ) { v0.set( this.x - v.x, this.y - v.y ); return v0; }
217
- mul ( v, v0 = new vec2() ) { if( v.constructor == Number ) { v = new vec2( v ) } v0.set( this.x * v.x, this.y * v.y ); return v0; }
218
- div ( v, v0 = new vec2() ) { if( v.constructor == Number ) { v = new vec2( v ) } v0.set( this.x / v.x, this.y / v.y ); return v0; }
219
- abs ( v0 = new vec2() ) { v0.set( Math.abs( this.x ), Math.abs( this.y ) ); return v0; }
220
- dot ( v ) { return this.x * v.x + this.y * v.y; }
221
- len2 () { return this.dot( this ) }
222
- len () { return Math.sqrt( this.len2() ); }
223
- nrm ( v0 = new vec2() ) { v0.set( this.x, this.y ); return v0.mul( 1.0 / this.len(), v0 ); }
224
- dst ( v ) { return v.sub( this ).len(); }
225
- clp ( min, max, v0 = new vec2() ) { v0.set( clamp( this.x, min, max ), clamp( this.y, min, max ) ); return v0; }
226
- };
227
-
228
- LX.vec2 = vec2;
229
-
230
- // Other utils
231
-
232
297
  /**
233
298
  * @method makeDraggable
299
+ * @description Allow an element to be dragged
234
300
  * @param {Element} domEl
235
301
  * @param {Object} options
236
302
  * autoAdjust (Bool): Sets in a correct position at the beggining
@@ -238,8 +304,8 @@ LX.vec2 = vec2;
238
304
  * onMove (Function): Called each move event
239
305
  * onDragStart (Function): Called when drag event starts
240
306
  */
241
- function makeDraggable( domEl, options = { } ) {
242
-
307
+ function makeDraggable( domEl, options = { } )
308
+ {
243
309
  let offsetX = 0;
244
310
  let offsetY = 0;
245
311
  let currentTarget = null;
@@ -329,8 +395,131 @@ function makeDraggable( domEl, options = { } ) {
329
395
 
330
396
  LX.makeDraggable = makeDraggable;
331
397
 
332
- function create_global_searchbar( root ) {
398
+ /**
399
+ * @method makeCodeSnippet
400
+ * @description Create a code snippet in a specific language
401
+ * @param {String} code
402
+ * @param {Array} size
403
+ * @param {Object} options
404
+ * language (String):
405
+ * windowMode (Boolean):
406
+ * lineNumbers (Boolean):
407
+ * tabName (String):
408
+ */
409
+ function makeCodeSnippet( code, size, options = { } )
410
+ {
411
+ if( !LX.has('CodeEditor') )
412
+ {
413
+ console.error( "Import the CodeEditor component to create snippets!" );
414
+ return;
415
+ }
416
+
417
+ const snippet = document.createElement( "div" );
418
+ snippet.className = "lexcodesnippet";
419
+ snippet.style.width = size[ 0 ];
420
+ snippet.style.height = size[ 1 ];
421
+ const area = new Area( { noAppend: true } );
422
+ let editor = new LX.CodeEditor( area, {
423
+ skipInfo: true,
424
+ disableEdition: true,
425
+ allowAddScripts: false,
426
+ name: options.tabName,
427
+ // showTab: options.showTab ?? true,
428
+ // lineNumbers: options.lineNumbers ?? true
429
+ } );
430
+ editor.setText( code, options.language ?? "Plain Text" );
431
+
432
+ if( options.linesAdded )
433
+ {
434
+ const code = editor.root.querySelector( ".code" );
435
+ for( let l of options.linesAdded )
436
+ {
437
+ if( l.constructor == Number )
438
+ {
439
+ code.childNodes[ l - 1 ].classList.add( "added" );
440
+ }
441
+ else if( l.constructor == Array ) // It's a range
442
+ {
443
+ for( let i = ( l[0] - 1 ); i <= ( l[1] - 1 ); i++ )
444
+ {
445
+ code.childNodes[ i ].classList.add( "added" );
446
+ }
447
+ }
448
+ }
449
+ }
450
+
451
+ if( options.linesRemoved )
452
+ {
453
+ const code = editor.root.querySelector( ".code" );
454
+ for( let l of options.linesRemoved )
455
+ {
456
+ if( l.constructor == Number )
457
+ {
458
+ code.childNodes[ l - 1 ].classList.add( "removed" );
459
+ }
460
+ else if( l.constructor == Array ) // It's a range
461
+ {
462
+ for( let i = ( l[0] - 1 ); i <= ( l[1] - 1 ); i++ )
463
+ {
464
+ code.childNodes[ i ].classList.add( "removed" );
465
+ }
466
+ }
467
+ }
468
+ }
469
+
470
+ if( options.windowMode )
471
+ {
472
+ const windowActionButtons = document.createElement( "div" );
473
+ windowActionButtons.className = "lexwindowbuttons";
474
+ const aButton = document.createElement( "span" );
475
+ aButton.style.background = "#ee4f50";
476
+ const bButton = document.createElement( "span" );
477
+ bButton.style.background = "#f5b720";
478
+ const cButton = document.createElement( "span" );
479
+ cButton.style.background = "#53ca29";
480
+ windowActionButtons.appendChild( aButton );
481
+ windowActionButtons.appendChild( bButton );
482
+ windowActionButtons.appendChild( cButton );
483
+ const tabs = editor.root.querySelector( ".lexareatabs" );
484
+ tabs.prepend( windowActionButtons );
485
+ }
486
+
487
+ snippet.appendChild( area.root );
488
+ return snippet;
489
+ }
490
+
491
+ LX.makeCodeSnippet = makeCodeSnippet;
492
+
493
+ // Math classes
494
+
495
+ class vec2 {
496
+
497
+ constructor( x, y ) {
498
+ this.x = x ?? 0;
499
+ this.y = y ?? ( x ?? 0 );
500
+ }
501
+
502
+ get xy() { return [ this.x, this.y ]; }
503
+ get yx() { return [ this.y, this.x ]; }
333
504
 
505
+ set ( x, y ) { this.x = x; this.y = y; }
506
+ add ( v, v0 = new vec2() ) { v0.set( this.x + v.x, this.y + v.y ); return v0; }
507
+ sub ( v, v0 = new vec2() ) { v0.set( this.x - v.x, this.y - v.y ); return v0; }
508
+ mul ( v, v0 = new vec2() ) { if( v.constructor == Number ) { v = new vec2( v ) } v0.set( this.x * v.x, this.y * v.y ); return v0; }
509
+ div ( v, v0 = new vec2() ) { if( v.constructor == Number ) { v = new vec2( v ) } v0.set( this.x / v.x, this.y / v.y ); return v0; }
510
+ abs ( v0 = new vec2() ) { v0.set( Math.abs( this.x ), Math.abs( this.y ) ); return v0; }
511
+ dot ( v ) { return this.x * v.x + this.y * v.y; }
512
+ len2 () { return this.dot( this ) }
513
+ len () { return Math.sqrt( this.len2() ); }
514
+ nrm ( v0 = new vec2() ) { v0.set( this.x, this.y ); return v0.mul( 1.0 / this.len(), v0 ); }
515
+ dst ( v ) { return v.sub( this ).len(); }
516
+ clp ( min, max, v0 = new vec2() ) { v0.set( clamp( this.x, min, max ), clamp( this.y, min, max ) ); return v0; }
517
+ };
518
+
519
+ LX.vec2 = vec2;
520
+
521
+ function create_global_searchbar( root )
522
+ {
334
523
  let globalSearch = document.createElement("div");
335
524
  globalSearch.id = "global-search";
336
525
  globalSearch.className = "hidden";
@@ -2590,6 +2779,17 @@ class Menubar {
2590
2779
  const swapIcon = document.createElement( "a" );
2591
2780
  swapIcon.className = data.swap + " swap-on lexicon";
2592
2781
  button.appendChild( swapIcon );
2782
+
2783
+ button.swap = function() {
2784
+ const swapInput = this.querySelector( "input" );
2785
+ swapInput.checked = !swapInput.checked;
2786
+ };
2787
+
2788
+ // Set if swap has to be performed
2789
+ button.setState = function( v ) {
2790
+ const swapInput = this.querySelector( "input" );
2791
+ swapInput.checked = v;
2792
+ };
2593
2793
  }
2594
2794
 
2595
2795
  trigger.addEventListener("click", e => {
@@ -4813,10 +5013,11 @@ class Panel {
4813
5013
  const topPosition = rect.y;
4814
5014
 
4815
5015
  let maxY = window.innerHeight;
5016
+ let overflowContainer = list.getParentArea();
4816
5017
 
4817
- if( this.mainContainer )
5018
+ if( overflowContainer )
4818
5019
  {
4819
- const parentRect = this.mainContainer.getBoundingClientRect();
5020
+ const parentRect = overflowContainer.getBoundingClientRect();
4820
5021
  maxY = parentRect.y + parentRect.height;
4821
5022
  }
4822
5023
 
@@ -9864,6 +10065,14 @@ Element.prototype.getComputedSize = function() {
9864
10065
  }
9865
10066
  }
9866
10067
 
10068
+ Element.prototype.getParentArea = function() {
10069
+ let parent = this.parentElement;
10070
+ while( parent ) {
10071
+ if( parent.classList.contains( "lexarea" ) ) { return parent; }
10072
+ parent = parent.parentElement;
10073
+ }
10074
+ }
10075
+
9867
10076
  LX.UTILS = {
9868
10077
  getTime() { return new Date().getTime() },
9869
10078
  compareThreshold( v, p, n, t ) { return Math.abs(v - p) >= t || Math.abs(v - n) >= t },
package/changelog.md CHANGED
@@ -1,10 +1,15 @@
1
1
  # lexgui.js changelog
2
2
 
3
- ## 0.1.46 (dev)
3
+ ## 0.1.47 (dev)
4
4
 
5
+ ## 0.1.46 (master)
5
6
 
7
+ Support for creating code snippets `LX.makeCodeSnippet(options)`.
8
+ Added `swapButton.swap()` and Add `swapButton.setState(bool)`.
9
+ Fixed colored buttons in light theme.
10
+ Fixed GoogleSans.ttf usage.
6
11
 
7
- ## 0.1.45 (master)
12
+ ## 0.1.45
8
13
 
9
14
  Widgets:
10
15
  - New Counter Widget added `Panel.addCounter`.
package/demo.js CHANGED
@@ -21,6 +21,36 @@ let area = LX.init();
21
21
  // LX.setThemeColor('global-selected', "#a74");
22
22
  // LX.setThemeColor('global-text', "#f21");
23
23
 
24
+ const snippet = LX.makeCodeSnippet(`
25
+ import { LX } from 'lexgui';
26
+
27
+ class Test {
28
+
29
+ constructor() {
30
+
31
+ this.foo = 1;
32
+
33
+ var div = document.createElement('div');
34
+ div.style.width = "100px"
35
+ div.style.height = "100px"
36
+
37
+ // single line comment
38
+
39
+ document.body.appendChild( div );
40
+
41
+ let a = 1; /* single line block comment */ let b = 2;
42
+
43
+ /*
44
+ multiple line block comment
45
+ */
46
+ }
47
+ }
48
+ `, ["780px", "580px"], { tabName: "script.js", language: "JavaScript", linesAdded: [2, [10, 12]], linesRemoved: [14, 16], windowMode: true });
49
+ snippet.style.left = "200px";
50
+ snippet.style.top = "200px";
51
+ snippet.style.position = "absolute";
52
+ document.body.appendChild( snippet );
53
+
24
54
  // menu bar
25
55
  area.addMenubar( m => {
26
56
 
@@ -117,7 +147,11 @@ area.addMenubar( m => {
117
147
  },
118
148
  {
119
149
  icon: "fa-solid fa-magnifying-glass",
120
- callback: (event) => { console.log("glass!"); }
150
+ callback: (event) => {
151
+ const playButton = m.getButton( "Play" );
152
+ playButton.swap();
153
+ console.log("glass!");
154
+ }
121
155
  },
122
156
  {
123
157
  title: "Change Theme",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lexgui",
3
- "version": "0.1.45",
3
+ "version": "0.1.46",
4
4
  "description": "JS library to create web graphical user interfaces",
5
5
  "type": "module",
6
6
  "main": "./build/lexgui.js",