perfect-gui 4.12.0 → 4.12.4

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/src/index.js CHANGED
@@ -1,5 +1,11 @@
1
- import Slider from './components/Slider';
2
- import styles from './styles/styles';
1
+ import Button from './components/Button.js';
2
+ import Slider from './components/Slider.js';
3
+ import Image from './components/Image.js';
4
+ import Toggle from './components/Toggle.js';
5
+ import List from './components/List.js';
6
+ import Color from './components/Color.js';
7
+ import Vector2 from './components/Vector2.js';
8
+ import styles from './styles/styles.js';
3
9
 
4
10
  export default class GUI {
5
11
  constructor(options = {}) {
@@ -206,655 +212,51 @@ export default class GUI {
206
212
  this.header.append(close_btn);
207
213
  }
208
214
 
209
- button(options, callback) {
210
- let label = '';
211
- if (typeof options != 'string') {
212
- if (typeof options == 'object' && options?.hasOwnProperty('label')) {
213
- label = options.label == '' ? ' ' : options.label;
214
- } else {
215
- label = ' ';
216
- }
217
- } else {
218
- label = options == '' ? ' ' : options;
219
- }
220
-
221
- const tooltip = (typeof options.tooltip === 'string') ? options.tooltip : (options.tooltip === true ? label : null);
222
-
215
+ button(params = {}, callback) {
223
216
  this.imageContainer = null;
224
-
225
- const el = document.createElement('div');
226
- el.className = 'p-gui__button';
227
- el.textContent = label;
228
- if ( tooltip ) {
229
- el.setAttribute('title', tooltip);
230
- }
231
- el.addEventListener('click', () => {
232
- if (callback) {
233
- callback();
234
- }
235
-
236
- if (this.onUpdate) {
237
- this.onUpdate();
238
- } else if (this.isFolder && this.firstParent.onUpdate) {
239
- this.firstParent.onUpdate();
240
- }
241
- });
242
- this.wrapper.append(el);
243
-
244
- if (typeof options.color == 'string') {
245
- el.style.setProperty('--color-accent', options.color);
246
- el.style.setProperty('--color-accent-hover', options.hoverColor || options.color);
247
- }
217
+ const el = new Button(this, params, callback);
218
+ return el;
248
219
  }
249
220
 
250
221
  image(params = {}, callback) {
251
- if (typeof params != 'object') {
252
- throw Error(`[GUI] image() first parameter must be an object. Received: ${typeof params}.`);
253
- }
254
-
255
- let path;
256
- if (typeof params.path == 'string') {
257
- path = params.path;
258
- } else {
259
- if (typeof params.path == undefined) {
260
- throw Error(`[GUI] image() path must be provided.`);
261
- } else {
262
- throw Error(`[GUI] image() path must be a string.`);
263
- }
264
- }
265
- let filename = path.replace(/^.*[\\\/]/, '');
266
- let label;
267
- if (params.label == undefined) {
268
- label = filename;
269
- } else {
270
- label = typeof params.label == 'string' ? params.label || ' ' : ' ';
271
- }
272
-
273
- const tooltip = (typeof params.tooltip === 'string') ? params.tooltip : (params.tooltip === true ? label : null);
274
-
275
- const selected = params.selected === true;
276
- const selectionBorder = params.selectionBorder !== false;
277
-
278
- // width & height options
279
- let inline_styles = '';
280
- if (params.width) {
281
- if (typeof params.width == 'number') {
282
- params.width += 'px';
283
- }
284
- inline_styles += `flex: 0 0 calc(${params.width} - 5px); `;
285
- }
286
-
287
- if (params.height) {
288
- if (typeof params.height == 'number') {
289
- params.height += 'px';
290
- }
291
- inline_styles += `height: ${params.height}; `;
292
- }
293
-
294
222
  if (!this.imageContainer) {
295
223
  this.imageContainer = document.createElement('div');
296
224
  this.imageContainer.className = 'p-gui__image-container';
297
225
  this.wrapper.append(this.imageContainer);
298
226
  }
299
-
300
- // Image button
301
- const image = document.createElement('div');
302
- image.className = 'p-gui__image';
303
- image.style = 'background-image: url(' + path + '); ' + inline_styles;
304
- if ( tooltip ) {
305
- image.setAttribute('title', tooltip);
306
- }
307
- this.imageContainer.append(image);
308
-
309
- if (selected && selectionBorder) {
310
- image.classList.add('p-gui__image--selected');
311
- }
312
-
313
- // Text inside image
314
- const text = document.createElement('div');
315
- text.className = 'p-gui__image-text';
316
- text.textContent = label;
317
- image.append(text);
318
-
319
- image.addEventListener('click', () => {
320
- let selected_items = image.parentElement.querySelectorAll('.p-gui__image--selected');
321
- for (let i = 0; i < selected_items.length; i++) {
322
- selected_items[i].classList.remove('p-gui__image--selected');
323
- }
324
- if (selectionBorder) {
325
- image.classList.add('p-gui__image--selected');
326
- }
327
- if (typeof callback == 'function') {
328
- callback({ path, text: label });
329
- }
330
- if (this.onUpdate) {
331
- this.onUpdate();
332
- } else if (this.isFolder && this.firstParent.onUpdate) {
333
- this.firstParent.onUpdate();
334
- }
335
- });
336
-
337
- return image;
227
+ const el = new Image(this, params, callback);
228
+ return el;
338
229
  }
339
230
 
340
- slider (params = {}, callback) {
231
+ slider(params = {}, callback) {
232
+ this.imageContainer = null;
341
233
  const el = new Slider(this, params, callback);
342
- this.wrapper.append(el);
234
+ return el;
343
235
  }
344
236
 
345
237
  toggle(params = {}, callback) {
346
- if (typeof params != 'object') {
347
- throw Error(`[GUI] toggle() first parameter must be an object. Received: ${typeof params}.`);
348
- }
349
-
350
- let label = typeof params.label == 'string' ? params.label || ' ' : ' ';
351
- let isObject = false;
352
- let propReferenceIndex = null;
353
- let obj = params.obj;
354
- let prop = params.prop;
355
- let value = typeof params.value === 'boolean' ? params.value : null;
356
-
357
- // callback mode
358
- if ( value !== null ) {
359
- if (prop != undefined || obj != undefined) {
360
- console.warn(`[GUI] toggle() "obj" and "prop" parameters are ignored when a "value" parameter is used.`);
361
- }
362
- }
363
-
364
- // object-binding
365
- else if (prop != undefined && obj != undefined) {
366
- if (typeof prop != 'string') {
367
- throw Error(`[GUI] toggle() "prop" parameter must be an string. Received: ${typeof prop}.`);
368
- }
369
- if (typeof obj != 'object') {
370
- throw Error(`[GUI] toggle() "obj" parameter must be an object. Received: ${typeof obj}.`);
371
- }
372
-
373
- if (label == ' ') {
374
- label = prop;
375
- }
376
-
377
- propReferenceIndex = this.propReferences.push(obj[prop]) - 1;
378
- isObject = true;
379
- }
380
- else {
381
- if ((prop != undefined && obj == undefined) || (prop == undefined && obj == undefined)) {
382
- console.warn(`[GUI] toggle() "obj" and "prop" parameters must be used together.`);
383
- }
384
- }
385
-
386
- const tooltip = (typeof params.tooltip === 'string') ? params.tooltip : (params.tooltip === true ? label : null);
387
-
388
238
  this.imageContainer = null;
389
-
390
- const container = document.createElement('div');
391
- container.textContent = label;
392
- container.className = 'p-gui__switch';
393
- if ( tooltip ) {
394
- container.setAttribute('title', tooltip);
395
- }
396
- this.wrapper.append(container);
397
-
398
- container.addEventListener('click', (ev) => {
399
- const checkbox = ev.target.childNodes[1];
400
-
401
- let value = true;
402
-
403
- if (checkbox.classList.contains('p-gui__switch-checkbox--active')) {
404
- value = false;
405
- }
406
-
407
- checkbox.classList.toggle('p-gui__switch-checkbox--active');
408
-
409
- if ( isObject ) {
410
- obj[prop] = value;
411
- }
412
-
413
- else {
414
- if (typeof callback == 'function') {
415
- callback(value);
416
- }
417
- }
418
-
419
- if (this.onUpdate) {
420
- this.onUpdate();
421
- } else if (this.isFolder && this.firstParent.onUpdate) {
422
- this.firstParent.onUpdate();
423
- }
424
- });
425
-
426
- let activeClass = (() => {
427
- if (!isObject) {
428
- return value ? ' p-gui__switch-checkbox--active' : '';
429
- } else {
430
- return obj[prop] ? ' p-gui__switch-checkbox--active' : '';
431
- }
432
- })();
433
-
434
- const checkbox = document.createElement('div');
435
- checkbox.className = 'p-gui__switch-checkbox' + activeClass;
436
- container.append(checkbox);
437
-
438
- if ( isObject ) {
439
- Object.defineProperty( obj, prop, {
440
- set: val => {
441
- this.propReferences[propReferenceIndex] = val;
442
-
443
- if (val) {
444
- checkbox.classList.add('p-gui__switch-checkbox--active');
445
- } else {
446
- checkbox.classList.remove('p-gui__switch-checkbox--active');
447
- }
448
-
449
- if (typeof callback == 'function') {
450
- callback(val);
451
- }
452
- },
453
- get: () => {
454
- return this.propReferences[propReferenceIndex];
455
- }
456
- });
457
- }
239
+ const el = new Toggle(this, params, callback);
240
+ this.wrapper.append(el);
241
+ return el;
458
242
  }
459
243
 
460
244
  list(params = {}, callback) {
461
- if (typeof params != 'object') {
462
- throw Error(`[GUI] list() first parameter must be an object. Received: ${typeof params}.`);
463
- }
464
-
465
- let label = typeof params.label == 'string' ? params.label : ' ';
466
- let isObject = false;
467
- let propReferenceIndex = null;
468
- let obj = params.obj;
469
- let prop = params.prop;
470
- let values = Array.isArray(params.values) ? params.values : null;
471
- let value;
472
- let objectValues = typeof values[0] == 'string' ? false : true;
473
- const tooltip = (typeof params.tooltip === 'string') ? params.tooltip : (params.tooltip === true ? label : null);
474
-
475
- callback = typeof callback == 'function' ? callback : null;
476
-
477
- // callback mode
478
- if ( params.value !== undefined ||
479
- (params.value === undefined && obj === undefined && prop === undefined)) {
480
- if (prop != undefined || obj != undefined) {
481
- console.warn(`[GUI] list() "obj" and "prop" parameters are ignored when a "value" parameter is used.`);
482
- }
483
-
484
- value = (() => {
485
- if (!values) {
486
- return null;
487
- }
488
- if (typeof params.value == 'string') {
489
- return values.indexOf(params.value);
490
- }
491
- if (typeof params.value == 'number') {
492
- return params.value;
493
- }
494
- })();
495
- }
496
-
497
- // object-binding mode
498
- else if (prop != undefined && obj != undefined) {
499
- if (typeof prop != 'string') {
500
- throw Error(`[GUI] list() "prop" parameter must be an string. Received: ${typeof prop}.`);
501
- }
502
- if (typeof obj != 'object') {
503
- throw Error(`[GUI] list() "obj" parameter must be an object. Received: ${typeof obj}.`);
504
- }
505
-
506
- value = (() => {
507
- if (!values) {
508
- return null;
509
- }
510
- if (typeof obj[prop] == 'string') {
511
- if ( !objectValues ) { // values is an array of strings
512
- return values.indexOf(obj[prop]);
513
- }
514
- else { // values is an array of objects
515
- return values.find(item => item.value === obj[prop]).value;
516
- }
517
- }
518
- if (typeof obj[prop] == 'number') {
519
- if ( !objectValues ) { // values is an array of strings
520
- return obj[prop];
521
- }
522
- else { // values is an array of objects
523
- return values.find(item => item.value === obj[prop]).value;
524
- }
525
- }
526
- })();
527
-
528
- propReferenceIndex = this.propReferences.push(obj[prop]) - 1;
529
- isObject = true;
530
- }
531
-
532
- else {
533
- if ((prop != undefined && obj == undefined) || (prop == undefined && obj == undefined)) {
534
- console.warn(`[GUI] list() "obj" and "prop" parameters must be used together.`);
535
- }
536
- }
537
-
538
245
  this.imageContainer = null;
539
-
540
- let container = document.createElement('div');
541
- container.className = 'p-gui__list';
542
- container.textContent = label;
543
- if (tooltip) {
544
- container.setAttribute('title', tooltip);
545
- }
546
- this.wrapper.append(container);
547
-
548
- let select = document.createElement('select');
549
- container.append(select);
550
- select.className = 'p-gui__list-dropdown';
551
- select.addEventListener('change', (ev) => {
552
- if ( isObject ) {
553
- obj[prop] = ev.target.value;
554
- }
555
-
556
- else if (callback) {
557
- callback(ev.target.value);
558
- }
559
-
560
- if (this.onUpdate) {
561
- this.onUpdate();
562
- } else if (this.isFolder && this.firstParent.onUpdate) {
563
- this.firstParent.onUpdate();
564
- }
565
- });
566
-
567
- if (values)
568
- {
569
- values.forEach((item, index) =>
570
- {
571
- const optionName = objectValues ? item.label : item;
572
- const optionValue = objectValues ? item.value : item;
573
- let option = document.createElement('option');
574
- option.setAttribute('value', optionValue);
575
- option.textContent = optionName;
576
- select.append(option);
577
-
578
- if (!objectValues && value == index || objectValues && value == optionValue) {
579
- option.setAttribute('selected', '');
580
- }
581
- });
582
- }
583
-
584
- if ( isObject ) {
585
- Object.defineProperty( obj, prop, {
586
- set: val => {
587
- let newIndex, newValue, newObj;
588
- if (objectValues) {
589
- newObj = values.find(item => {
590
- return item.value == val;
591
- });
592
- newValue = newObj?.value || values[0].value;
593
- newIndex = values.indexOf(newObj);
594
- } else {
595
- if (typeof val == 'string') {
596
- newIndex = values.indexOf(val);
597
- newValue = val;
598
- }
599
- if (typeof val == 'number') {
600
- newIndex = val;
601
- newValue = values[val];
602
- }
603
- }
604
-
605
- this.propReferences[propReferenceIndex] = objectValues ? newValue : val;
606
-
607
- const previousSelection = select.querySelector('[selected]');
608
- if ( previousSelection ) {
609
- previousSelection.removeAttribute('selected')
610
- }
611
- select.querySelectorAll('option')[newIndex].setAttribute('selected', '');
612
-
613
- if (typeof callback == 'function') {
614
- if (objectValues) {
615
- callback(newObj, newIndex);
616
- } else {
617
- callback(newValue, newIndex);
618
- }
619
- }
620
- },
621
- get: () => {
622
- return this.propReferences[propReferenceIndex];
623
- }
624
- });
625
- }
246
+ const el = new List(this, params, callback);
247
+ return el;
626
248
  }
627
249
 
628
250
  color(params = {}, callback) {
629
- if (typeof params != 'object') {
630
- throw Error(`[GUI] color() first parameter must be an object. Received: ${typeof params}.`);
631
- }
632
-
633
- let label = typeof params.label == 'string' ? params.label || ' ' : ' ';
634
-
635
- let isObject = false;
636
- let propReferenceIndex = null;
637
- let obj = params.obj;
638
- let prop = params.prop;
639
- let value;
640
- const tooltip = (typeof params.tooltip === 'string') ? params.tooltip : (params.tooltip === true ? label : null);
641
-
642
- if (typeof params.value == 'string') {
643
- if (params.value.length != 7 || params.value[0] != '#') {
644
- console.error(`[GUI] color() 'value' parameter must be an hexadecimal string in the format "#ffffff". Received: "${params.value}".`)
645
- }
646
- else {
647
- value = params.value;
648
- }
649
- }
650
- if (!value) value = '#000000';
651
-
652
- // callback mode
653
- if ( params.value !== undefined ) {
654
- if (prop != undefined || obj != undefined) {
655
- console.warn(`[GUI] color() "obj" and "prop" parameters are ignored when a "value" parameter is used.`);
656
- }
657
- }
658
-
659
- // object-binding
660
- else if (prop != undefined && obj != undefined) {
661
- if (typeof prop != 'string') {
662
- throw Error(`[GUI] color() "prop" parameter must be an string. Received: ${typeof prop}.`);
663
- }
664
- if (typeof obj != 'object') {
665
- throw Error(`[GUI] color() "obj" parameter must be an object. Received: ${typeof obj}.`);
666
- }
667
-
668
- if (label == ' ') {
669
- label = prop;
670
- }
671
-
672
- propReferenceIndex = this.propReferences.push(obj[prop]) - 1;
673
- isObject = true;
674
- }
675
- else {
676
- if ((prop != undefined && obj == undefined) || (prop == undefined && obj == undefined)) {
677
- console.warn(`[GUI] color() "obj" and "prop" parameters must be used together.`);
678
- }
679
- }
680
-
681
251
  this.imageContainer = null;
682
-
683
- const container = document.createElement('div');
684
- container.className = 'p-gui__color';
685
- container.textContent = label;
686
- if ( tooltip ) {
687
- container.setAttribute('title', tooltip);
688
- }
689
- this.wrapper.append(container);
690
-
691
- const colorpicker = document.createElement('input');
692
- colorpicker.className = 'p-gui__color-picker';
693
- colorpicker.setAttribute('type', 'color');
694
- colorpicker.value = value;
695
- container.append(colorpicker);
696
-
697
- if (typeof callback == 'function') {
698
- colorpicker.addEventListener('input', () => {
699
- if ( isObject ) {
700
- obj[prop] = colorpicker.value;
701
- }
702
-
703
- else if (typeof callback == 'function') {
704
- callback(colorpicker.value);
705
- }
706
-
707
- if (this.onUpdate) {
708
- this.onUpdate();
709
- } else if (this.isFolder && this.firstParent.onUpdate) {
710
- this.firstParent.onUpdate();
711
- }
712
- });
713
- }
714
-
715
- if ( isObject ) {
716
- Object.defineProperty( obj, prop, {
717
- set: val => {
718
- this.propReferences[propReferenceIndex] = val;
719
-
720
- colorpicker.value = val;
721
-
722
- if (typeof callback == 'function') {
723
- callback(val);
724
- }
725
- },
726
- get: () => {
727
- return this.propReferences[propReferenceIndex];
728
- }
729
- });
730
- }
252
+ const el = new Color(this, params, callback);
253
+ return el;
731
254
  }
732
255
 
733
- vector2( params = {}, callback) {
734
- if (typeof params != 'object') {
735
- throw Error(`[GUI] vector2() first parameter must be an object. Received: ${typeof params}.`);
736
- }
737
-
738
- let label = typeof params.label == 'string' ? params.label || ' ' : ' ';
739
-
740
- const minX = params.x.min ?? 0;
741
- const maxX = params.x.max ?? 1;
742
- const minY = params.y.min ?? 0;
743
- const maxY = params.y.max ?? 1;
744
- const stepX = params.x.step || (maxX - minX) / 100;
745
- const stepY = params.y.step || (maxY - minY) / 100;
746
- const decimalsX = this._countDecimals(stepX);
747
- const decimalsY = this._countDecimals(stepY);
748
-
749
- const objectX = params.x.obj;
750
- const propX = params.x.prop;
751
- const propXReferenceIndex = this.propReferences.push(objectX[propX]) - 1;
752
-
753
- const objectY = params.y.obj;
754
- const propY = params.y.prop;
755
- const propYReferenceIndex = this.propReferences.push(objectY[propY]) - 1;
756
-
757
- const tooltip = (typeof params.tooltip === 'string') ? params.tooltip : (params.tooltip === true ? label : null);
758
-
759
- callback = typeof callback == 'function' ? callback : null;
760
-
256
+ vector2(params = {}, callback) {
761
257
  this.imageContainer = null;
762
-
763
- const container = document.createElement('div');
764
- container.className = 'p-gui__vector2';
765
- container.textContent = label;
766
- if ( tooltip ) {
767
- container.setAttribute('title', tooltip);
768
- }
769
- this.wrapper.append(container);
770
-
771
- const vector_value = document.createElement('div');
772
- vector_value.className = 'p-gui__vector-value';
773
- vector_value.textContent = objectX[propX] + ', ' + objectY[propY];
774
- container.append(vector_value);
775
-
776
- const area = document.createElement('div');
777
- area.className = 'p-gui__vector2-area';
778
- container.append(area);
779
- area.addEventListener('click', evt => {
780
- const newX = parseFloat(this._mapLinear(evt.offsetX, 0, area.clientWidth, minX, maxX));
781
- const newY = parseFloat(this._mapLinear(evt.offsetY, 0, area.clientHeight, maxY, minY));
782
- objectX[propX] = newX.toFixed(decimalsX);
783
- objectY[propY] = newY.toFixed(decimalsY);
784
-
785
- if (callback) {
786
- callback(objectX[propX], objectX[propY]);
787
- }
788
-
789
- if (this.onUpdate) {
790
- this.onUpdate();
791
- } else if (this.isFolder && this.firstParent.onUpdate) {
792
- this.firstParent.onUpdate();
793
- }
794
- });
795
-
796
- let pointer_is_down = false;
797
- area.addEventListener('pointerdown', (evt) => {
798
- pointer_is_down = true;
799
- });
800
- area.addEventListener('pointerup', () => {
801
- pointer_is_down = false;
802
- });
803
- area.addEventListener('pointermove', (evt) => {
804
- if (pointer_is_down) {
805
- const newX = parseFloat(this._mapLinear(evt.offsetX, 0, area.clientWidth, minX, maxX));
806
- const newY = parseFloat(this._mapLinear(evt.offsetY, 0, area.clientHeight, maxY, minY));
807
- objectX[propX] = newX.toFixed(decimalsX);
808
- objectY[propY] = newY.toFixed(decimalsY);
809
-
810
- if (callback) {
811
- callback(objectX[propX], objectX[propY]);
812
- }
813
-
814
- if (this.onUpdate) {
815
- this.onUpdate();
816
- } else if (this.isFolder && this.firstParent.onUpdate) {
817
- this.firstParent.onUpdate();
818
- }
819
- }
820
- });
821
-
822
- const line_x = document.createElement('div');
823
- line_x.className = 'p-gui__vector2-line p-gui__vector2-line-x';
824
- area.append(line_x);
825
-
826
- const line_y = document.createElement('div');
827
- line_y.className = 'p-gui__vector2-line p-gui__vector2-line-y';
828
- area.append(line_y);
829
-
830
- const dot = document.createElement('div');
831
- dot.className = 'p-gui__vector2-dot';
832
- area.append(dot);
833
-
834
- dot.style.left = this._mapLinear(objectX[propX], minX, maxX, 0, area.clientWidth) + 'px';
835
- dot.style.top = this._mapLinear(objectY[propY], minY, maxY, area.clientHeight, 0) + 'px';
836
-
837
- Object.defineProperty( objectX, propX, {
838
- set: val => {
839
- this.propReferences[propXReferenceIndex] = val;
840
- dot.style.left = this._mapLinear(val, minX, maxX, 0, area.clientWidth) + 'px';
841
- vector_value.textContent = String( val ) + ', ' + objectY[propY];
842
- },
843
- get: () => {
844
- return this.propReferences[propXReferenceIndex];
845
- }
846
- });
847
-
848
- Object.defineProperty( objectY, propY, {
849
- set: val => {
850
- this.propReferences[propYReferenceIndex] = val;
851
- dot.style.top = this._mapLinear(val, minY, maxY, area.clientHeight, 0) + 'px';
852
- vector_value.textContent = objectX[propX] + ', ' + String( val );
853
- },
854
- get: () => {
855
- return this.propReferences[propYReferenceIndex];
856
- }
857
- });
258
+ const el = new Vector2(this, params, callback);
259
+ return el;
858
260
  }
859
261
 
860
262
  folder(options = {}) {
@@ -2,7 +2,7 @@ export default /* css */ `
2
2
  .p-gui__folder {
3
3
  width: 100%;
4
4
  position: relative;
5
- background: #333333;
5
+ background: var(--color-bg);
6
6
  overflow: auto;
7
7
  margin-bottom: 2px;
8
8
  display: flex;