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
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "perfect-gui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "6.0.0",
|
|
5
5
|
"description": "GUI for JavaScript",
|
|
6
6
|
"main": "dist/perfect-gui.js",
|
|
7
7
|
"module": "dist/perfect-gui.js",
|
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://thibka.github.io/perfect-gui/public/",
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"
|
|
28
|
+
"eslint": "^9.39.4",
|
|
29
|
+
"typescript": "^6.0.3",
|
|
30
|
+
"vite": "^8.0.16"
|
|
29
31
|
}
|
|
30
|
-
}
|
|
32
|
+
}
|
|
@@ -1,20 +1,34 @@
|
|
|
1
|
+
import type GUI from '../index.js';
|
|
2
|
+
|
|
3
|
+
export type Options = {
|
|
4
|
+
label?: string;
|
|
5
|
+
tooltip?: string | boolean;
|
|
6
|
+
color?: string;
|
|
7
|
+
hoverColor?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type Callback = () => void;
|
|
11
|
+
|
|
1
12
|
export default class Button {
|
|
2
|
-
|
|
13
|
+
public parent: GUI;
|
|
14
|
+
public callback: null | Callback = null;
|
|
15
|
+
public element: HTMLDivElement;
|
|
16
|
+
|
|
17
|
+
constructor(parent: GUI, options: Options = {}) {
|
|
3
18
|
this.parent = parent;
|
|
4
|
-
this.callback = null;
|
|
5
19
|
|
|
6
|
-
if (typeof
|
|
20
|
+
if (typeof options !== 'object') {
|
|
7
21
|
throw Error(
|
|
8
|
-
`[GUI] button() first parameter must be an object. Received: ${typeof
|
|
22
|
+
`[GUI] button() first parameter must be an object. Received: ${typeof options}.`,
|
|
9
23
|
);
|
|
10
24
|
}
|
|
11
25
|
|
|
12
|
-
let label =
|
|
26
|
+
let label = options.label || ' ';
|
|
13
27
|
|
|
14
28
|
const tooltip =
|
|
15
|
-
typeof
|
|
16
|
-
?
|
|
17
|
-
:
|
|
29
|
+
typeof options.tooltip === 'string'
|
|
30
|
+
? options.tooltip
|
|
31
|
+
: options.tooltip === true
|
|
18
32
|
? label
|
|
19
33
|
: null;
|
|
20
34
|
|
|
@@ -39,11 +53,11 @@ export default class Button {
|
|
|
39
53
|
}
|
|
40
54
|
});
|
|
41
55
|
|
|
42
|
-
if (typeof
|
|
43
|
-
el.style.setProperty('--color-accent',
|
|
56
|
+
if (typeof options.color == 'string') {
|
|
57
|
+
el.style.setProperty('--color-accent', options.color);
|
|
44
58
|
el.style.setProperty(
|
|
45
59
|
'--color-accent-hover',
|
|
46
|
-
|
|
60
|
+
options.hoverColor || options.color,
|
|
47
61
|
);
|
|
48
62
|
}
|
|
49
63
|
|
|
@@ -53,7 +67,7 @@ export default class Button {
|
|
|
53
67
|
this.element = el;
|
|
54
68
|
}
|
|
55
69
|
|
|
56
|
-
onClick(callback) {
|
|
70
|
+
onClick(callback: Callback) {
|
|
57
71
|
this.callback = callback;
|
|
58
72
|
return this;
|
|
59
73
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type GUI from '../index.js';
|
|
2
|
+
|
|
3
|
+
export type Options = {
|
|
4
|
+
label?: string;
|
|
5
|
+
tooltip?: string | boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
type Callback = (value: string) => void;
|
|
9
|
+
|
|
10
|
+
export default class Color {
|
|
11
|
+
private parent: GUI;
|
|
12
|
+
private callback: null | Callback = null;
|
|
13
|
+
public element: HTMLDivElement;
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
parent: GUI,
|
|
17
|
+
obj: any,
|
|
18
|
+
prop: string,
|
|
19
|
+
params: Options = {}
|
|
20
|
+
) {
|
|
21
|
+
this.parent = parent;
|
|
22
|
+
|
|
23
|
+
if (typeof obj !== 'object' || typeof prop !== 'string') {
|
|
24
|
+
throw Error(`[GUI] color() invalid parameters. Expected (object, string, options).`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let label = typeof params.label === 'string' ? params.label || ' ' : ' ';
|
|
28
|
+
if (label === ' ') {
|
|
29
|
+
label = prop;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const tooltip =
|
|
33
|
+
typeof params.tooltip === 'string'
|
|
34
|
+
? params.tooltip
|
|
35
|
+
: params.tooltip === true
|
|
36
|
+
? label
|
|
37
|
+
: null;
|
|
38
|
+
|
|
39
|
+
const propReferenceIndex = this.parent.propReferences.push(obj[prop]) - 1;
|
|
40
|
+
const value = obj[prop] || '#000000';
|
|
41
|
+
|
|
42
|
+
const container = document.createElement('div');
|
|
43
|
+
container.className = 'p-gui__color';
|
|
44
|
+
container.textContent = label;
|
|
45
|
+
if (tooltip) {
|
|
46
|
+
container.setAttribute('title', tooltip);
|
|
47
|
+
}
|
|
48
|
+
this.parent.wrapper.append(container);
|
|
49
|
+
|
|
50
|
+
// Expose the DOM element
|
|
51
|
+
this.element = container;
|
|
52
|
+
|
|
53
|
+
const colorpicker = document.createElement('input');
|
|
54
|
+
colorpicker.className = 'p-gui__color-picker';
|
|
55
|
+
colorpicker.setAttribute('type', 'color');
|
|
56
|
+
colorpicker.value = value;
|
|
57
|
+
container.append(colorpicker);
|
|
58
|
+
|
|
59
|
+
colorpicker.addEventListener('input', () => {
|
|
60
|
+
obj[prop] = colorpicker.value;
|
|
61
|
+
|
|
62
|
+
if (this.parent.onUpdate) {
|
|
63
|
+
this.parent.onUpdate();
|
|
64
|
+
} else if (
|
|
65
|
+
this.parent.isFolder &&
|
|
66
|
+
this.parent.firstParent.onUpdate
|
|
67
|
+
) {
|
|
68
|
+
this.parent.firstParent.onUpdate();
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
Object.defineProperty(obj, prop, {
|
|
73
|
+
set: (val: string) => {
|
|
74
|
+
this.parent.propReferences[propReferenceIndex] = val;
|
|
75
|
+
|
|
76
|
+
colorpicker.value = val;
|
|
77
|
+
|
|
78
|
+
if (typeof this.callback === 'function') {
|
|
79
|
+
this.callback(val);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
get: () => {
|
|
83
|
+
return this.parent.propReferences[propReferenceIndex];
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
onChange(callback: Callback) {
|
|
89
|
+
this.callback = callback;
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
@@ -1,23 +1,36 @@
|
|
|
1
|
+
import type GUI from '../index.js';
|
|
2
|
+
|
|
3
|
+
export type Options = {
|
|
4
|
+
label?: string;
|
|
5
|
+
tooltip?: string | boolean;
|
|
6
|
+
selected?: boolean;
|
|
7
|
+
selectionBorder?: boolean;
|
|
8
|
+
width?: number | string;
|
|
9
|
+
height?: number | string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type Callback = ({path, text}: {path: string, text: string}) => void;
|
|
13
|
+
|
|
1
14
|
export default class Image {
|
|
2
|
-
|
|
15
|
+
public parent: GUI;
|
|
16
|
+
|
|
17
|
+
private callback: null | Callback = null;
|
|
18
|
+
|
|
19
|
+
private element: HTMLElement;
|
|
20
|
+
|
|
21
|
+
constructor(parent: GUI, path: string, params: Options = {}) {
|
|
3
22
|
this.parent = parent;
|
|
4
|
-
this.callback = null;
|
|
5
23
|
|
|
6
|
-
if (
|
|
7
|
-
throw Error(
|
|
8
|
-
|
|
9
|
-
);
|
|
24
|
+
if (path === undefined) {
|
|
25
|
+
throw Error(`[GUI] image() path must be provided.`);
|
|
26
|
+
} else if (typeof path !== 'string') {
|
|
27
|
+
throw Error(`[GUI] image() path must be a string.`);
|
|
10
28
|
}
|
|
11
29
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (typeof params.path == undefined) {
|
|
17
|
-
throw Error(`[GUI] image() path must be provided.`);
|
|
18
|
-
} else {
|
|
19
|
-
throw Error(`[GUI] image() path must be a string.`);
|
|
20
|
-
}
|
|
30
|
+
if (typeof params !== 'object') {
|
|
31
|
+
throw Error(
|
|
32
|
+
`[GUI] image() second parameter must be an object. Received: ${typeof params}.`,
|
|
33
|
+
);
|
|
21
34
|
}
|
|
22
35
|
let filename = path.replace(/^.*[\\\/]/, '');
|
|
23
36
|
let label;
|
|
@@ -40,17 +53,19 @@ export default class Image {
|
|
|
40
53
|
// width & height options
|
|
41
54
|
let inline_styles = '';
|
|
42
55
|
if (params.width) {
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
let width = params.width;
|
|
57
|
+
if (typeof width === 'number') {
|
|
58
|
+
width = `${width}px`;
|
|
45
59
|
}
|
|
46
|
-
inline_styles += `flex: 0 0 calc(${
|
|
60
|
+
inline_styles += `flex: 0 0 calc(${width} - 5px); `;
|
|
47
61
|
}
|
|
48
62
|
|
|
49
63
|
if (params.height) {
|
|
50
|
-
|
|
51
|
-
|
|
64
|
+
let height = params.height;
|
|
65
|
+
if (typeof height == 'number') {
|
|
66
|
+
height = `${height}px`;
|
|
52
67
|
}
|
|
53
|
-
inline_styles += `height: ${
|
|
68
|
+
inline_styles += `height: ${height}; `;
|
|
54
69
|
}
|
|
55
70
|
|
|
56
71
|
// Image button
|
|
@@ -60,7 +75,7 @@ export default class Image {
|
|
|
60
75
|
if (tooltip) {
|
|
61
76
|
image.setAttribute('title', tooltip);
|
|
62
77
|
}
|
|
63
|
-
this.parent.imageContainer
|
|
78
|
+
this.parent.imageContainer!.append(image);
|
|
64
79
|
|
|
65
80
|
// Expose the DOM element
|
|
66
81
|
this.element = image;
|
|
@@ -76,9 +91,9 @@ export default class Image {
|
|
|
76
91
|
image.append(text);
|
|
77
92
|
|
|
78
93
|
image.addEventListener('click', () => {
|
|
79
|
-
let selected_items = image.parentElement
|
|
94
|
+
let selected_items = image.parentElement?.querySelectorAll(
|
|
80
95
|
'.p-gui__image--selected',
|
|
81
|
-
);
|
|
96
|
+
) || [];
|
|
82
97
|
for (let i = 0; i < selected_items.length; i++) {
|
|
83
98
|
selected_items[i].classList.remove('p-gui__image--selected');
|
|
84
99
|
}
|
|
@@ -99,7 +114,7 @@ export default class Image {
|
|
|
99
114
|
});
|
|
100
115
|
}
|
|
101
116
|
|
|
102
|
-
onClick(callback) {
|
|
117
|
+
onClick(callback: Callback) {
|
|
103
118
|
this.callback = callback;
|
|
104
119
|
return this;
|
|
105
120
|
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type GUI from '../index.js';
|
|
2
|
+
|
|
3
|
+
type ValueObjectItem = { label: string; value: string | number };
|
|
4
|
+
export type Values = (string | number)[] | ValueObjectItem[];
|
|
5
|
+
|
|
6
|
+
export type Options = {
|
|
7
|
+
label?: string;
|
|
8
|
+
tooltip?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type Callback = ((value: string | number | ValueObjectItem, index: number) => void) | null;
|
|
12
|
+
|
|
13
|
+
export default class List {
|
|
14
|
+
private callback: Callback;
|
|
15
|
+
|
|
16
|
+
public element: HTMLElement;
|
|
17
|
+
|
|
18
|
+
constructor(private parent: GUI, obj: any, prop: string, valuesArg: Values, options: Options = {}) {
|
|
19
|
+
this.callback = null;
|
|
20
|
+
|
|
21
|
+
if (!obj || typeof obj !== 'object' || typeof prop !== 'string') {
|
|
22
|
+
throw Error(`[GUI] list() invalid parameters.`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let label = typeof options.label === 'string' ? options.label : prop;
|
|
26
|
+
let values = Array.isArray(valuesArg) ? valuesArg : null;
|
|
27
|
+
if (!values) {
|
|
28
|
+
throw Error(`[GUI] list() Third argument must be an array.`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let valuesIsObject =
|
|
32
|
+
values && values.length > 0 && typeof values[0] === 'object';
|
|
33
|
+
const tooltip =
|
|
34
|
+
typeof options.tooltip === 'string'
|
|
35
|
+
? options.tooltip
|
|
36
|
+
: options.tooltip === true
|
|
37
|
+
? label
|
|
38
|
+
: null;
|
|
39
|
+
|
|
40
|
+
let value = (() => {
|
|
41
|
+
if (!values) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (typeof obj[prop] === 'string') {
|
|
45
|
+
if (!valuesIsObject) {
|
|
46
|
+
// values is an array of strings
|
|
47
|
+
return (values as (string | number)[]).indexOf(obj[prop]);
|
|
48
|
+
} else {
|
|
49
|
+
// values is an array of objects
|
|
50
|
+
return (values as ValueObjectItem[]).find((item) => item.value === obj[prop])
|
|
51
|
+
?.value;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (typeof obj[prop] == 'number') {
|
|
55
|
+
if (!valuesIsObject) {
|
|
56
|
+
// values is an array of strings
|
|
57
|
+
return obj[prop];
|
|
58
|
+
} else {
|
|
59
|
+
// values is an array of objects
|
|
60
|
+
return (values as ValueObjectItem[]).find((item) => item.value === obj[prop])
|
|
61
|
+
?.value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
})();
|
|
65
|
+
|
|
66
|
+
const propReferenceIndex = this.parent.propReferences.push(obj[prop]) - 1;
|
|
67
|
+
|
|
68
|
+
let container = document.createElement('div');
|
|
69
|
+
container.className = 'p-gui__list';
|
|
70
|
+
container.textContent = label;
|
|
71
|
+
if (tooltip) {
|
|
72
|
+
container.setAttribute('title', tooltip);
|
|
73
|
+
}
|
|
74
|
+
this.parent.wrapper.append(container);
|
|
75
|
+
|
|
76
|
+
// Expose the DOM element
|
|
77
|
+
this.element = container;
|
|
78
|
+
|
|
79
|
+
let select = document.createElement('select');
|
|
80
|
+
container.append(select);
|
|
81
|
+
select.className = 'p-gui__list-dropdown';
|
|
82
|
+
select.addEventListener('change', (ev) => {
|
|
83
|
+
obj[prop] = (ev.target as HTMLSelectElement).value;
|
|
84
|
+
|
|
85
|
+
if (this.parent.onUpdate) {
|
|
86
|
+
this.parent.onUpdate();
|
|
87
|
+
} else if (
|
|
88
|
+
this.parent.isFolder &&
|
|
89
|
+
this.parent.firstParent.onUpdate
|
|
90
|
+
) {
|
|
91
|
+
this.parent.firstParent.onUpdate();
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (values) {
|
|
96
|
+
values.forEach((item, index) => {
|
|
97
|
+
const optionName = valuesIsObject ? (item as ValueObjectItem).label : item;
|
|
98
|
+
const optionValue = valuesIsObject ? (item as ValueObjectItem).value : item;
|
|
99
|
+
let option = document.createElement('option');
|
|
100
|
+
option.setAttribute('value', String(optionValue));
|
|
101
|
+
option.textContent = String(optionName);
|
|
102
|
+
select.append(option);
|
|
103
|
+
|
|
104
|
+
if (
|
|
105
|
+
(!valuesIsObject && value == index) ||
|
|
106
|
+
(valuesIsObject && value == optionValue)
|
|
107
|
+
) {
|
|
108
|
+
option.setAttribute('selected', '');
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
Object.defineProperty(obj, prop, {
|
|
114
|
+
set: (val) => {
|
|
115
|
+
let newIndex, newValue, newObj;
|
|
116
|
+
if (valuesIsObject) {
|
|
117
|
+
newObj = values?.find((item) => {
|
|
118
|
+
return (item as ValueObjectItem).value == val;
|
|
119
|
+
});
|
|
120
|
+
if (!newObj) {
|
|
121
|
+
console.error(`[GUI] list() value ${val} not found in values`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
newValue = (newObj as ValueObjectItem)?.value || (values[0] as ValueObjectItem).value;
|
|
125
|
+
newIndex = (values as ValueObjectItem[]).indexOf(newObj as ValueObjectItem);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
if (typeof val == 'string') {
|
|
129
|
+
newIndex = (values as (string | number)[]).indexOf(val);
|
|
130
|
+
newValue = val;
|
|
131
|
+
}
|
|
132
|
+
if (typeof val == 'number') {
|
|
133
|
+
newIndex = val;
|
|
134
|
+
newValue = values[val];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (newIndex === undefined || newValue === undefined) {
|
|
139
|
+
console.error('[GUI] list() newIndex or newValue is undefined');
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.parent.propReferences[propReferenceIndex] =
|
|
144
|
+
valuesIsObject ? newValue : val;
|
|
145
|
+
|
|
146
|
+
const previousSelection =
|
|
147
|
+
select.querySelector('[selected]');
|
|
148
|
+
if (previousSelection) {
|
|
149
|
+
previousSelection.removeAttribute('selected');
|
|
150
|
+
}
|
|
151
|
+
select
|
|
152
|
+
.querySelectorAll('option')
|
|
153
|
+
[newIndex].setAttribute('selected', '');
|
|
154
|
+
|
|
155
|
+
if (typeof this.callback == 'function') {
|
|
156
|
+
if (valuesIsObject && newObj) {
|
|
157
|
+
this.callback(newObj, newIndex);
|
|
158
|
+
} else {
|
|
159
|
+
this.callback(newValue, newIndex);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
get: () => {
|
|
164
|
+
return this.parent.propReferences[propReferenceIndex];
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
onChange(callback: Callback) {
|
|
170
|
+
this.callback = callback;
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
173
|
+
}
|