lexgui 0.1.39 → 0.1.40

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/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # lexgui.js
2
2
 
3
- **lexgui.js** is a lightweight JavaScript library that allows you to create web interfaces using only JavaScript, HTML, and CSS. It provides an easy-to-use API for building dynamic and interactive common and complex editor interfaces without the need for tedious frameworks or big libraries. With lexgui.js, you can create custom UI components, handle user interactions, and update the interface dynamically.
3
+ **lexgui.js** is a lightweight JavaScript library that allows you to create web interfaces using only JavaScript, HTML, and CSS. It provides an easy API for building dynamic and interactive editor interfaces without the need for tedious frameworks or big libraries. With lexgui.js, you can create custom UI components, handle user interactions, and update the interface dynamically.
4
+
5
+ NPM Package: [npmjs.com/package/lexgui](https://www.npmjs.com/package/lexgui)
4
6
 
5
7
  <table>
6
8
  <tr>
@@ -359,16 +359,19 @@ class VideoEditor {
359
359
  this.startTimeString = "0:0";
360
360
  this.endTimeString = "0:0";
361
361
 
362
- let [videoArea, controlsArea] = area.split({ type: 'vertical', sizes: ["80%", null], minimizable: false, resize: false });
362
+ this.mainArea = area;
363
+
364
+ let [videoArea, controlsArea] = area.split({ type: 'vertical', sizes: ["85%", null], minimizable: false, resize: false });
363
365
  controlsArea.root.classList.add('lexconstrolsarea');
364
366
 
365
367
  // Create video element and load it
366
368
  let video = this.video = options.video ?? document.createElement( 'video' );
369
+ this.video.loop = true;
370
+
367
371
  if(options.src) {
368
372
  this.video.src = options.src;
373
+ this._loadVideo(options);
369
374
  }
370
- this.video.loop = true;
371
- this._loadVideo(options);
372
375
  if(options.videoArea) {
373
376
  options.videoArea.root.classList.add("lexvideoeditor");
374
377
  videoArea.attach(options.videoArea);
@@ -458,6 +461,24 @@ class VideoEditor {
458
461
  this.timebar.resize([availableWidth, timeBarArea.root.clientHeight]);
459
462
  })
460
463
 
464
+ this.onKeyUp = (event) => {
465
+ if(this.controls && event.key == " ") {
466
+ event.preventDefault();
467
+ event.stopPropagation();
468
+
469
+ if(!this.playing) {
470
+ this.video.play();
471
+ }
472
+ else {
473
+ this.video.pause();
474
+ }
475
+ this.playing = !this.playing;
476
+ this.controlsPanelLeft.refresh();
477
+ }
478
+ }
479
+
480
+ window.addEventListener( "keyup", this.onKeyUp);
481
+
461
482
  videoArea.onresize = (v) => {
462
483
  bottomArea.setSize([v.width, 40]);
463
484
  }
@@ -483,7 +504,7 @@ class VideoEditor {
483
504
  this.timebar.onMouseMove(event);
484
505
  }
485
506
  });
486
-
507
+
487
508
  }
488
509
 
