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.
- package/.eslintrc.js +30 -0
- package/dist/perfect-gui.js +454 -583
- package/package.json +5 -3
- package/src/components/{Button.js → Button.ts} +26 -12
- package/src/components/Color.ts +93 -0
- package/src/components/{Image.js → Image.ts} +40 -25
- package/src/components/List.ts +173 -0
- package/src/components/{Slider.js → Slider.ts} +78 -77
- package/src/components/Toggle.ts +98 -0
- package/src/components/{Vector2.js → Vector2.ts} +47 -36
- package/src/{index.js → index.ts} +287 -142
- package/src/plugins/three.js +2 -8
- package/tsconfig.json +111 -0
- package/src/components/Color.js +0 -107
- package/src/components/List.js +0 -178
- package/src/components/Toggle.js +0 -124
|
@@ -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
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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
|
|
44
|
+
let label = typeof options.label == 'string' ? options.label || ' ' : ' ';
|
|
24
45
|
|
|
25
|
-
if (
|
|
46
|
+
if (label == ' ') {
|
|
26
47
|
label = this.prop;
|
|
27
48
|
}
|
|
28
49
|
|
|
29
|
-
this.min =
|
|
30
|
-
this.max =
|
|
31
|
-
this.step =
|
|
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
|
-
|
|
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
|
|
46
|
-
?
|
|
47
|
-
:
|
|
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.
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
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
|
|
7
|
-
let objectX, objectY, propX, propY;
|
|
27
|
+
let objectX, objectY;
|
|
8
28
|
|
|
9
29
|
if (
|
|
10
|
-
|
|
11
|
-
typeof
|
|
12
|
-
typeof
|
|
13
|
-
typeof
|
|
30
|
+
obj &&
|
|
31
|
+
typeof obj === 'object' &&
|
|
32
|
+
typeof propX === 'string' &&
|
|
33
|
+
typeof propY === 'string'
|
|
14
34
|
) {
|
|
15
|
-
objectX =
|
|
16
|
-
objectY =
|
|
17
|
-
propX =
|
|
18
|
-
propY =
|
|
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',
|
|
41
|
+
`[GUI] vector2() invalid parameters. Use: gui.vector2(obj, 'propX', 'propY', options)`,
|
|
31
42
|
);
|
|
32
43
|
}
|
|
33
44
|
|
|
34
|
-
let label = typeof
|
|
45
|
+
let label = typeof options.label == 'string' ? options.label || ' ' : ' ';
|
|
35
46
|
if (label === ' ') label = propX + ' / ' + propY;
|
|
36
47
|
|
|
37
|
-
const safeParamsX =
|
|
38
|
-
const safeParamsY =
|
|
48
|
+
const safeParamsX = options.x || {};
|
|
49
|
+
const safeParamsY = options.y || {};
|
|
39
50
|
|
|
40
|
-
const minX = safeParamsX.min ??
|
|
41
|
-
const maxX = safeParamsX.max ??
|
|
42
|
-
const minY = safeParamsY.min ??
|
|
43
|
-
const maxY = safeParamsY.max ??
|
|
44
|
-
const stepX = safeParamsX.step ||
|
|
45
|
-
const stepY = safeParamsY.step ||
|
|
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
|
|
56
|
-
?
|
|
57
|
-
:
|
|
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
|
}
|