perfect-gui 5.1.2 → 6.0.0

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.
@@ -1,50 +1,62 @@
1
+ import type GUI from '../index';
2
+
3
+ export type Options = {
4
+ label?: string;
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ tooltip?: string;
9
+ }
10
+
1
11
  export default class Slider {
2
- constructor(parent, arg1, arg2, arg3) {
12
+ private parent: GUI;
13
+ private propReferences: string[];
14
+ private min: number;
15
+ private max: number;
16
+ private step: number;
17
+ private decimals: number;
18
+ private obj: any;
19
+ private prop: string;
20
+ private callback: ((value: number) => void) | null = null;
21
+
22
+ private ctrlDiv: HTMLDivElement & {
23
+ pointerDown?: boolean;
24
+ prevPosition?: number;
25
+ pointerDelta?: number
26
+ };
27
+ private handle: HTMLElement & { position?: number };
28
+ private filling: HTMLElement;
29
+ private valueInput: HTMLInputElement;
30
+
31
+ public element: HTMLElement;
32
+
33
+ constructor(parent: GUI, obj: any, prop: string, options: Options = {}) {
3
34
  this.parent = parent;
4
35
  this.propReferences = [];
5
36
 
6
- let params = {};
7
- let value = null;
8
-
9
- if (arg1 && typeof arg1 === 'object' && typeof arg2 === 'string') {
10
- this.obj = arg1;
11
- this.prop = arg2;
12
- this.isObject = true;
13
- params = arg3 || {};
14
- this.callback = null;
15
- } else if (arg1 && typeof arg1 === 'object') {
16
- this.isObject = false;
17
- params = arg1;
18
- value = typeof params.value == 'number' ? params.value : null;
37
+ if (obj && typeof obj === 'object' && typeof prop === 'string') {
38
+ this.obj = obj;
39
+ this.prop = prop;
19
40
  } else {
20
41
  throw Error(`[GUI] slider() invalid parameters.`);
21
42
  }
22
43
 
23
- let label = typeof params.label == 'string' ? params.label || ' ' : ' ';
44
+ let label = typeof options.label == 'string' ? options.label || ' ' : ' ';
24
45
 
25
- if (this.isObject && label == ' ') {
46
+ if (label == ' ') {
26
47
  label = this.prop;
27
48
  }
28
49
 
29
- this.min = params.min ?? 0;
30
- this.max = params.max ?? 1;
31
- this.step = params.step || (this.max - this.min) / 100;
50
+ this.min = options.min ?? 0;
51
+ this.max = options.max ?? 1;
52
+ this.step = options.step || (this.max - this.min) / 100;
32
53
  this.decimals = this.parent._countDecimals(this.step);
33
54
 
34
- let propReferenceIndex = null;
35
-
36
- if (this.isObject) {
37
- propReferenceIndex =
38
- this.propReferences.push(this.obj[this.prop]) - 1;
39
- } else {
40
- if (value === null) {
41
- value = (this.max - this.min) / 2;
42
- }
43
- }
55
+ const propReferenceIndex = this.propReferences.push(this.obj[this.prop]) - 1;
44
56
  const tooltip =
45
- typeof params.tooltip === 'string'
46
- ? params.tooltip
47
- : params.tooltip === true
57
+ typeof options.tooltip === 'string'
58
+ ? options.tooltip
59
+ : options.tooltip === true
48
60
  ? label
49
61
  : null;
50
62
 
@@ -66,10 +78,11 @@ export default class Slider {
66
78
  container.append(slider_name);
67
79
 
68
80
  this.ctrlDiv = document.createElement('div');
81
+ this.ctrlDiv.prevPosition = 0;
69
82
  this.ctrlDiv.className = 'p-gui__slider-ctrl';
70
83
  this.ctrlDiv.setAttribute('type', 'range');
71
- this.ctrlDiv.setAttribute('min', this.min);
72
- this.ctrlDiv.setAttribute('max', this.max);
84
+ this.ctrlDiv.setAttribute('min', String(this.min));
85
+ this.ctrlDiv.setAttribute('max', String(this.max));
73
86
  container.append(this.ctrlDiv);
74
87
 
75
88
  const slider_bar = document.createElement('div');
@@ -86,15 +99,15 @@ export default class Slider {
86
99
 
87
100
  this.valueInput = document.createElement('input');
88
101
  this.valueInput.className = 'p-gui__slider-value';
89
- this.valueInput.value = this.isObject ? this.obj[this.prop] : value;
102
+ this.valueInput.value = this.obj[this.prop];
90
103
  container.append(this.valueInput);
91
104
 
92
105
  // init position
93
106
  setTimeout(() => {
94
107
  const sliderWidth = this.ctrlDiv.offsetWidth;
95
108
  const handleWidth = this.handle.offsetWidth;
96
- this.handle.position = this._mapLinear(
97
- this.valueInput.value,
109
+ this.handle.position = this.parent._mapLinear(
110
+ parseFloat(this.valueInput.value),
98
111
  this.min,
99
112
  this.max,
100
113
  handleWidth / 2,
@@ -134,42 +147,40 @@ export default class Slider {
134
147
  window.addEventListener('pointermove', (evt) => {
135
148
  if (this.ctrlDiv.pointerDown) {
136
149
  this.ctrlDiv.pointerDelta =
137
- evt.clientX - this.ctrlDiv.prevPosition;
150
+ evt.clientX - (this.ctrlDiv.prevPosition ?? 0);
138
151
  this._updateHandlePositionFromPointer(evt);
139
152
  }
140
153
  });
141
154
 
142
- if (this.isObject) {
143
- Object.defineProperty(this.obj, this.prop, {
144
- set: (val) => {
145
- this.propReferences[propReferenceIndex] = val;
146
- this.valueInput.value = val;
147
-
148
- this._updateHandlePositionFromValue();
149
-
150
- if (this.callback) {
151
- this.callback(parseFloat(this.valueInput.value));
152
- }
153
- },
154
- get: () => {
155
- return this.propReferences[propReferenceIndex];
156
- },
157
- });
158
- }
155
+ Object.defineProperty(this.obj, this.prop, {
156
+ set: (val) => {
157
+ this.propReferences[propReferenceIndex] = val;
158
+ this.valueInput.value = val;
159
+
160
+ this._updateHandlePositionFromValue();
161
+
162
+ if (this.callback) {
163
+ this.callback(parseFloat(this.valueInput.value));
164
+ }
165
+ },
166
+ get: () => {
167
+ return this.propReferences[propReferenceIndex];
168
+ },
169
+ });
159
170
  }
160
171
 
161
- _updateHandlePositionFromPointer(evt, firstDown = false) {
172
+ _updateHandlePositionFromPointer(evt: PointerEvent, firstDown = false) {
162
173
  const rect = this.ctrlDiv.getBoundingClientRect();
163
174
  const sliderWidth = rect.width;
164
175
  const handleWidth = this.handle.offsetWidth;
165
- const pointerDelta = evt.clientX - this.ctrlDiv.prevPosition;
176
+ const pointerDelta = evt.clientX - (this.ctrlDiv.prevPosition ?? 0);
166
177
  const currentValue = parseFloat(this.valueInput.value);
167
178
  let handlePosition;
168
179
 
169
180
  if (firstDown) {
170
181
  handlePosition = evt.clientX - rect.left;
171
182
  } else {
172
- handlePosition = this.handle.position + pointerDelta;
183
+ handlePosition = (this.handle.position ?? 0) + pointerDelta;
173
184
  }
174
185
 
175
186
  handlePosition = Math.max(
@@ -193,9 +204,9 @@ export default class Slider {
193
204
  const prevValue = parseFloat((currentValue - this.step).toFixed(9));
194
205
 
195
206
  if (newValue >= nextValue || newValue <= prevValue) {
196
- newValue = newValue.toFixed(this.decimals);
207
+ newValue = parseFloat(newValue.toFixed(this.decimals));
197
208
 
198
- this.valueInput.value = newValue;
209
+ this.valueInput.value = String(newValue);
199
210
 
200
211
  this.ctrlDiv.prevPosition = evt.clientX;
201
212
 
@@ -211,8 +222,8 @@ export default class Slider {
211
222
  _updateHandlePositionFromValue() {
212
223
  const sliderWidth = this.ctrlDiv.offsetWidth;
213
224
  const handleWidth = this.handle.offsetWidth;
214
- let handlePosition = this._mapLinear(
215
- this.valueInput.value,
225
+ let handlePosition = this.parent._mapLinear(
226
+ parseFloat(this.valueInput.value),
216
227
  this.min,
217
228
  this.max,
218
229
  handleWidth / 2,
@@ -231,13 +242,7 @@ export default class Slider {
231
242
  }
232
243
 
233
244
  _triggerCallbacks() {
234
- if (this.isObject) {
235
- this.obj[this.prop] = parseFloat(this.valueInput.value);
236
- } else {
237
- if (this.callback) {
238
- this.callback(parseFloat(this.valueInput.value));
239
- }
240
- }
245
+ this.obj[this.prop] = parseFloat(this.valueInput.value);
241
246
 
242
247
  if (this.parent.onUpdate) {
243
248
  this.parent.onUpdate();
@@ -246,23 +251,19 @@ export default class Slider {
246
251
  }
247
252
  }
248
253
 
249
- _mapLinear(x, a1, a2, b1, b2) {
250
- return b1 + ((x - a1) * (b2 - b1)) / (a2 - a1);
251
- }
252
-
253
- _quantize(x, step) {
254
+ _quantize(x: number, step: number): number {
254
255
  return step * Math.round(x / step);
255
256
  }
256
257
 
257
- _quantizeCeil(x, step) {
258
+ _quantizeCeil(x: number, step: number): number {
258
259
  return step * Math.ceil(x / step);
259
260
  }
260
261
 
261
- _quantizeFloor(x, step) {
262
+ _quantizeFloor(x: number, step: number): number {
262
263
  return step * Math.floor(x / step);
263
264
  }
264
265
 
265
- onChange(callback) {
266
+ onChange(callback: (value: number) => void) {
266
267
  this.callback = callback;
267
268
  return this;
268
269
  }
@@ -0,0 +1,98 @@
1
+ import type GUI from '../index';
2
+
3
+ export type Options = {
4
+ label?: string;
5
+ tooltip?: string;
6
+ }
7
+
8
+ export default class Toggle {
9
+ private callback: ((value: boolean) => void) | null;
10
+
11
+ public element: HTMLDivElement;
12
+
13
+ constructor(private parent: GUI, obj: any, prop: string, options: Options = {}) {
14
+ this.callback = null;
15
+
16
+ if (!obj || typeof obj !== 'object' || typeof prop !== 'string') {
17
+ throw Error(`[GUI] toggle() invalid parameters.`);
18
+ }
19
+
20
+ let label =
21
+ typeof options.label === 'string' && options.label !== ''
22
+ ? options.label
23
+ : prop;
24
+ const propReferenceIndex =
25
+ this.parent.propReferences.push(obj[prop]) - 1;
26
+
27
+ const tooltip =
28
+ typeof options.tooltip === 'string'
29
+ ? options.tooltip
30
+ : options.tooltip === true
31
+ ? label
32
+ : null;
33
+
34
+ const container = document.createElement('div');
35
+ container.textContent = label;
36
+ container.className = 'p-gui__toggle';
37
+ if (tooltip) {
38
+ container.setAttribute('title', tooltip);
39
+ }
40
+ this.parent.wrapper.append(container);
41
+
42
+ // Expose the DOM element
43
+ this.element = container;
44
+
45
+ let activeClass = obj[prop] ? ' p-gui__toggle-checkbox--active' : '';
46
+
47
+ const checkbox = document.createElement('div');
48
+ checkbox.className = 'p-gui__toggle-checkbox' + activeClass;
49
+ container.append(checkbox);
50
+
51
+ container.addEventListener('click', (ev) => {
52
+ if (!ev.target || !(ev.target instanceof HTMLElement)) return;
53
+
54
+ let value = true;
55
+
56
+ if (checkbox.classList.contains('p-gui__toggle-checkbox--active')) {
57
+ value = false;
58
+ }
59
+
60
+ checkbox.classList.toggle('p-gui__toggle-checkbox--active');
61
+
62
+ obj[prop] = value;
63
+
64
+ if (this.parent.onUpdate) {
65
+ this.parent.onUpdate();
66
+ } else if (
67
+ this.parent.isFolder &&
68
+ this.parent.firstParent.onUpdate
69
+ ) {
70
+ this.parent.firstParent.onUpdate();
71
+ }
72
+ });
73
+
74
+ Object.defineProperty(obj, prop, {
75
+ set: (val) => {
76
+ this.parent.propReferences[propReferenceIndex] = val;
77
+
78
+ if (val) {
79
+ checkbox.classList.add('p-gui__toggle-checkbox--active');
80
+ } else {
81
+ checkbox.classList.remove('p-gui__toggle-checkbox--active');
82
+ }
83
+
84
+ if (typeof this.callback == 'function') {
85
+ this.callback(val);
86
+ }
87
+ },
88
+ get: () => {
89
+ return this.parent.propReferences[propReferenceIndex];
90
+ },
91
+ });
92
+ }
93
+
94
+ onChange(callback: (value: boolean) => void) {
95
+ this.callback = callback;
96
+ return this;
97
+ }
98
+ }
@@ -1,48 +1,59 @@
1
+ import type GUI from '../index';
2
+
3
+ type AxisOption = {
4
+ min?: number;
5
+ max?: number;
6
+ step?: number;
7
+ }
8
+
9
+ export type Options = {
10
+ label?: string;
11
+ tooltip?: string;
12
+ min?: number;
13
+ max?: number;
14
+ step?: number;
15
+ x?: AxisOption; // X axis options
16
+ y?: AxisOption; // Y axis options
17
+ }
18
+
19
+ type Callback = ((x: number, y: number) => void);
20
+
1
21
  export default class Vector2 {
2
- constructor(parent, arg1, arg2, arg3, arg4) {
3
- this.parent = parent;
22
+ private callback: Callback | null;
23
+
24
+ constructor(private parent: GUI, obj: any, propX: string, propY: string, options: Options = {}) {
4
25
  this.callback = null;
5
26
 
6
- let params = {};
7
- let objectX, objectY, propX, propY;
27
+ let objectX, objectY;
8
28
 
9
29
  if (
10
- arg1 &&
11
- typeof arg1 === 'object' &&
12
- typeof arg2 === 'string' &&
13
- typeof arg3 === 'string'
30
+ obj &&
31
+ typeof obj === 'object' &&
32
+ typeof propX === 'string' &&
33
+ typeof propY === 'string'
14
34
  ) {
15
- objectX = arg1;
16
- objectY = arg1;
17
- propX = arg2;
18
- propY = arg3;
19
- params = arg4 || {};
20
- } else if (arg1 && typeof arg1 === 'object' && arg1.x && arg1.x.obj) {
21
- // Backwards compatibility for the old verbose { x: { obj, prop }, y: { obj, prop } } structure
22
- // incase it's heavily used, since Vector2 doesn't have a 'simple' mode.
23
- params = arg1;
24
- objectX = params.x.obj;
25
- propX = params.x.prop;
26
- objectY = params.y.obj;
27
- propY = params.y.prop;
35
+ objectX = obj;
36
+ objectY = obj;
37
+ propX = propX;
38
+ propY = propY;
28
39
  } else {
29
40
  throw Error(
30
- `[GUI] vector2() invalid parameters. Use: gui.vector2(obj, 'propX', 'propY', params)`,
41
+ `[GUI] vector2() invalid parameters. Use: gui.vector2(obj, 'propX', 'propY', options)`,
31
42
  );
32
43
  }
33
44
 
34
- let label = typeof params.label == 'string' ? params.label || ' ' : ' ';
45
+ let label = typeof options.label == 'string' ? options.label || ' ' : ' ';
35
46
  if (label === ' ') label = propX + ' / ' + propY;
36
47
 
37
- const safeParamsX = params.x || {};
38
- const safeParamsY = params.y || {};
48
+ const safeParamsX = options.x || {};
49
+ const safeParamsY = options.y || {};
39
50
 
40
- const minX = safeParamsX.min ?? params.min ?? 0;
41
- const maxX = safeParamsX.max ?? params.max ?? 1;
42
- const minY = safeParamsY.min ?? params.min ?? 0;
43
- const maxY = safeParamsY.max ?? params.max ?? 1;
44
- const stepX = safeParamsX.step || params.step || (maxX - minX) / 100;
45
- const stepY = safeParamsY.step || params.step || (maxY - minY) / 100;
51
+ const minX = safeParamsX.min ?? options.min ?? 0;
52
+ const maxX = safeParamsX.max ?? options.max ?? 1;
53
+ const minY = safeParamsY.min ?? options.min ?? 0;
54
+ const maxY = safeParamsY.max ?? options.max ?? 1;
55
+ const stepX = safeParamsX.step || options.step || (maxX - minX) / 100;
56
+ const stepY = safeParamsY.step || options.step || (maxY - minY) / 100;
46
57
  const decimalsX = this.parent._countDecimals(stepX);
47
58
  const decimalsY = this.parent._countDecimals(stepY);
48
59
 
@@ -52,9 +63,9 @@ export default class Vector2 {
52
63
  this.parent.propReferences.push(objectY[propY]) - 1;
53
64
 
54
65
  const tooltip =
55
- typeof params.tooltip === 'string'
56
- ? params.tooltip
57
- : params.tooltip === true
66
+ typeof options.tooltip === 'string'
67
+ ? options.tooltip
68
+ : options.tooltip === true
58
69
  ? label
59
70
  : null;
60
71
 
@@ -110,7 +121,7 @@ export default class Vector2 {
110
121
  }
111
122
  });
112
123
 
113
- const handlePointerMove = (evt) => {
124
+ const handlePointerMove = (evt: PointerEvent) => {
114
125
  const rect = area.getBoundingClientRect();
115
126
  const offsetX = evt.clientX - rect.left;
116
127
  const offsetY = evt.clientY - rect.top;
@@ -235,7 +246,7 @@ export default class Vector2 {
235
246
  });
236
247
  }
237
248
 
238
- onChange(callback) {
249
+ onChange(callback: Callback) {
239
250
  this.callback = callback;
240
251
  return this;
241
252
  }