489
510
  async _loadVideo( options = {} ) {
@@ -491,13 +512,17 @@ class VideoEditor {
491
512
  await new Promise(r => setTimeout(r, 1000));
492
513
  this.video.currentTime = 10000000 * Math.random();
493
514
  }
494
- this.video.currentTime = -1; // BUG: some videos will not play unless this line is present
495
- this.video.currentTime = 0;
515
+
516
+ this.timebar.startX = this.timebar.position.x;
517
+ this.timebar.endX = this.timebar.width;
518
+
519
+ this.video.currentTime = 0.01; // BUG: some videos will not play unless this line is present
496
520
  this.endTime = this.video.duration;
497
- this.timebar.currentX = this._timeToX(0);
521
+
498
522
  this._setEndValue(this.timebar.endX);
499
523
  this._setStartValue(this.timebar.startX);
500
- this._setCurrentValue(this.timebar.currentX);
524
+ this.timebar.currentX = this._timeToX(this.video.currentTime);
525
+ this._setCurrentValue(this.timebar.currentX, false);
501
526
  this.timebar.update(this.timebar.currentX);
502
527
 
503
528
  if ( !this.requestId ){ // only have one update on flight
@@ -508,6 +533,8 @@ class VideoEditor {
508
533
  this.hideControls();
509
534
  }
510
535
 
536
+ window.addEventListener( "keyup", this.onKeyUp);
537
+
511
538
  if(this.onVideoLoaded) {
512
539
  this.onVideoLoaded(this.video);
513
540
  }
@@ -627,9 +654,15 @@ class VideoEditor {
627
654
  }
628
655
  }
629
656
 
630
- delete ( ) {
657
+ unbind ( ) {
631
658
  this.stopUpdates();
632
- delete this;
659
+
660
+ this.video.pause();
661
+ this.playing = false;
662
+ this.controlsPanelLeft.refresh();
663
+ this.video.src = "";
664
+
665
+ window.removeEventListener("keyup", this.onKeyUp);
633
666
  }
634
667
  }
635
668
 
package/build/lexgui.js CHANGED
@@ -12,7 +12,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
12
12
  */
13
13
 
14
14
  var LX = global.LX = {
15
- version: "0.1.39",
15
+ version: "0.1.40",
16
16
  ready: false,
17
17
  components: [], // specific pre-build components
18
18
  signals: {} // events and triggers
@@ -1200,7 +1200,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
1200
1200
  // Send area resize to every widget in the area
1201
1201
  for( let widget of widgets )
1202
1202
  {
1203
- const jsInstance = widget.jsIinstance;
1203
+ const jsInstance = widget.jsInstance;
1204
1204
 
1205
1205
  if( jsInstance.onresize )
1206
1206
  {
@@ -2541,6 +2541,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2541
2541
  static KNOB = 23;
2542
2542
  static SIZE = 24;
2543
2543
  static PAD = 25;
2544
+ static FORM = 26;
2544
2545
 
2545
2546
  static NO_CONTEXT_TYPES = [
2546
2547
  Widget.BUTTON,
@@ -2627,6 +2628,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
2627
2628
  case Widget.KNOB: return "Knob";
2628
2629
  case Widget.SIZE: return "Size";
2629
2630
  case Widget.PAD: return "Pad";
2631
+ case Widget.FORM: return "Form";
2630
2632
  case Widget.CUSTOM: return this.customName;
2631
2633
  }
2632
2634
  }
@@ -3528,7 +3530,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3528
3530
 
3529
3531
  if( name != undefined )
3530
3532
  {
3531
- if( !(options.no_name ?? false) )
3533
+ if( !(options.hideName ?? false) )
3532
3534
  {
3533
3535
  let domName = document.createElement( 'div' );
3534
3536
  domName.className = "lexwidgetname";
@@ -3568,6 +3570,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3568
3570
  }
3569
3571
 
3570
3572
  widget.domEl = element;
3573
+ element.jsInstance = widget;
3571
3574
 
3572
3575
  const insert_widget = el => {
3573
3576
  if(options.container)
@@ -3637,7 +3640,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
3637
3640
  element.className += " lexfilter noname";
3638
3641
 
3639
3642
  let input = document.createElement('input');
3640
- input.id = 'input-filter';
3643
+ input.className = 'lexinput-filter';
3641
3644
  input.setAttribute("placeholder", options.placeholder);
3642
3645
  input.style.width = "calc( 100% - 17px )";
3643
3646
  input.value = options.filterValue || "";
@@ -4222,7 +4225,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4222
4225
 
4223
4226
  addCard( name, options = {} ) {
4224
4227
 
4225
- options.no_name = true;
4228
+ options.hideName = true;
4226
4229
  let widget = this.create_widget(name, Widget.CARD, options);
4227
4230
  let element = widget.domEl;
4228
4231
 
@@ -4273,6 +4276,89 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4273
4276
  return widget;
4274
4277
  }
4275
4278
 
4279
+ /**
4280
+ * @method addForm
4281
+ * @param {String} name Widget name
4282
+ * @param {Object} data Form data
4283
+ * @param {Function} callback Callback function on submit form
4284
+ * @param {*} options:
4285
+ * actionName: Text to be shown in the button
4286
+ */
4287
+
4288
+ addForm( name, data, callback, options = {} ) {
4289
+
4290
+ if( data.constructor != Object )
4291
+ {
4292
+ console.error( "Form data must be an Object" );
4293
+ return;
4294
+ }
4295
+
4296
+ // Always hide name for this one
4297
+ options.hideName = true;
4298
+
4299
+ let widget = this.create_widget( name, Widget.FORM, options );
4300
+
4301
+ widget.onGetValue = () => {
4302
+ return container.formData;
4303
+ };
4304
+
4305
+ widget.onSetValue = ( newValue, skipCallback ) => {
4306
+ container.formData = newValue;
4307
+ const entries = container.querySelectorAll( ".lexwidget" );
4308
+ for( let i = 0; i < entries.length; ++i )
4309
+ {
4310
+ const entry = entries[ i ];
4311
+ if( entry.jsInstance.type != LX.Widget.TEXT )
4312
+ {
4313
+ continue;
4314
+ }
4315
+ let entryName = entries[ i ].querySelector( ".lexwidgetname" ).innerText;
4316
+ let entryInput = entries[ i ].querySelector( ".lextext input" );
4317
+ entryInput.value = newValue[ entryName ] ?? "";
4318
+ Panel._dispatch_event( entryInput, "focusout", skipCallback );
4319
+ }
4320
+ };
4321
+
4322
+ // Add widget value
4323
+
4324
+ let element = widget.domEl;
4325
+
4326
+ let container = document.createElement( 'div' );
4327
+ container.className = "lexformdata";
4328
+
4329
+ this.queue( container );
4330
+
4331
+ container.formData = {};
4332
+
4333
+ for( let entry in data )
4334
+ {
4335
+ const entryData = data[ entry ];
4336
+ this.addText( entry, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
4337
+ container.formData[ entry ] = value;
4338
+ }, entryData );
4339
+
4340
+ container.formData[ entry ] = entryData.constructor == Object ? entryData.value : entryData;
4341
+ }
4342
+
4343
+ this.addButton( null, options.actionName ?? "Submit", ( value, event ) => {
4344
+ if( callback )
4345
+ {
4346
+ callback( container.formData, event );
4347
+ }
4348
+ } );
4349
+
4350
+ this.clearQueue();
4351
+
4352
+ element.appendChild( container );
4353
+
4354
+ if( !widget.name || options.hideName ) {
4355
+ element.className += " noname";
4356
+ container.style.width = "100%";
4357
+ }
4358
+
4359
+ return widget;
4360
+ }
4361
+
4276
4362
  /**
4277
4363
  * @method addContent
4278
4364
  * @param {HTMLElement} element
@@ -4297,27 +4383,29 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4297
4383
  async addImage( url, options = {} ) {
4298
4384
 
4299
4385
  if( !url )
4300
- return;
4386
+ {
4387
+ return;
4388
+ }
4301
4389
 
4302
- options.no_name = true;
4303
- let widget = this.create_widget(null, Widget.IMAGE, options);
4390
+ options.hideName = true;
4391
+ let widget = this.create_widget( null, Widget.IMAGE, options );
4304
4392
  let element = widget.domEl;
4305
4393
 
4306
- let container = document.createElement('div');
4394
+ let container = document.createElement( 'div' );
4307
4395
  container.className = "leximage";
4308
4396
  container.style.width = "100%";
4309
4397
 
4310
- let img = document.createElement('img');
4398
+ let img = document.createElement( 'img' );
4311
4399
  img.src = url;
4312
4400
 
4313
- for(let s in options.style) {
4314
-
4315
- img.style[s] = options.style[s];
4401
+ for( let s in options.style )
4402
+ {
4403
+ img.style[ s ] = options.style[ s ];
4316
4404
  }
4317
4405
 
4318
4406
  await img.decode();
4319
- container.appendChild(img);
4320
- element.appendChild(container);
4407
+ container.appendChild( img );
4408
+ element.appendChild( container );
4321
4409
 
4322
4410
  return widget;
4323
4411
  }
@@ -4416,7 +4504,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
4416
4504
  setTimeout( () => delete this.unfocus_event, 200 );
4417
4505
  } else if ( e.relatedTarget && e.relatedTarget.tagName == "INPUT" ) {
4418
4506
  return;
4419
- }else if ( e.target.id == 'input-filter' ) {
4507
+ }else if ( e.target.id == 'lexinput-filter' ) {
4420
4508
  return;
4421
4509
  }
4422
4510
  this.toggleAttribute( 'hidden', true );
@@ -5391,7 +5479,6 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5391
5479
  vecinput.addEventListener( "mousedown", inner_mousedown );
5392
5480
 
5393
5481
  var that = this;
5394
- var lastY = 0;
5395
5482
 
5396
5483
  function inner_mousedown( e )
5397
5484
  {
@@ -5403,13 +5490,16 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5403
5490
  var doc = that.root.ownerDocument;
5404
5491
  doc.addEventListener( 'mousemove', inner_mousemove );
5405
5492
  doc.addEventListener( 'mouseup', inner_mouseup );
5406
- lastY = e.pageY;
5407
- document.body.classList.add( 'nocursor' );
5408
5493
  document.body.classList.add( 'noevents' );
5409
5494
  dragIcon.classList.remove( 'hidden' );
5410
5495
  e.stopImmediatePropagation();
5411
5496
  e.stopPropagation();
5412
5497
 
5498
+ if( !document.pointerLockElement )
5499
+ {
5500
+ vecinput.requestPointerLock();
5501
+ }
5502
+
5413
5503
  if( options.onPress )
5414
5504
  {
5415
5505
  options.onPress.bind( vecinput )( e, vecinput );
@@ -5418,8 +5508,10 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5418
5508
 
5419
5509
  function inner_mousemove( e )
5420
5510
  {
5421
- if ( lastY != e.pageY ) {
5422
- let dt = lastY - e.pageY;
5511
+ let dt = -e.movementY;
5512
+
5513
+ if ( dt != 0 )
5514
+ {
5423
5515
  let mult = options.step ?? 1;
5424
5516
  if( e.shiftKey ) mult *= 10;
5425
5517
  else if( e.altKey ) mult *= 0.1;
@@ -5428,7 +5520,6 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5428
5520
  Panel._dispatch_event( vecinput, "change" );
5429
5521
  }
5430
5522
 
5431
- lastY = e.pageY;
5432
5523
  e.stopPropagation();
5433
5524
  e.preventDefault();
5434
5525
  }
@@ -5438,10 +5529,14 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5438
5529
  var doc = that.root.ownerDocument;
5439
5530
  doc.removeEventListener( 'mousemove', inner_mousemove );
5440
5531
  doc.removeEventListener( 'mouseup', inner_mouseup );
5441
- document.body.classList.remove( 'nocursor' );
5442
5532
  document.body.classList.remove( 'noevents' );
5443
5533
  dragIcon.classList.add( 'hidden' );
5444
5534
 
5535
+ if( document.pointerLockElement )
5536
+ {
5537
+ document.exitPointerLock();
5538
+ }
5539
+
5445
5540
  if( options.onRelease )
5446
5541
  {
5447
5542
  options.onRelease.bind( vecinput )( e, vecinput );
@@ -5534,7 +5629,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5534
5629
 
5535
5630
  if( value[ i ].constructor == Number )
5536
5631
  {
5537
- value[ i ] = clamp(value[ i ], +vecinput.min, +vecinput.max);
5632
+ value[ i ] = clamp( value[ i ], +vecinput.min, +vecinput.max );
5538
5633
  value[ i ] = round( value[ i ], options.precision );
5539
5634
  }
5540
5635
 
@@ -5576,7 +5671,9 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5576
5671
  vecinput.addEventListener( "change", e => {
5577
5672
 
5578
5673
  if( isNaN( e.target.value ) )
5674
+ {
5579
5675
  return;
5676
+ }
5580
5677
 
5581
5678
  const skipCallback = e.detail;
5582
5679
 
@@ -5587,7 +5684,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5587
5684
  if( !skipCallback )
5588
5685
  {
5589
5686
  let btn = element.querySelector( ".lexwidgetname .lexicon" );
5590
- if( btn ) btn.style.display = val != vecinput.iValue ? "block": "none";
5687
+ if( btn ) btn.style.display = val != vecinput.iValue ? "block" : "none";
5591
5688
  }
5592
5689
 
5593
5690
  if( locker.locked )
@@ -5596,7 +5693,8 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5596
5693
  v.value = val;
5597
5694
  value[ v.idx ] = val;
5598
5695
  }
5599
- } else
5696
+ }
5697
+ else
5600
5698
  {
5601
5699
  vecinput.value = val;
5602
5700
  value[ e.target.idx ] = val;
@@ -5610,7 +5708,7 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5610
5708
  vecinput.addEventListener( "mousedown", inner_mousedown );
5611
5709
 
5612
5710
  var that = this;
5613
- var lastY = 0;
5711
+
5614
5712
  function inner_mousedown( e )
5615
5713
  {
5616
5714
  if( document.activeElement == vecinput )
@@ -5621,13 +5719,17 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5621
5719
  var doc = that.root.ownerDocument;
5622
5720
  doc.addEventListener( 'mousemove', inner_mousemove );
5623
5721
  doc.addEventListener( 'mouseup', inner_mouseup );
5624
- lastY = e.pageY;
5625
5722
  document.body.classList.add( 'nocursor' );
5626
5723
  document.body.classList.add( 'noevents' );
5627
5724
  dragIcon.classList.remove( 'hidden' );
5628
5725
  e.stopImmediatePropagation();
5629
5726
  e.stopPropagation();
5630
5727
 
5728
+ if( !document.pointerLockElement )
5729
+ {
5730
+ vecinput.requestPointerLock();
5731
+ }
5732
+
5631
5733
  if( options.onPress )
5632
5734
  {
5633
5735
  options.onPress.bind( vecinput )( e, vecinput );
@@ -5636,8 +5738,10 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5636
5738
 
5637
5739
  function inner_mousemove( e )
5638
5740
  {
5639
- if ( lastY != e.pageY ) {
5640
- let dt = lastY - e.pageY;
5741
+ let dt = -e.movementY;
5742
+
5743
+ if ( dt != 0 )
5744
+ {
5641
5745
  let mult = options.step ?? 1;
5642
5746
  if( e.shiftKey ) mult = 10;
5643
5747
  else if( e.altKey ) mult = 0.1;
@@ -5656,7 +5760,6 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5656
5760
  }
5657
5761
  }
5658
5762
 
5659
- lastY = e.pageY;
5660
5763
  e.stopPropagation();
5661
5764
  e.preventDefault();
5662
5765
  }
@@ -5666,10 +5769,14 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5666
5769
  var doc = that.root.ownerDocument;
5667
5770
  doc.removeEventListener( 'mousemove', inner_mousemove );
5668
5771
  doc.removeEventListener( 'mouseup', inner_mouseup );
5669
- document.body.classList.remove( 'nocursor' );
5670
5772
  document.body.classList.remove( 'noevents' );
5671
5773
  dragIcon.classList.add('hidden');
5672
5774
 
5775
+ if( document.pointerLockElement )
5776
+ {
5777
+ document.exitPointerLock();
5778
+ }
5779
+
5673
5780
  if( options.onRelease )
5674
5781
  {
5675
5782
  options.onRelease.bind( vecinput )( e, vecinput );
@@ -5707,7 +5814,8 @@ console.warn( 'Script "build/lexgui.js" is depracated and will be removed soon.
5707
5814
  {
5708
5815
  this.classList.add( "fa-lock" );
5709
5816
  this.classList.remove( "fa-lock-open" );
5710
- } else {
5817
+ }
5818
+ else {
5711
5819
  this.classList.add( "fa-lock-open" );
5712
5820
  this.classList.remove( "fa-lock" );
5713
5821
  }
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  var LX = {
11
- version: "0.1.39",
11
+ version: "0.1.40",
12
12
  ready: false,
13
13
  components: [], // specific pre-build components
14
14
  signals: {} // events and triggers
@@ -1198,7 +1198,7 @@ class Area {
1198
1198
  // Send area resize to every widget in the area
1199
1199
  for( let widget of widgets )
1200
1200
  {
1201
- const jsInstance = widget.jsIinstance;
1201
+ const jsInstance = widget.jsInstance;
1202
1202
 
1203
1203
  if( jsInstance.onresize )
1204
1204
  {
@@ -2538,6 +2538,7 @@ class Widget {
2538
2538
  static KNOB = 23;
2539
2539
  static SIZE = 24;
2540
2540
  static PAD = 25;
2541
+ static FORM = 26;
2541
2542
 
2542
2543
  static NO_CONTEXT_TYPES = [
2543
2544
  Widget.BUTTON,
@@ -2624,6 +2625,7 @@ class Widget {
2624
2625
  case Widget.KNOB: return "Knob";
2625
2626
  case Widget.SIZE: return "Size";
2626
2627
  case Widget.PAD: return "Pad";
2628
+ case Widget.FORM: return "Form";
2627
2629
  case Widget.CUSTOM: return this.customName;
2628
2630
  }
2629
2631
  }
@@ -3526,7 +3528,7 @@ class Panel {
3526
3528
 
3527
3529
  if( name != undefined )
3528
3530
  {
3529
- if( !(options.no_name ?? false) )
3531
+ if( !(options.hideName ?? false) )
3530
3532
  {
3531
3533
  let domName = document.createElement( 'div' );
3532
3534
  domName.className = "lexwidgetname";
@@ -3566,7 +3568,7 @@ class Panel {
3566
3568
  }
3567
3569
 
3568
3570
  widget.domEl = element;
3569
- element.jsIinstance = widget;
3571
+ element.jsInstance = widget;
3570
3572
 
3571
3573
  const insert_widget = el => {
3572
3574
  if(options.container)
@@ -3636,7 +3638,7 @@ class Panel {
3636
3638
  element.className += " lexfilter noname";
3637
3639
 
3638
3640
  let input = document.createElement('input');
3639
- input.id = 'input-filter';
3641
+ input.className = 'lexinput-filter';
3640
3642
  input.setAttribute("placeholder", options.placeholder);
3641
3643
  input.style.width = "calc( 100% - 17px )";
3642
3644
  input.value = options.filterValue || "";
@@ -4224,7 +4226,7 @@ class Panel {
4224
4226
 
4225
4227
  addCard( name, options = {} ) {
4226
4228
 
4227
- options.no_name = true;
4229
+ options.hideName = true;
4228
4230
  let widget = this.create_widget(name, Widget.CARD, options);
4229
4231
  let element = widget.domEl;
4230
4232
 
@@ -4275,6 +4277,89 @@ class Panel {
4275
4277
  return widget;
4276
4278
  }
4277
4279
 
4280
+ /**
4281
+ * @method addForm
4282
+ * @param {String} name Widget name
4283
+ * @param {Object} data Form data
4284
+ * @param {Function} callback Callback function on submit form
4285
+ * @param {*} options:
4286
+ * actionName: Text to be shown in the button
4287
+ */
4288
+
4289
+ addForm( name, data, callback, options = {} ) {
4290
+
4291
+ if( data.constructor != Object )
4292
+ {
4293
+ console.error( "Form data must be an Object" );
4294
+ return;
4295
+ }
4296
+
4297
+ // Always hide name for this one
4298
+ options.hideName = true;
4299
+
4300
+ let widget = this.create_widget( name, Widget.FORM, options );
4301
+
4302
+ widget.onGetValue = () => {
4303
+ return container.formData;
4304
+ };
4305
+
4306
+ widget.onSetValue = ( newValue, skipCallback ) => {
4307
+ container.formData = newValue;
4308
+ const entries = container.querySelectorAll( ".lexwidget" );
4309
+ for( let i = 0; i < entries.length; ++i )
4310
+ {
4311
+ const entry = entries[ i ];
4312
+ if( entry.jsInstance.type != LX.Widget.TEXT )
4313
+ {
4314
+ continue;
4315
+ }
4316
+ let entryName = entries[ i ].querySelector( ".lexwidgetname" ).innerText;
4317
+ let entryInput = entries[ i ].querySelector( ".lextext input" );
4318
+ entryInput.value = newValue[ entryName ] ?? "";
4319
+ Panel._dispatch_event( entryInput, "focusout", skipCallback );
4320
+ }
4321
+ };
4322
+
4323
+ // Add widget value
4324
+
4325
+ let element = widget.domEl;
4326
+
4327
+ let container = document.createElement( 'div' );
4328
+ container.className = "lexformdata";
4329
+
4330
+ this.queue( container );
4331
+
4332
+ container.formData = {};
4333
+
4334
+ for( let entry in data )
4335
+ {
4336
+ const entryData = data[ entry ];
4337
+ this.addText( entry, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
4338
+ container.formData[ entry ] = value;
4339
+ }, entryData );
4340
+
4341
+ container.formData[ entry ] = entryData.constructor == Object ? entryData.value : entryData;
4342
+ }
4343
+
4344
+ this.addButton( null, options.actionName ?? "Submit", ( value, event ) => {
4345
+ if( callback )
4346
+ {
4347
+ callback( container.formData, event );
4348
+ }
4349
+ } );
4350
+
4351
+ this.clearQueue();
4352
+
4353
+ element.appendChild( container );
4354
+
4355
+ if( !widget.name || options.hideName ) {
4356
+ element.className += " noname";
4357
+ container.style.width = "100%";
4358
+ }
4359
+
4360
+ return widget;
4361
+ }
4362
+
4278
4363
  /**
4279
4364
  * @method addContent
4280
4365
  * @param {HTMLElement} element
@@ -4299,27 +4384,29 @@ class Panel {
4299
4384
  async addImage( url, options = {} ) {
4300
4385
 
4301
4386
  if( !url )
4302
- return;
4387
+ {
4388
+ return;
4389
+ }
4303
4390
 
4304
- options.no_name = true;
4305
- let widget = this.create_widget(null, Widget.IMAGE, options);
4391
+ options.hideName = true;
4392
+ let widget = this.create_widget( null, Widget.IMAGE, options );
4306
4393
  let element = widget.domEl;
4307
4394
 
4308
- let container = document.createElement('div');
4395
+ let container = document.createElement( 'div' );
4309
4396
  container.className = "leximage";
4310
4397
  container.style.width = "100%";
4311
4398
 
4312
- let img = document.createElement('img');
4399
+ let img = document.createElement( 'img' );
4313
4400
  img.src = url;
4314
4401
 
4315
- for(let s in options.style) {
4316
-
4317
- img.style[s] = options.style[s];
4402
+ for( let s in options.style )
4403
+ {
4404
+ img.style[ s ] = options.style[ s ];
4318
4405
  }
4319
4406
 
4320
4407
  await img.decode();
4321
- container.appendChild(img);
4322
- element.appendChild(container);
4408
+ container.appendChild( img );
4409
+ element.appendChild( container );
4323
4410
 
4324
4411
  return widget;
4325
4412
  }
@@ -4418,7 +4505,7 @@ class Panel {
4418
4505
  setTimeout( () => delete this.unfocus_event, 200 );
4419
4506
  } else if ( e.relatedTarget && e.relatedTarget.tagName == "INPUT" ) {
4420
4507
  return;
4421
- }else if ( e.target.id == 'input-filter' ) {
4508
+ }else if ( e.target.className == 'lexinput-filter' ) {
4422
4509
  return;
4423
4510
  }
4424
4511
  this.toggleAttribute( 'hidden', true );
@@ -5393,7 +5480,6 @@ class Panel {
5393
5480
  vecinput.addEventListener( "mousedown", inner_mousedown );
5394
5481
 
5395
5482
  var that = this;
5396
- var lastY = 0;
5397
5483
 
5398
5484
  function inner_mousedown( e )
5399
5485
  {
@@ -5405,13 +5491,16 @@ class Panel {
5405
5491
  var doc = that.root.ownerDocument;
5406
5492
  doc.addEventListener( 'mousemove', inner_mousemove );
5407
5493
  doc.addEventListener( 'mouseup', inner_mouseup );
5408
- lastY = e.pageY;
5409
- document.body.classList.add( 'nocursor' );
5410
5494
  document.body.classList.add( 'noevents' );
5411
5495
  dragIcon.classList.remove( 'hidden' );
5412
5496
  e.stopImmediatePropagation();
5413
5497
  e.stopPropagation();
5414
5498
 
5499
+ if( !document.pointerLockElement )
5500
+ {
5501
+ vecinput.requestPointerLock();
5502
+ }
5503
+
5415
5504
  if( options.onPress )
5416
5505
  {
5417
5506
  options.onPress.bind( vecinput )( e, vecinput );
@@ -5420,8 +5509,10 @@ class Panel {
5420
5509
 
5421
5510
  function inner_mousemove( e )
5422
5511
  {
5423
- if ( lastY != e.pageY ) {
5424
- let dt = lastY - e.pageY;
5512
+ let dt = -e.movementY;
5513
+
5514
+ if ( dt != 0 )
5515
+ {
5425
5516
  let mult = options.step ?? 1;
5426
5517
  if( e.shiftKey ) mult *= 10;
5427
5518
  else if( e.altKey ) mult *= 0.1;
@@ -5430,7 +5521,6 @@ class Panel {
5430
5521
  Panel._dispatch_event( vecinput, "change" );
5431
5522
  }
5432
5523
 
5433
- lastY = e.pageY;
5434
5524
  e.stopPropagation();
5435
5525
  e.preventDefault();
5436
5526
  }
@@ -5440,10 +5530,14 @@ class Panel {
5440
5530
  var doc = that.root.ownerDocument;
5441
5531
  doc.removeEventListener( 'mousemove', inner_mousemove );
5442
5532
  doc.removeEventListener( 'mouseup', inner_mouseup );
5443
- document.body.classList.remove( 'nocursor' );
5444
5533
  document.body.classList.remove( 'noevents' );
5445
5534
  dragIcon.classList.add( 'hidden' );
5446
5535
 
5536
+ if( document.pointerLockElement )
5537
+ {
5538
+ document.exitPointerLock();
5539
+ }
5540
+
5447
5541
  if( options.onRelease )
5448
5542
  {
5449
5543
  options.onRelease.bind( vecinput )( e, vecinput );
@@ -5536,7 +5630,7 @@ class Panel {
5536
5630
 
5537
5631
  if( value[ i ].constructor == Number )
5538
5632
  {
5539
- value[ i ] = clamp(value[ i ], +vecinput.min, +vecinput.max);
5633
+ value[ i ] = clamp( value[ i ], +vecinput.min, +vecinput.max );
5540
5634
  value[ i ] = round( value[ i ], options.precision );
5541
5635
  }
5542
5636
 
@@ -5578,7 +5672,9 @@ class Panel {
5578
5672
  vecinput.addEventListener( "change", e => {
5579
5673
 
5580
5674
  if( isNaN( e.target.value ) )
5675
+ {
5581
5676
  return;
5677
+ }
5582
5678
 
5583
5679
  const skipCallback = e.detail;
5584
5680
 
@@ -5589,7 +5685,7 @@ class Panel {
5589
5685
  if( !skipCallback )
5590
5686
  {
5591
5687
  let btn = element.querySelector( ".lexwidgetname .lexicon" );
5592
- if( btn ) btn.style.display = val != vecinput.iValue ? "block": "none";
5688
+ if( btn ) btn.style.display = val != vecinput.iValue ? "block" : "none";
5593
5689
  }
5594
5690
 
5595
5691
  if( locker.locked )
@@ -5598,7 +5694,8 @@ class Panel {
5598
5694
  v.value = val;
5599
5695
  value[ v.idx ] = val;
5600
5696
  }
5601
- } else
5697
+ }
5698
+ else
5602
5699
  {
5603
5700
  vecinput.value = val;
5604
5701
  value[ e.target.idx ] = val;
@@ -5612,7 +5709,7 @@ class Panel {
5612
5709
  vecinput.addEventListener( "mousedown", inner_mousedown );
5613
5710
 
5614
5711
  var that = this;
5615
- var lastY = 0;
5712
+
5616
5713
  function inner_mousedown( e )
5617
5714
  {
5618
5715
  if( document.activeElement == vecinput )
@@ -5623,13 +5720,16 @@ class Panel {
5623
5720
  var doc = that.root.ownerDocument;
5624
5721
  doc.addEventListener( 'mousemove', inner_mousemove );
5625
5722
  doc.addEventListener( 'mouseup', inner_mouseup );
5626
- lastY = e.pageY;
5627
- document.body.classList.add( 'nocursor' );
5628
5723
  document.body.classList.add( 'noevents' );
5629
5724
  dragIcon.classList.remove( 'hidden' );
5630
5725
  e.stopImmediatePropagation();
5631
5726
  e.stopPropagation();
5632
5727
 
5728
+ if( !document.pointerLockElement )
5729
+ {
5730
+ vecinput.requestPointerLock();
5731
+ }
5732
+
5633
5733
  if( options.onPress )
5634
5734
  {
5635
5735
  options.onPress.bind( vecinput )( e, vecinput );
@@ -5638,8 +5738,10 @@ class Panel {
5638
5738
 
5639
5739
  function inner_mousemove( e )
5640
5740
  {
5641
- if ( lastY != e.pageY ) {
5642
- let dt = lastY - e.pageY;
5741
+ let dt = -e.movementY;
5742
+
5743
+ if ( dt != 0 )
5744
+ {
5643
5745
  let mult = options.step ?? 1;
5644
5746
  if( e.shiftKey ) mult = 10;
5645
5747
  else if( e.altKey ) mult = 0.1;
@@ -5658,7 +5760,6 @@ class Panel {
5658
5760
  }
5659
5761
  }
5660
5762
 
5661
- lastY = e.pageY;
5662
5763
  e.stopPropagation();
5663
5764
  e.preventDefault();
5664
5765
  }
@@ -5668,10 +5769,14 @@ class Panel {
5668
5769
  var doc = that.root.ownerDocument;
5669
5770
  doc.removeEventListener( 'mousemove', inner_mousemove );
5670
5771
  doc.removeEventListener( 'mouseup', inner_mouseup );
5671
- document.body.classList.remove( 'nocursor' );
5672
5772
  document.body.classList.remove( 'noevents' );
5673
5773
  dragIcon.classList.add('hidden');
5674
5774
 
5775
+ if( document.pointerLockElement )
5776
+ {
5777
+ document.exitPointerLock();
5778
+ }
5779
+
5675
5780
  if( options.onRelease )
5676
5781
  {
5677
5782
  options.onRelease.bind( vecinput )( e, vecinput );
@@ -5709,7 +5814,9 @@ class Panel {
5709
5814
  {
5710
5815
  this.classList.add( "fa-lock" );
5711
5816
  this.classList.remove( "fa-lock-open" );
5712
- } else {
5817
+ }
5818
+ else
5819
+ {
5713
5820
  this.classList.add( "fa-lock-open" );
5714
5821
  this.classList.remove( "fa-lock" );
5715
5822
  }
package/changelog.md CHANGED
@@ -1,8 +1,17 @@
1
1
  # lexgui.js changelog
2
2
 
3
- ## 0.1.40 (dev)
3
+ ## 0.1.41 (dev)
4
4
 
5
- ## 0.1.39 (master)
5
+
6
+
7
+ ## 0.1.40 (master)
8
+
9
+ New widget: Form. Series of Text + Submit Button using form data (Object).
10
+ Fix non-unique "input-filter" id.
11
+ Improved Number/Vector mouse interaction with `pointerLock`.
12
+ Updated docs.
13
+
14
+ ## 0.1.39
6
15
 
7
16
  New widget: Pad. Bidimensional slider.
8
17
  `LX.makeDraggable` now supports 'absolute' and 'fixed' positions.
package/demo.js CHANGED
@@ -481,12 +481,6 @@ function fillPanel( panel ) {
481
481
  panel.sameLine(2);
482
482
  panel.addFile("Img1", data => { console.log(data) }, {} );
483
483
  panel.addFile("Img2", data => { console.log(data) }, {} );
484
- panel.addPad("2D Pad", [0.5, 0.5], (value, event) => {
485
- console.log(value);
486
- }, { padSize: "100px", min: -1, max: 2 });
487
- panel.addSize("Screen Res", [1280, 720], (value, event) => {
488
- console.log(value);
489
- }, { units: "p" });
490
484
  panel.addDropdown("Best Engine", ["Godot", "Unity", "Unreal Engine"], "Unity", (value, event) => {
491
485
  console.log(value);
492
486
  });
@@ -543,6 +537,17 @@ function fillPanel( panel ) {
543
537
  panel.addCurve("Opacity", opacityValues, (value, event) => {
544
538
  console.log(value);
545
539
  });
540
+ panel.addPad("2D Pad", [0.5, 0.5], (value, event) => {
541
+ console.log(value);
542
+ }, { padSize: "100px", min: -1, max: 2 });
543
+ panel.addSize("Screen Res", [1280, 720], (value, event) => {
544
+ console.log(value);
545
+ }, { units: "p" });
546
+
547
+ const formData = { username: "", password: { value: "", type: "password" } };
548
+ panel.addForm("Test form", formData, (value, event) => {
549
+ console.log(value);
550
+ }, { actionName: "Login" });
546
551
 
547
552
  // another branch
548
553
  panel.branch("Canvas", {icon: "fa-solid fa-palette", filter: true});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lexgui",
3
- "version": "0.1.39",
3
+ "version": "0.1.40",
4
4
  "description": "JS library to create web graphical user interfaces",
5
5
  "type": "module",
6
6
  "main": "./build/lexgui.js",