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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "perfect-gui",
3
3
  "type": "module",
4
- "version": "5.1.2",
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
- "vite": "^7.3.1"
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
- constructor(parent, params = {}) {
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 params !== 'object') {
20
+ if (typeof options !== 'object') {
7
21
  throw Error(
8
- `[GUI] button() first parameter must be an object. Received: ${typeof params}.`,
22
+ `[GUI] button() first parameter must be an object. Received: ${typeof options}.`,
9
23
  );
10
24
  }
11
25
 
12
- let label = params.label || ' ';
26
+ let label = options.label || ' ';
13
27
 
14
28
  const tooltip =
15
- typeof params.tooltip === 'string'
16
- ? params.tooltip
17
- : params.tooltip === true
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 params.color == 'string') {
43
- el.style.setProperty('--color-accent', params.color);
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
- params.hoverColor || params.color,
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
- constructor(parent, params = {}) {
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 (typeof params != 'object') {
7
- throw Error(
8
- `[GUI] image() first parameter must be an object. Received: ${typeof params}.`,
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
- let path;
13
- if (typeof params.path == 'string') {
14
- path = params.path;
15
- } else {
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
- if (typeof params.width == 'number') {
44
- params.width += 'px';
56
+ let width = params.width;
57
+ if (typeof width === 'number') {
58
+ width = `${width}px`;
45
59
  }
46
- inline_styles += `flex: 0 0 calc(${params.width} - 5px); `;
60
+ inline_styles += `flex: 0 0 calc(${width} - 5px); `;
47
61
  }
48
62
 
49
63
  if (params.height) {
50
- if (typeof params.height == 'number') {
51
- params.height += 'px';
64
+ let height = params.height;
65
+ if (typeof height == 'number') {
66
+ height = `${height}px`;
52
67
  }
53
- inline_styles += `height: ${params.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.append(image);
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.querySelectorAll(
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
+ }