muigui 0.0.12 → 0.0.13
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/README.md +349 -42
- package/package.json +2 -2
- package/src/controllers/Button.js +3 -0
- package/src/controllers/Canvas.js +2 -2
- package/src/controllers/ColorChooser.js +12 -11
- package/src/controllers/Folder.js +1 -0
- package/src/controllers/PopDownController.js +4 -1
- package/src/controllers/Text.js +1 -1
- package/src/controllers/TextNumber.js +1 -1
- package/src/controllers/create-controller.js +3 -0
- package/src/esm.ts +8 -0
- package/src/libs/color-utils.js +44 -0
- package/src/libs/graph.js +42 -0
- package/src/libs/monitor.js +5 -0
- package/src/muigui.js +16 -3
- package/src/styles/muigui.css.js +120 -28
- package/src/views/NumberView.js +10 -3
- package/src/views/TextView.js +6 -2
package/README.md
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
# muigui
|
|
2
|
-
|
|
3
|
-
# NOT READY for USE
|
|
4
|
-
|
|
5
|
-
<!---
|
|
6
|
-
|
|
7
|
-
<img src="./images/muigui.png" style="max-width: 640px">
|
|
1
|
+
# muigui (⍺)
|
|
8
2
|
|
|
9
3
|
A simple Web UI library.
|
|
10
4
|
|
|
5
|
+
[See docs here](https://muigui.org)
|
|
6
|
+
|
|
11
7
|
muigui is a simple UI library in the spirit of
|
|
12
|
-
[dat.gui](https://github.com/dataarts/dat.gui) and/or [lil-gui](https://github.com/georgealways/).
|
|
8
|
+
[dat.gui](https://github.com/dataarts/dat.gui) and/or [lil-gui](https://github.com/georgealways/) and [tweakpane](https://cocopon.github.io/tweakpane/)
|
|
13
9
|
|
|
14
10
|
## Usage
|
|
15
11
|
|
|
@@ -28,66 +24,377 @@ Then
|
|
|
28
24
|
```js
|
|
29
25
|
const s = {
|
|
30
26
|
someNumber: 123,
|
|
31
|
-
someString:
|
|
32
|
-
someOption:
|
|
27
|
+
someString: 'hello',
|
|
28
|
+
someOption: 'dog',
|
|
33
29
|
someColor: '#ED3281',
|
|
34
30
|
someFunction: () => console.log('called')
|
|
35
31
|
};
|
|
36
32
|
|
|
37
33
|
const gui = new GUI();
|
|
38
34
|
gui.add(s, 'someNumber', 0, 200); // range 0 to 200
|
|
39
|
-
gui.add(s, 'someString);
|
|
40
|
-
gui.add(s, 'someOption, ['cat', 'bird', 'dog']);
|
|
35
|
+
gui.add(s, 'someString');
|
|
36
|
+
gui.add(s, 'someOption', ['cat', 'bird', 'dog']);
|
|
41
37
|
gui.addColor(s, 'someColor');
|
|
42
38
|
gui.add(s, 'someFunction');
|
|
43
39
|
```
|
|
44
40
|
|
|
45
41
|
produces
|
|
46
42
|
|
|
47
|
-
<img src="./images/muigui-screenshot.png" style="max-width:
|
|
43
|
+
<img src="./images/muigui-screenshot.png" style="max-width: 250px">
|
|
48
44
|
|
|
49
|
-
|
|
45
|
+
## Why
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
So, to be honest, I like [tweakpane](https://cocopon.github.io/tweakpane/) and
|
|
48
|
+
I didn't know about it when I started this library. That said, I've looked
|
|
49
|
+
into using tweakpane and it doesn't meet my needs as of v4.0.0. Examples below
|
|
50
|
+
|
|
51
|
+
* ## Simpler
|
|
52
|
+
|
|
53
|
+
I wanted certain things to be simpler. For example, in dat.gui/lil.gui/tweakpane, if I wanted
|
|
54
|
+
to store radians in code but show degrees in the UI I had to jump through
|
|
55
|
+
hoops. I could either, store degrees and convert 😢
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
const settings = {
|
|
59
|
+
angle: 45,
|
|
60
|
+
};
|
|
61
|
+
gui.add(settings, 'angle', -360, 360);
|
|
62
|
+
|
|
63
|
+
...
|
|
64
|
+
const rotation = settings.angle * Math.PI / 180
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
That's bad IMO. I shouldn't have to refactor my code to fit the GUI.
|
|
68
|
+
|
|
69
|
+
I can make some proxy class that presents degrees to the GUI
|
|
70
|
+
and stores them in radians like this
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
class DegRadHelper {
|
|
74
|
+
constructor( obj, prop ) {
|
|
75
|
+
this.obj = obj;
|
|
76
|
+
this.prop = prop;
|
|
77
|
+
}
|
|
78
|
+
get value() {
|
|
79
|
+
return this.obj[this.prop] * 180 / Math.PI;
|
|
80
|
+
}
|
|
81
|
+
set value(v) {
|
|
82
|
+
this.obj[this.prop] = v * Math.PI / 180;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const settings = {
|
|
86
|
+
angle: Math.PI * 0.5,
|
|
87
|
+
};
|
|
88
|
+
gui.add(new DegRadHelper(settings, 'angle'), 'value', -360, 360);
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
But that looks poor to use. What is `'value'`?? 😭
|
|
92
|
+
|
|
93
|
+
So, muigui handles that slightly nicer in the form of converters.
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
const settings = {
|
|
97
|
+
angle: Math.PI * 0.5,
|
|
98
|
+
};
|
|
99
|
+
const degToRad = d => d * Math.PI / 180;
|
|
100
|
+
const radToDeg = r => r * 180 / Math.PI;
|
|
101
|
+
gui.add(s, 'angleRad', {
|
|
102
|
+
min: -360, max: 360,
|
|
103
|
+
converters: {
|
|
104
|
+
to: radToDeg,
|
|
105
|
+
from: v => [true, degToRad(v)],
|
|
106
|
+
}});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Typically I'll pull out the settings like this
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
const degToRad = d => d * Math.PI / 180;
|
|
113
|
+
const radToDeg = r => r * 180 / Math.PI;
|
|
114
|
+
const radToDegSettings {
|
|
115
|
+
min: -360, max: 360,
|
|
116
|
+
converters: {
|
|
117
|
+
to: radToDeg,
|
|
118
|
+
from: v => [true, degToRad(v)],
|
|
119
|
+
}});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
And then I can use it like this
|
|
123
|
+
|
|
124
|
+
```js
|
|
125
|
+
const settings = {
|
|
126
|
+
angle: Math.PI * 0.5,
|
|
127
|
+
rotation: Math.PI * 0.25,
|
|
128
|
+
};
|
|
129
|
+
gui.add(s, 'angleRad', radToDegSettings);
|
|
130
|
+
gui.add(s, 'rotation', radToDegSettings);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
You provide converters where `to` converts to the form the UI wants
|
|
134
|
+
and `from` converts back. The reason `from` returns a tuple is that
|
|
135
|
+
it's used to convert the text and gives you a chance to say the text
|
|
136
|
+
does not match the required format in which case you return `[false]`
|
|
137
|
+
|
|
138
|
+
Other examples of simpler: Want a drop-down for numbers?
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
const settings = { speed: 1 }
|
|
142
|
+
|
|
143
|
+
// tweakpane
|
|
144
|
+
pane.addBinding(settings, speed, {
|
|
145
|
+
options: {
|
|
146
|
+
slow: 0,
|
|
147
|
+
medium: 1,
|
|
148
|
+
fast: 2,
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
// muigui
|
|
153
|
+
gui.add(settings, 'speed', ['slow', 'medium', 'fast']);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Want a drop-down for strings
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
const settings = { alphaMode: 'opaque' }
|
|
160
|
+
|
|
161
|
+
// tweakpane
|
|
162
|
+
pane.addBindng(settings, 'alphaMode', {
|
|
163
|
+
options: {
|
|
164
|
+
'opaque': 'opaque',
|
|
165
|
+
'premultiplied': 'premultiplied',
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// muigui
|
|
170
|
+
gui.add(settings, 'alphaMode', ['opaque', 'premultiplied']);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Of course you can also pass in key/value settings like tweakpane.
|
|
174
|
+
|
|
175
|
+
* ## Color formats and storage
|
|
176
|
+
|
|
177
|
+
I often work with WebGL and WebGPU. The most common colors are in an array or a typedArray
|
|
178
|
+
|
|
179
|
+
```js
|
|
180
|
+
const uniforms = {
|
|
181
|
+
color1: [1, 0.5, 0.25], // orange
|
|
182
|
+
color2: new Float32Array([0, 1, 1]); // cyan
|
|
183
|
+
color3: new Uint8Array([0, 128, 0, 128]); // transparent green
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Neither dat.gui, lil.gui, nor tweakpane can edit these AFAICT. (2023)
|
|
188
|
+
You'd have to jump
|
|
189
|
+
through the hoops like the `DegRadHelper` example above but it's not
|
|
190
|
+
that easy because, if you're showing the value textually
|
|
191
|
+
then you want that value to be the one you want to show the user,
|
|
192
|
+
not the value the editor wants.
|
|
193
|
+
|
|
194
|
+
This is still an issue in muigui where it uses the browser's built
|
|
195
|
+
in color editor in parts. That editor might show 0-255 values but if it's
|
|
196
|
+
editing 0 to 1 values it'd be nice if the editor showed 0 to 1 values.
|
|
197
|
+
|
|
198
|
+
muigui does handle this in the text part of it's color display. If you
|
|
199
|
+
ask it to edit 0 to 1 values it shows 0 to 1 values in the text part.
|
|
200
|
+
The reason you need the text part is so you can copy and paste colors
|
|
59
201
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
202
|
+
muigui also edits hsl colors. By that I don't mean the editor can
|
|
203
|
+
switch to an HSL editor, I mean the actual value that comes out
|
|
204
|
+
is `hsl(hue, sat, luminance)` and not some RGB value. Like the number
|
|
205
|
+
conversions, it would be easy to add `hsv`, `hsb`, maybe `labch` etc...
|
|
206
|
+
|
|
207
|
+
* ## More Use Cases
|
|
208
|
+
|
|
209
|
+
In some projects, I'd end up writing a small
|
|
210
|
+
app with an HTML form and then have to write all the code to parse
|
|
211
|
+
the form and I'd be thinking "It would be so nice if I could use
|
|
212
|
+
the same API as dat.gui 🙁
|
|
213
|
+
|
|
214
|
+
So, I thought I'd try to write a library that handled that case.
|
|
215
|
+
|
|
216
|
+
I also wanted to explore various things though many of them
|
|
217
|
+
have not made it into muigui yet.
|
|
218
|
+
|
|
219
|
+
* ## PropertyGrid
|
|
220
|
+
|
|
221
|
+
I'm sure the first app to do this was something from the 60s or 70s
|
|
222
|
+
in Smalltalk or something but my first experience was C#. In C#
|
|
223
|
+
the UI library had a `PropertyGrid` which you could pass any class
|
|
224
|
+
and with would auto-magically make UI to edit the public fields
|
|
225
|
+
of that class. If you've ever used Unity, it's the same. You declare
|
|
226
|
+
a class and it immediately shows a UI for all of its public properties.
|
|
227
|
+
|
|
228
|
+
That's easier in a typed language than a more loose language like
|
|
229
|
+
JavaScript.
|
|
230
|
+
|
|
231
|
+
I'm still experimenting with ideas but it sure would be nice to
|
|
232
|
+
get that for JS. Just give it an object and get a UI. You can
|
|
233
|
+
then customize later.
|
|
234
|
+
|
|
235
|
+
To be more concrete. Here's some code to setup a GUI
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
const s = {
|
|
239
|
+
someNumber: 123,
|
|
240
|
+
someString: 'hello',
|
|
241
|
+
someOption: 'dog',
|
|
242
|
+
someColor: '#ED3281',
|
|
243
|
+
someFunction: () => console.log('called')
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const gui = new GUI();
|
|
247
|
+
gui.add(s, 'someNumber', 0, 200); // range 0 to 200
|
|
248
|
+
gui.add(s, 'someString');
|
|
249
|
+
gui.add(s, 'someOption', ['cat', 'bird', 'dog']);
|
|
250
|
+
gui.addColor(s, 'someColor');
|
|
251
|
+
gui.add(s, 'someFunction');
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
I'd really like it to be this
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
const s = {
|
|
258
|
+
someNumber: 123,
|
|
259
|
+
someString: 'hello',
|
|
260
|
+
someOption: 'dog',
|
|
261
|
+
someColor: '#ED3281',
|
|
262
|
+
someFunction: () => console.log('called')
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const gui = new GUI();
|
|
266
|
+
gui.add(s);
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
At the moment that won't work. `someNumber` could only become
|
|
270
|
+
a `TextNumber` because there's no range. `someOption` would
|
|
271
|
+
only become a `Text` because there's no info that it's an enum.
|
|
272
|
+
`someColor` would become a `Text` because there's no info that
|
|
273
|
+
it's a color. So in the end only 2 of the 5 would work without
|
|
274
|
+
having to provide more info.
|
|
275
|
+
|
|
276
|
+
It's not clear in JS that adding that info would be a win
|
|
277
|
+
for keeping it simple but it sure would be nice.
|
|
278
|
+
|
|
279
|
+
* ## Modularity
|
|
280
|
+
|
|
281
|
+
Ideally I'd like it to be easy to make UIs based on collections
|
|
282
|
+
of parts. A simple example might be a 3 component vector
|
|
283
|
+
editor that is the combination of 3 number editors.
|
|
284
|
+
|
|
285
|
+
I'm still experimenting. While muigui has components that
|
|
286
|
+
do this I'm not happy with the API ergonomics yet.
|
|
287
|
+
|
|
288
|
+
Similarly I'd like to more easily split layout so it's trivial
|
|
289
|
+
to layout sub components. Again, still experimenting.
|
|
290
|
+
|
|
291
|
+
* ## Don't over specialize
|
|
292
|
+
|
|
293
|
+
This might be ranty, but I find libraries that try to do too much,
|
|
294
|
+
frustrating. In this case, it would
|
|
295
|
+
be a library that graphs data for you. The problem
|
|
296
|
+
with this functionality is that there is no end to
|
|
297
|
+
the number of features that will be requested.
|
|
298
|
+
|
|
299
|
+
You start with "graph an array of numbers".
|
|
300
|
+
Then you'll be asked to be able to supply a range.
|
|
301
|
+
Then you'll be asked to allow more than one array
|
|
302
|
+
for the graph.
|
|
303
|
+
You'll next be asked to let you specify a different
|
|
304
|
+
color for each array. Next you'll be asked to draw
|
|
305
|
+
axes, in different colors, with different units,
|
|
306
|
+
and labels. Then you'll be asked to have an option
|
|
307
|
+
to fill under the graph. Etc, etc, etc... forever.
|
|
308
|
+
|
|
309
|
+
In this case, It's arguably better to provide
|
|
310
|
+
a canvas and let the developer write their
|
|
311
|
+
own graphing code. Maybe provide an example or
|
|
312
|
+
a simple helper for the simplest case.
|
|
313
|
+
|
|
314
|
+
They can even choose when to update vs having to
|
|
315
|
+
choose an interval.
|
|
316
|
+
|
|
317
|
+
Let's compare
|
|
318
|
+
|
|
319
|
+
```js
|
|
320
|
+
// tweakpane
|
|
321
|
+
const pane = new Pane();
|
|
322
|
+
pane.addBinding(PARAMS, 'wave', {
|
|
323
|
+
readonly: true,
|
|
324
|
+
view: 'graph',
|
|
325
|
+
min: -1,
|
|
326
|
+
max: +1,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// muigui
|
|
330
|
+
const gui = new GUI();
|
|
331
|
+
helpers.graph(gui.addCanvas('wave'), waveData, {
|
|
332
|
+
min: -1,
|
|
333
|
+
max: +1,
|
|
334
|
+
});
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
It wasn't any harder to use, but the fact that we
|
|
338
|
+
just returned a canvas and left the rest outside
|
|
339
|
+
the library made it way more flexible.
|
|
340
|
+
|
|
341
|
+
This problem of providing too specialized a solution
|
|
342
|
+
is endemic throughout the library ecosystem of pretty much
|
|
343
|
+
every language.
|
|
344
|
+
|
|
345
|
+
There's a balance, but in general, if you need
|
|
346
|
+
to add more and more options then it was probably the
|
|
347
|
+
wrong solution. It's better to provide the
|
|
348
|
+
building blocks.
|
|
349
|
+
|
|
350
|
+
## No Save/Restore
|
|
351
|
+
|
|
352
|
+
The problem with save/restore in lil.gui etc is it assumes the data
|
|
353
|
+
I want to edit can be serialized to JSON
|
|
354
|
+
|
|
355
|
+
Just as the simplest example I can think of
|
|
356
|
+
|
|
357
|
+
```js
|
|
358
|
+
const material = new THREE.MeshBasicMaterial();
|
|
64
359
|
|
|
65
360
|
const gui = new GUI();
|
|
66
|
-
gui.
|
|
361
|
+
gui.addColor(material, 'color');
|
|
67
362
|
```
|
|
68
363
|
|
|
69
|
-
|
|
364
|
+
It makes no sense to save/restore here. I'm editing a three.js material.
|
|
365
|
+
If I wanted to serialize anything I'd serialize the material.
|
|
70
366
|
|
|
71
|
-
|
|
72
|
-
Rather, it is a small, easy to use library for small apps.
|
|
73
|
-
Basically I liked how simple it was to use dat.gui to add
|
|
74
|
-
a few sliders and options to a demo.
|
|
367
|
+
Otherwise I can just save the stuff I passed to the GUI.
|
|
75
368
|
|
|
76
|
-
|
|
77
|
-
|
|
369
|
+
```js
|
|
370
|
+
const s = {
|
|
371
|
+
someNumber: 123,
|
|
372
|
+
someString: 'hello',
|
|
373
|
+
someOption: 'dog',
|
|
374
|
+
someColor: '#ED3281',
|
|
375
|
+
};
|
|
78
376
|
|
|
79
|
-
|
|
377
|
+
// save
|
|
378
|
+
const str = JSON.stringify(s);
|
|
80
379
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
380
|
+
// restore
|
|
381
|
+
Object.assign(s, JSON.parse(str));
|
|
382
|
+
gui.updateDisplay();
|
|
383
|
+
```
|
|
85
384
|
|
|
86
|
-
|
|
385
|
+
In other words, the serialization is too specialized. It's trivial
|
|
386
|
+
to call `JSON.stringify` on data that serializable. No need to put
|
|
387
|
+
serialization in the GUI. Note: I get that you might want to save
|
|
388
|
+
some hidden gui state like whether or not a folder is expanded.
|
|
389
|
+
You still run into the issue though that the data being edited
|
|
390
|
+
might not be easily serializable so you'd have to find another solution.
|
|
87
391
|
|
|
88
|
-
|
|
392
|
+
## Future
|
|
89
393
|
|
|
90
|
-
|
|
394
|
+
I'm under sure how much time I'll continue to put into this.
|
|
395
|
+
I get the feeling other people are far more motivated to make
|
|
396
|
+
UIs. Maybe if I'm lucky they'll take some inspiration from
|
|
397
|
+
the thoughts above and I'll find they've covered it all.
|
|
91
398
|
|
|
92
399
|
## License
|
|
93
400
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muigui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "A Simple GUI",
|
|
5
5
|
"main": "muigui.js",
|
|
6
6
|
"module": "src/muigui.js",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
|
46
46
|
"@typescript-eslint/parser": "^6.12.0",
|
|
47
47
|
"chokidar": "^3.5.3",
|
|
48
|
-
"eslint": "^8.
|
|
48
|
+
"eslint": "^8.54.0",
|
|
49
49
|
"eslint-plugin-html": "^7.1.0",
|
|
50
50
|
"eslint-plugin-one-variable-per-var": "^0.0.3",
|
|
51
51
|
"eslint-plugin-optional-comma-spacing": "0.0.4",
|
|
@@ -26,6 +26,9 @@ export default class Button extends Controller {
|
|
|
26
26
|
}));
|
|
27
27
|
this.setOptions({name: property, ...options});
|
|
28
28
|
}
|
|
29
|
+
name(name) {
|
|
30
|
+
this.#buttonElem.textContent = name;
|
|
31
|
+
}
|
|
29
32
|
setOptions(options) {
|
|
30
33
|
copyExistingProperties(this.#options, options);
|
|
31
34
|
const {name} = this.#options;
|
|
@@ -5,8 +5,8 @@ import LabelController from './LabelController.js';
|
|
|
5
5
|
export default class Canvas extends LabelController {
|
|
6
6
|
#canvasElem;
|
|
7
7
|
|
|
8
|
-
constructor() {
|
|
9
|
-
super('muigui-canvas');
|
|
8
|
+
constructor(name) {
|
|
9
|
+
super('muigui-canvas', name);
|
|
10
10
|
this.#canvasElem = this.add(
|
|
11
11
|
new ElementView('canvas', 'muigui-canvas'),
|
|
12
12
|
).domElement;
|
|
@@ -16,7 +16,6 @@ export default class ColorChooser extends PopDownController {
|
|
|
16
16
|
#colorView;
|
|
17
17
|
#textView;
|
|
18
18
|
#to;
|
|
19
|
-
#setKnobHelper;
|
|
20
19
|
|
|
21
20
|
constructor(object, property, options = {}) {
|
|
22
21
|
super(object, property, 'muigui-color-chooser');
|
|
@@ -28,20 +27,22 @@ export default class ColorChooser extends PopDownController {
|
|
|
28
27
|
this.addTop(this.#textView);
|
|
29
28
|
this.addBottom(this.#colorView);
|
|
30
29
|
// WTF! FIX!
|
|
31
|
-
this
|
|
32
|
-
if (this.#to) {
|
|
33
|
-
const hex6Or8 = this.#to(this.getValue());
|
|
34
|
-
const hsl = rgbUint8ToHsl(hexToUint8RGB(hex6Or8));
|
|
35
|
-
hsl[2] = (hsl[2] + 50) % 100;
|
|
36
|
-
const hex = uint8RGBToHex(hslToRgbUint8(hsl));
|
|
37
|
-
this.setKnobColor(`${hex6Or8.substring(0, 7)}FF`, hex);
|
|
38
|
-
}
|
|
39
|
-
};
|
|
30
|
+
this.___setKnobHelper = true;
|
|
40
31
|
this.updateDisplay();
|
|
41
32
|
}
|
|
33
|
+
#setKnobHelper() {
|
|
34
|
+
if (this.#to) {
|
|
35
|
+
const hex6Or8 = this.#to(this.getValue());
|
|
36
|
+
const alpha = hex6Or8.length === 9 ? hex6Or8.substring(7, 9) : 'FF';
|
|
37
|
+
const hsl = rgbUint8ToHsl(hexToUint8RGB(hex6Or8));
|
|
38
|
+
hsl[2] = (hsl[2] + 50) % 100;
|
|
39
|
+
const hex = uint8RGBToHex(hslToRgbUint8(hsl));
|
|
40
|
+
this.setKnobColor(`${hex6Or8.substring(0, 7)}${alpha}`, hex);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
42
43
|
updateDisplay() {
|
|
43
44
|
super.updateDisplay();
|
|
44
|
-
if (this
|
|
45
|
+
if (this.___setKnobHelper) {
|
|
45
46
|
this.#setKnobHelper();
|
|
46
47
|
}
|
|
47
48
|
}
|
|
@@ -11,6 +11,7 @@ export default class Folder extends Container {
|
|
|
11
11
|
type: 'button',
|
|
12
12
|
onClick: () => this.toggleOpen(),
|
|
13
13
|
}, [this.#labelElem]));
|
|
14
|
+
this.pushContainer(new Container('muigui-open-container'));
|
|
14
15
|
this.pushContainer(new Container());
|
|
15
16
|
this.name(name);
|
|
16
17
|
this.open();
|
|
@@ -54,7 +54,10 @@ export default class PopDownController extends ValueController {
|
|
|
54
54
|
}));
|
|
55
55
|
this.#checkboxElem = checkboxElem;
|
|
56
56
|
this.#valuesView = this.#top.add(new ElementView('div', 'muigui-pop-down-values'));
|
|
57
|
-
|
|
57
|
+
const container = new ElementView('div', 'muigui-pop-down-bottom muigui-open-container');
|
|
58
|
+
this.#bottom = new ElementView('div');
|
|
59
|
+
container.add(this.#bottom);
|
|
60
|
+
this.add(container);
|
|
58
61
|
this.setOptions(options);
|
|
59
62
|
}
|
|
60
63
|
setKnobColor(bgCssColor/*, fgCssColor*/) {
|
package/src/controllers/Text.js
CHANGED
|
@@ -3,7 +3,7 @@ import ValueController from './ValueController.js';
|
|
|
3
3
|
|
|
4
4
|
export default class Text extends ValueController {
|
|
5
5
|
constructor(object, property) {
|
|
6
|
-
super(object, property, 'muigui-
|
|
6
|
+
super(object, property, 'muigui-text');
|
|
7
7
|
this.add(new TextView(this));
|
|
8
8
|
this.updateDisplay();
|
|
9
9
|
}
|
|
@@ -11,7 +11,7 @@ export default class TextNumber extends ValueController {
|
|
|
11
11
|
#step;
|
|
12
12
|
|
|
13
13
|
constructor(object, property, options = {}) {
|
|
14
|
-
super(object, property, 'muigui-
|
|
14
|
+
super(object, property, 'muigui-text-number');
|
|
15
15
|
this.#textView = this.add(new NumberView(this, options));
|
|
16
16
|
this.updateDisplay();
|
|
17
17
|
}
|
|
@@ -24,6 +24,9 @@ export function createController(object, property, ...args) {
|
|
|
24
24
|
if (Array.isArray(arg1)) {
|
|
25
25
|
return new Select(object, property, {keyValues: arg1});
|
|
26
26
|
}
|
|
27
|
+
if (arg1 && arg1.keyValues) {
|
|
28
|
+
return new Select(object, property, {keyValues: arg1.keyValues});
|
|
29
|
+
}
|
|
27
30
|
|
|
28
31
|
const t = typeof object[property];
|
|
29
32
|
switch (t) {
|
package/src/esm.ts
CHANGED
|
@@ -9,4 +9,12 @@ export { default as Slider } from './controllers/Slider.js';
|
|
|
9
9
|
export { default as TextNumber } from './controllers/TextNumber.js';
|
|
10
10
|
export { default as Vec2 } from './controllers/Vec2.js';
|
|
11
11
|
|
|
12
|
+
import {graph} from './libs/graph.js';
|
|
13
|
+
import {monitor} from './libs/monitor.js';
|
|
14
|
+
|
|
15
|
+
export const helpers = {
|
|
16
|
+
graph,
|
|
17
|
+
monitor,
|
|
18
|
+
};
|
|
19
|
+
|
|
12
20
|
export default GUI;
|
package/src/libs/color-utils.js
CHANGED
|
@@ -583,6 +583,50 @@ export const colorFormatConverters = {
|
|
|
583
583
|
to: v => Array.from(v).map(v => f3(v)).join(', '),
|
|
584
584
|
},
|
|
585
585
|
},
|
|
586
|
+
'float-hsv': {
|
|
587
|
+
color: {
|
|
588
|
+
from: v => [true, rgbFloatToHSV01(hexToFloatRGB(v))],
|
|
589
|
+
to: v => hsv01ToRGBFloat(floatRGBToHex(v)),
|
|
590
|
+
},
|
|
591
|
+
text: {
|
|
592
|
+
from: strTo3Floats,
|
|
593
|
+
// need Array.from because map of Float32Array makes a Float32Array
|
|
594
|
+
to: v => Array.from(v).map(v => f3(v)).join(', '),
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
'float-hsva': {
|
|
598
|
+
color: {
|
|
599
|
+
from: v => [true, rgbaFloatToHSVA01(hexToFloatRGB(v))],
|
|
600
|
+
to: v => hsva01ToRGBAFloat(floatRGBToHex(v)),
|
|
601
|
+
},
|
|
602
|
+
text: {
|
|
603
|
+
from: strTo4Floats,
|
|
604
|
+
// need Array.from because map of Float32Array makes a Float32Array
|
|
605
|
+
to: v => Array.from(v).map(v => f3(v)).join(', '),
|
|
606
|
+
},
|
|
607
|
+
},
|
|
608
|
+
//'float-hsl': {
|
|
609
|
+
// color: {
|
|
610
|
+
// from: v => [true, rgbFloatToHsl01(hexToFloatRGB(v))],
|
|
611
|
+
// to: v => hsl01ToRGBFloat(floatRGBToHex(v)),
|
|
612
|
+
// },
|
|
613
|
+
// text: {
|
|
614
|
+
// from: strTo3Floats,
|
|
615
|
+
// // need Array.from because map of Float32Array makes a Float32Array
|
|
616
|
+
// to: v => Array.from(v).map(v => f3(v)).join(', '),
|
|
617
|
+
// },
|
|
618
|
+
//},
|
|
619
|
+
//'float-hsla': {
|
|
620
|
+
// color: {
|
|
621
|
+
// from: v => [true, hexToFloatRGBA(v)],
|
|
622
|
+
// to: floatRGBAToHex,
|
|
623
|
+
// },
|
|
624
|
+
// text: {
|
|
625
|
+
// from: strTo4Floats,
|
|
626
|
+
// // need Array.from because map of Float32Array makes a Float32Array
|
|
627
|
+
// to: v => Array.from(v).map(v => f3(v)).join(', '),
|
|
628
|
+
// },
|
|
629
|
+
//},
|
|
586
630
|
'object-rgb': {
|
|
587
631
|
color: {
|
|
588
632
|
from: v => [true, hexToObjectRGB(v)],
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const darkColors = {
|
|
2
|
+
main: '#ddd',
|
|
3
|
+
};
|
|
4
|
+
const lightColors = {
|
|
5
|
+
main: '#333',
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const darkMatcher = window.matchMedia('(prefers-color-scheme: dark)');
|
|
9
|
+
|
|
10
|
+
let colors;
|
|
11
|
+
let isDarkMode;
|
|
12
|
+
|
|
13
|
+
function update() {
|
|
14
|
+
isDarkMode = darkMatcher.matches;
|
|
15
|
+
colors = isDarkMode ? darkColors : lightColors;
|
|
16
|
+
}
|
|
17
|
+
darkMatcher.addEventListener('change', update);
|
|
18
|
+
update();
|
|
19
|
+
|
|
20
|
+
export function graph(canvas, data, {
|
|
21
|
+
min = -1,
|
|
22
|
+
max = 1,
|
|
23
|
+
interval = 16,
|
|
24
|
+
color,
|
|
25
|
+
}) {
|
|
26
|
+
const ctx = canvas.getContext('2d');
|
|
27
|
+
|
|
28
|
+
function render() {
|
|
29
|
+
const {width, height} = canvas;
|
|
30
|
+
ctx.clearRect(0, 0, width, height);
|
|
31
|
+
ctx.beginPath();
|
|
32
|
+
const range = max - min;
|
|
33
|
+
for (let i = 0; i < data.length; ++i) {
|
|
34
|
+
const x = i * width / data.length;
|
|
35
|
+
const y = (data[i] - min) * height / range;
|
|
36
|
+
ctx.lineTo(x, y);
|
|
37
|
+
}
|
|
38
|
+
ctx.strokeStyle = color || colors.main;
|
|
39
|
+
ctx.stroke();
|
|
40
|
+
}
|
|
41
|
+
setInterval(render, interval);
|
|
42
|
+
}
|
package/src/muigui.js
CHANGED
|
@@ -61,6 +61,10 @@ export class GUIFolder extends Folder {
|
|
|
61
61
|
addLabel(text) {
|
|
62
62
|
return this.addController(new Label(text));
|
|
63
63
|
}
|
|
64
|
+
addButton(name, fn) {
|
|
65
|
+
const o = {fn};
|
|
66
|
+
return this.add(o, 'fn').name(name);
|
|
67
|
+
}
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
class MuiguiElement extends HTMLElement {
|
|
@@ -73,7 +77,7 @@ class MuiguiElement extends HTMLElement {
|
|
|
73
77
|
customElements.define('muigui-element', MuiguiElement);
|
|
74
78
|
|
|
75
79
|
const baseStyleSheet = new CSSStyleSheet();
|
|
76
|
-
baseStyleSheet.replaceSync(css.default);
|
|
80
|
+
//baseStyleSheet.replaceSync(css.default);
|
|
77
81
|
const userStyleSheet = new CSSStyleSheet();
|
|
78
82
|
|
|
79
83
|
function makeStyleSheetUpdater(styleSheet) {
|
|
@@ -100,6 +104,11 @@ function makeStyleSheetUpdater(styleSheet) {
|
|
|
100
104
|
const updateBaseStyle = makeStyleSheetUpdater(baseStyleSheet);
|
|
101
105
|
const updateUserStyle = makeStyleSheetUpdater(userStyleSheet);
|
|
102
106
|
|
|
107
|
+
function getTheme(name) {
|
|
108
|
+
const { include, css: cssStr } = css.themes[name];
|
|
109
|
+
return `${include.map(m => css[m]).join('\n')} : css.default}\n${cssStr || ''}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
103
112
|
export class GUI extends GUIFolder {
|
|
104
113
|
static converters = converters;
|
|
105
114
|
static mapRange = mapRange;
|
|
@@ -131,13 +140,14 @@ export class GUI extends GUIFolder {
|
|
|
131
140
|
}
|
|
132
141
|
if (parent) {
|
|
133
142
|
const muiguiElement = createElem('muigui-element');
|
|
134
|
-
muiguiElement.shadowRoot.adoptedStyleSheets = [baseStyleSheet, userStyleSheet
|
|
143
|
+
muiguiElement.shadowRoot.adoptedStyleSheets = [this.#localStyleSheet, baseStyleSheet, userStyleSheet];
|
|
135
144
|
muiguiElement.shadow.appendChild(this.domElement);
|
|
136
145
|
parent.appendChild(muiguiElement);
|
|
137
146
|
}
|
|
138
147
|
if (title) {
|
|
139
148
|
this.title(title);
|
|
140
149
|
}
|
|
150
|
+
this.#localStyleSheet.replaceSync(css.default);
|
|
141
151
|
this.domElement.classList.add('muigui', 'muigui-colors');
|
|
142
152
|
}
|
|
143
153
|
setStyle(css) {
|
|
@@ -155,8 +165,11 @@ export class GUI extends GUIFolder {
|
|
|
155
165
|
static getUserStyleSheet() {
|
|
156
166
|
return userStyleSheet;
|
|
157
167
|
}
|
|
168
|
+
setTheme(name) {
|
|
169
|
+
this.setStyle(getTheme(name));
|
|
170
|
+
}
|
|
158
171
|
static setTheme(name) {
|
|
159
|
-
GUI.setBaseStyles(
|
|
172
|
+
GUI.setBaseStyles(getTheme(name));
|
|
160
173
|
}
|
|
161
174
|
}
|
|
162
175
|
|
package/src/styles/muigui.css.js
CHANGED
|
@@ -10,16 +10,24 @@ export default {
|
|
|
10
10
|
--menu-bg-color: #f8f8f8;
|
|
11
11
|
--menu-sep-color: #bbb;
|
|
12
12
|
--hover-bg-color: #999;
|
|
13
|
-
--focus-color: #
|
|
14
|
-
--range-color: #
|
|
13
|
+
--focus-color: #8BF;
|
|
14
|
+
--range-color: #AAA;
|
|
15
15
|
--invalid-color: #FF0000;
|
|
16
16
|
--selected-color: rgb(255, 255, 255, 0.9);
|
|
17
17
|
|
|
18
18
|
--button-bg-color: var(--value-bg-color);
|
|
19
19
|
|
|
20
|
+
--image-open: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxyZWN0IHN0eWxlPSJmaWxsOiAjNDQ0OyIgeD0iMjAlIiB5PSI0NSUiIHdpZHRoPSI2MCUiIGhlaWdodD0iMTAlIj48L3JlY3Q+Cjwvc3ZnPg==);
|
|
21
|
+
--image-closed: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxyZWN0IHN0eWxlPSJmaWxsOiAjNDQ0OyIgeD0iNDUlIiB5PSIyMCUiIHdpZHRoPSIxMCUiIGhlaWdodD0iNjAlIj48L3JlY3Q+CiAgPHJlY3Qgc3R5bGU9ImZpbGw6ICM0NDQ7IiB4PSIyMCUiIHk9IjQ1JSIgd2lkdGg9IjYwJSIgaGVpZ2h0PSIxMCUiPjwvcmVjdD4KPC9zdmc+);
|
|
22
|
+
--image-checkerboard: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxyZWN0IHN0eWxlPSJmaWxsOiAjNDA0MDQwOyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSI+PC9yZWN0PgogIDxyZWN0IHN0eWxlPSJmaWxsOiAjODA4MDgwOyIgeD0iMCIgeT0iMCIgd2lkdGg9IjUwJSIgaGVpZ2h0PSI1MCUiPjwvcmVjdD4KICA8cmVjdCBzdHlsZT0iZmlsbDogIzgwODA4MDsiIHg9IjUwJSIgeT0iNTAlIiB3aWR0aD0iNTAlIiBoZWlnaHQ9IjUwJSI+PC9yZWN0Pgo8L3N2Zz4=);
|
|
23
|
+
|
|
20
24
|
--range-left-color: var(--value-color);
|
|
21
25
|
--range-right-color: var(--value-bg-color);
|
|
22
26
|
--range-right-hover-color: var(--hover-bg-color);
|
|
27
|
+
--button-image:
|
|
28
|
+
linear-gradient(
|
|
29
|
+
rgba(255, 255, 255, 1), rgba(0, 0, 0, 0.2)
|
|
30
|
+
);
|
|
23
31
|
|
|
24
32
|
color: var(--color);
|
|
25
33
|
background-color: var(--bg-color);
|
|
@@ -36,7 +44,7 @@ export default {
|
|
|
36
44
|
--menu-bg-color: #080808;
|
|
37
45
|
--menu-sep-color: #444444;
|
|
38
46
|
--hover-bg-color: #666666;
|
|
39
|
-
--focus-color: #88AAFF
|
|
47
|
+
--focus-color: #458; /*#88AAFF*/;
|
|
40
48
|
--range-color: #888888;
|
|
41
49
|
--invalid-color: #FF6666;
|
|
42
50
|
--selected-color: rgba(255, 255, 255, 0.3);
|
|
@@ -46,9 +54,15 @@ export default {
|
|
|
46
54
|
--range-left-color: var(--value-color);
|
|
47
55
|
--range-right-color: var(--value-bg-color);
|
|
48
56
|
--range-right-hover-color: var(--hover-bg-color);
|
|
57
|
+
--button-image: linear-gradient(
|
|
58
|
+
rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.4)
|
|
59
|
+
);
|
|
49
60
|
|
|
50
61
|
color: var(--color);
|
|
51
62
|
background-color: var(--bg-color);
|
|
63
|
+
|
|
64
|
+
--image-closed: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxyZWN0IHN0eWxlPSJmaWxsOiAjREREOyIgeD0iMjAlIiB5PSI0NSUiIHdpZHRoPSI2MCUiIGhlaWdodD0iMTAlIj48L3JlY3Q+Cjwvc3ZnPg==);
|
|
65
|
+
--image-open: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogIDxyZWN0IHN0eWxlPSJmaWxsOiAjREREOyIgeD0iNDUlIiB5PSIyMCUiIHdpZHRoPSIxMCUiIGhlaWdodD0iNjAlIj48L3JlY3Q+CiAgPHJlY3Qgc3R5bGU9ImZpbGw6ICNEREQ7IiB4PSIyMCUiIHk9IjQ1JSIgd2lkdGg9IjYwJSIgaGVpZ2h0PSIxMCUiPjwvcmVjdD4KPC9zdmc+);
|
|
52
66
|
}
|
|
53
67
|
}
|
|
54
68
|
|
|
@@ -57,7 +71,6 @@ export default {
|
|
|
57
71
|
--label-width: 45%;
|
|
58
72
|
--number-width: 40%;
|
|
59
73
|
|
|
60
|
-
|
|
61
74
|
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
|
|
62
75
|
--font-size: 11px;
|
|
63
76
|
--font-family-mono: Menlo, Monaco, Consolas, "Droid Sans Mono", monospace;
|
|
@@ -150,6 +163,9 @@ export default {
|
|
|
150
163
|
min-width: 0;
|
|
151
164
|
min-height: var(--line-height);
|
|
152
165
|
}
|
|
166
|
+
.muigui-root {
|
|
167
|
+
z-index: 1;
|
|
168
|
+
}
|
|
153
169
|
.muigui-root,
|
|
154
170
|
.muigui-menu {
|
|
155
171
|
display: flex;
|
|
@@ -174,8 +190,7 @@ export default {
|
|
|
174
190
|
color: var(--color);
|
|
175
191
|
background-color: var(--menu-bg-color);
|
|
176
192
|
min-height: var(--line-height);
|
|
177
|
-
padding
|
|
178
|
-
padding-bottom: 0.2em;
|
|
193
|
+
padding: 0.2em;
|
|
179
194
|
cursor: pointer;
|
|
180
195
|
border-radius: var(--border-radius);
|
|
181
196
|
}
|
|
@@ -196,7 +211,7 @@ export default {
|
|
|
196
211
|
.muigui-controller>*:nth-child(1) {
|
|
197
212
|
flex: 1 0 var(--label-width);
|
|
198
213
|
min-width: 0;
|
|
199
|
-
white-space: pre;
|
|
214
|
+
/* white-space: pre; why?? */
|
|
200
215
|
}
|
|
201
216
|
.muigui-controller>label:nth-child(1) {
|
|
202
217
|
place-content: center start;
|
|
@@ -238,32 +253,41 @@ export default {
|
|
|
238
253
|
/* fix! */
|
|
239
254
|
.muigui-open>button>label::before,
|
|
240
255
|
.muigui-closed>button>label::before {
|
|
256
|
+
content: "X";
|
|
257
|
+
color: rgba(0, 0, 0, 0);
|
|
258
|
+
background-color: var(--range-color);
|
|
259
|
+
border-radius: 0.2em;
|
|
241
260
|
width: 1.25em;
|
|
242
|
-
|
|
261
|
+
margin-right: 0.25em;
|
|
262
|
+
height: 1.25em; /*var(--line-height);*/
|
|
243
263
|
display: inline-grid;
|
|
244
264
|
place-content: center start;
|
|
245
265
|
pointer-events: none;
|
|
246
266
|
}
|
|
247
267
|
.muigui-open>button>label::before {
|
|
248
|
-
|
|
268
|
+
background-image: var(--image-open);
|
|
249
269
|
}
|
|
250
270
|
.muigui-closed>button>label::before {
|
|
251
|
-
|
|
271
|
+
background-image: var(--image-closed);
|
|
252
272
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
max-height: 100vh;
|
|
273
|
+
|
|
274
|
+
.muigui-open>.muigui-open-container {
|
|
275
|
+
transition: all 0.1s ease-out;
|
|
257
276
|
overflow: auto;
|
|
258
|
-
|
|
277
|
+
height: 100%;
|
|
259
278
|
}
|
|
260
|
-
|
|
261
|
-
.
|
|
262
|
-
transition: max-height 0.2s ease-out,
|
|
263
|
-
opacity 1s;
|
|
264
|
-
max-height: 0;
|
|
265
|
-
opacity: 0;
|
|
279
|
+
.muigui-closed>.muigui-open-container {
|
|
280
|
+
transition: all 0.1s ease-out;
|
|
266
281
|
overflow: hidden;
|
|
282
|
+
min-height: 0;
|
|
283
|
+
}
|
|
284
|
+
.muigui-open>.muigui-open-container>* {
|
|
285
|
+
transition: all 0.1s ease-out;
|
|
286
|
+
margin-top: 0px;
|
|
287
|
+
}
|
|
288
|
+
.muigui-closed>.muigui-open-container>* {
|
|
289
|
+
transition: all 0.1s ease-out;
|
|
290
|
+
margin-top: -100%;
|
|
267
291
|
}
|
|
268
292
|
|
|
269
293
|
/* ---- popdown ---- */
|
|
@@ -275,8 +299,12 @@ export default {
|
|
|
275
299
|
.muigui-value>*:nth-child(1).muigui-pop-down-top {
|
|
276
300
|
flex: 0;
|
|
277
301
|
}
|
|
278
|
-
.muigui-pop-down-bottom {
|
|
302
|
+
.muigui-closed .muigui-pop-down-bottom {
|
|
303
|
+
max-height: 0;
|
|
304
|
+
}
|
|
279
305
|
|
|
306
|
+
.muigui-value .muigui-pop-down-bottom {
|
|
307
|
+
margin: 0;
|
|
280
308
|
}
|
|
281
309
|
|
|
282
310
|
.muigui-pop-down-values {
|
|
@@ -298,6 +326,10 @@ export default {
|
|
|
298
326
|
width: auto;
|
|
299
327
|
color: var(--value-color);
|
|
300
328
|
background-color: var(--value-bg-color);
|
|
329
|
+
background-image: var(--image-checkerboard);
|
|
330
|
+
background-size: 10px 10px;
|
|
331
|
+
background-position: 0 0, 0 5px, 5px -5px, -5px 0px;
|
|
332
|
+
|
|
301
333
|
cursor: pointer;
|
|
302
334
|
|
|
303
335
|
display: grid;
|
|
@@ -389,14 +421,16 @@ export default {
|
|
|
389
421
|
|
|
390
422
|
.muigui-button {
|
|
391
423
|
display: grid;
|
|
392
|
-
|
|
424
|
+
padding: 2px 0 2px 0;
|
|
393
425
|
}
|
|
394
426
|
.muigui-button button {
|
|
395
427
|
border: none;
|
|
396
428
|
color: var(--value-color);
|
|
397
429
|
background-color: var(--button-bg-color);
|
|
430
|
+
background-image: var(--button-image);
|
|
398
431
|
cursor: pointer;
|
|
399
432
|
place-content: center center;
|
|
433
|
+
height: var(--line-height);
|
|
400
434
|
}
|
|
401
435
|
|
|
402
436
|
/* ------ [ color ] ------ */
|
|
@@ -608,9 +642,9 @@ export default {
|
|
|
608
642
|
border-bottom: 1px solid rgba(0,0,0,0.2);
|
|
609
643
|
border-right: 1px solid rgba(0,0,0,0.2);
|
|
610
644
|
background-color: var(--range-color);
|
|
611
|
-
margin-top: calc((var(--line-height) -
|
|
612
|
-
width: calc(var(--line-height) -
|
|
613
|
-
height: calc(var(--line-height) -
|
|
645
|
+
margin-top: calc((var(--line-height) - 6px) / -2);
|
|
646
|
+
width: calc(var(--line-height) - 6px);
|
|
647
|
+
height: calc(var(--line-height) - 6px);
|
|
614
648
|
}
|
|
615
649
|
|
|
616
650
|
.muigui-range input[type=range]::-webkit-slider-runnable-track {
|
|
@@ -694,8 +728,14 @@ export default {
|
|
|
694
728
|
|
|
695
729
|
`,
|
|
696
730
|
themes: {
|
|
697
|
-
default:
|
|
698
|
-
|
|
731
|
+
default: {
|
|
732
|
+
include: ['default'],
|
|
733
|
+
css: `
|
|
734
|
+
`,
|
|
735
|
+
},
|
|
736
|
+
float: {
|
|
737
|
+
include: ['default'],
|
|
738
|
+
css: `
|
|
699
739
|
:root {
|
|
700
740
|
color-scheme: light dark,
|
|
701
741
|
}
|
|
@@ -752,5 +792,57 @@ themes: {
|
|
|
752
792
|
--range-color: rgba(0, 0, 0, 0.125);
|
|
753
793
|
}
|
|
754
794
|
`,
|
|
795
|
+
},
|
|
796
|
+
form: {
|
|
797
|
+
include: [],
|
|
798
|
+
css: `
|
|
799
|
+
.muigui {
|
|
800
|
+
--width: 100%;
|
|
801
|
+
--label-width: 45%;
|
|
802
|
+
--number-width: 40%;
|
|
803
|
+
}
|
|
804
|
+
.muigui-root>button {
|
|
805
|
+
display: none;
|
|
806
|
+
}
|
|
807
|
+
.muigui-controller {
|
|
808
|
+
margin-top: 1em;
|
|
809
|
+
}
|
|
810
|
+
.muigui-label-controller {
|
|
811
|
+
display: flex;
|
|
812
|
+
flex-direction: column;
|
|
813
|
+
align-items: stretch;
|
|
814
|
+
margin-top: 1em;
|
|
815
|
+
}
|
|
816
|
+
.muigui-label-controller:has(.muigui-checkbox) {
|
|
817
|
+
flex-direction: row;
|
|
818
|
+
}
|
|
819
|
+
.muigui-value {
|
|
820
|
+
display: flex;
|
|
821
|
+
align-items: stretch;
|
|
822
|
+
}
|
|
823
|
+
.muigui-value>* {
|
|
824
|
+
flex: 1 1 auto;
|
|
825
|
+
min-width: 0;
|
|
826
|
+
}
|
|
827
|
+
.muigui-controller>*:nth-child(1) {
|
|
828
|
+
flex: 1 0 var(--label-width);
|
|
829
|
+
min-width: 0;
|
|
830
|
+
white-space: pre;
|
|
831
|
+
}
|
|
832
|
+
.muigui-controller>label:nth-child(1) {
|
|
833
|
+
place-content: center start;
|
|
834
|
+
display: inline-grid;
|
|
835
|
+
overflow: hidden;
|
|
836
|
+
}
|
|
837
|
+
.muigui-controller>*:nth-child(2) {
|
|
838
|
+
flex: 1 1 75%;
|
|
839
|
+
min-width: 0;
|
|
840
|
+
}
|
|
841
|
+
`,
|
|
842
|
+
},
|
|
843
|
+
none: {
|
|
844
|
+
include: [],
|
|
845
|
+
css: '',
|
|
846
|
+
},
|
|
755
847
|
},
|
|
756
848
|
};
|
package/src/views/NumberView.js
CHANGED
|
@@ -22,15 +22,22 @@ export default class NumberView extends EditView {
|
|
|
22
22
|
const wheelHelper = createWheelHelper();
|
|
23
23
|
super(createElem('input', {
|
|
24
24
|
type: 'number',
|
|
25
|
-
onInput: () =>
|
|
26
|
-
|
|
25
|
+
onInput: () => {
|
|
26
|
+
this.#handleInput(setValue, true);
|
|
27
|
+
},
|
|
28
|
+
onChange: () => {
|
|
29
|
+
this.#handleInput(setFinalValue, false);
|
|
30
|
+
},
|
|
27
31
|
onWheel: e => {
|
|
28
32
|
e.preventDefault();
|
|
29
33
|
const {min, max, step} = this.#options;
|
|
30
34
|
const delta = wheelHelper(e, step);
|
|
31
35
|
const v = parseFloat(this.domElement.value);
|
|
32
36
|
const newV = clamp(stepify(v + delta, v => v, step), min, max);
|
|
33
|
-
|
|
37
|
+
const [valid, outV] = this.#from(newV);
|
|
38
|
+
if (valid) {
|
|
39
|
+
setter.setValue(outV);
|
|
40
|
+
}
|
|
34
41
|
},
|
|
35
42
|
}));
|
|
36
43
|
this.setOptions(options);
|
package/src/views/TextView.js
CHANGED
|
@@ -16,8 +16,12 @@ export default class TextView extends EditView {
|
|
|
16
16
|
const setFinalValue = setter.setFinalValue.bind(setter);
|
|
17
17
|
super(createElem('input', {
|
|
18
18
|
type: 'text',
|
|
19
|
-
onInput: () =>
|
|
20
|
-
|
|
19
|
+
onInput: () => {
|
|
20
|
+
this.#handleInput(setValue, true);
|
|
21
|
+
},
|
|
22
|
+
onChange: () => {
|
|
23
|
+
this.#handleInput(setFinalValue, false);
|
|
24
|
+
},
|
|
21
25
|
}));
|
|
22
26
|
this.setOptions(options);
|
|
23
27
|
}
|