muigui 0.0.6 → 0.0.12

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.
@@ -9,6 +9,11 @@ const hexToUint32RGB = v => (parseInt(v.substring(1, 3), 16) << 16) |
9
9
  (parseInt(v.substring(3, 5), 16) << 8 ) |
10
10
  (parseInt(v.substring(5, 7), 16) );
11
11
  const uint32RGBToHex = v => `#${(Math.round(v)).toString(16).padStart(6, '0')}`;
12
+ const hexToUint32RGBA = v => (parseInt(v.substring(1, 3), 16) * 2 ** 24) +
13
+ (parseInt(v.substring(3, 5), 16) * 2 ** 16) +
14
+ (parseInt(v.substring(5, 7), 16) * 2 ** 8) +
15
+ (parseInt(v.substring(7, 9), 16) );
16
+ const uint32RGBAToHex = v => `#${(Math.round(v)).toString(16).padStart(8, '0')}`;
12
17
 
13
18
  export const hexToUint8RGB = v => [
14
19
  parseInt(v.substring(1, 3), 16),
@@ -17,16 +22,35 @@ export const hexToUint8RGB = v => [
17
22
  ];
18
23
  export const uint8RGBToHex = v => `#${Array.from(v).map(v => v.toString(16).padStart(2, '0')).join('')}`;
19
24
 
25
+ export const hexToUint8RGBA = v => [
26
+ parseInt(v.substring(1, 3), 16),
27
+ parseInt(v.substring(3, 5), 16),
28
+ parseInt(v.substring(5, 7), 16),
29
+ parseInt(v.substring(7, 9), 16),
30
+ ];
31
+ export const uint8RGBAToHex = v => `#${Array.from(v).map(v => v.toString(16).padStart(2, '0')).join('')}`;
32
+
20
33
  export const hexToFloatRGB = v => hexToUint8RGB(v).map(v => f3(v / 255));
21
34
  export const floatRGBToHex = v => uint8RGBToHex(Array.from(v).map(v => Math.round(clamp(v * 255, 0, 255))));
22
35
 
36
+ export const hexToFloatRGBA = v => hexToUint8RGBA(v).map(v => f3(v / 255));
37
+ export const floatRGBAToHex = v => uint8RGBAToHex(Array.from(v).map(v => Math.round(clamp(v * 255, 0, 255))));
38
+
39
+ const scaleAndClamp = v => clamp(Math.round(v * 255), 0, 255).toString(16).padStart(2, '0');
40
+
23
41
  const hexToObjectRGB = v => ({
24
42
  r: parseInt(v.substring(1, 3), 16) / 255,
25
43
  g: parseInt(v.substring(3, 5), 16) / 255,
26
44
  b: parseInt(v.substring(5, 7), 16) / 255,
27
45
  });
28
- const scaleAndClamp = v => clamp(Math.round(v * 255), 0, 255).toString(16).padStart(2, '0');
29
46
  const objectRGBToHex = v => `#${scaleAndClamp(v.r)}${scaleAndClamp(v.g)}${scaleAndClamp(v.b)}`;
47
+ const hexToObjectRGBA = v => ({
48
+ r: parseInt(v.substring(1, 3), 16) / 255,
49
+ g: parseInt(v.substring(3, 5), 16) / 255,
50
+ b: parseInt(v.substring(5, 7), 16) / 255,
51
+ a: parseInt(v.substring(7, 9), 16) / 255,
52
+ });
53
+ const objectRGBAToHex = v => `#${scaleAndClamp(v.r)}${scaleAndClamp(v.g)}${scaleAndClamp(v.b)}${scaleAndClamp(v.a)}`;
30
54
 
31
55
  const hexToCssRGB = v => `rgb(${hexToUint8RGB(v).join(', ')})`;
32
56
  const cssRGBRegex = /^\s*rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/;
@@ -34,12 +58,23 @@ const cssRGBToHex = v => {
34
58
  const m = cssRGBRegex.exec(v);
35
59
  return uint8RGBToHex([m[1], m[2], m[3]].map(v => parseInt(v)));
36
60
  };
61
+ const hexToCssRGBA = v => `rgba(${hexToUint8RGBA(v).map((v, i) => i === 3 ? v / 255 : v).join(', ')})`;
62
+ const cssRGBARegex = /^\s*rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+\.\d+|\d+)\s*\)\s*$/;
63
+ const cssRGBAToHex = v => {
64
+ const m = cssRGBARegex.exec(v);
65
+ return uint8RGBAToHex([m[1], m[2], m[3], m[4]].map((v, i) => i === 3 ? (parseFloat(v) * 255 | 0) : parseInt(v)));
66
+ };
37
67
 
38
68
  const hexToCssHSL = v => {
39
69
  const hsl = rgbUint8ToHsl(hexToUint8RGB(v)).map(v => f0(v));
40
70
  return `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`;
41
71
  };
42
- const cssHSLRegex = /^\s*hsl\(\s*(\d+)(?:deg|)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)\s*$/;
72
+ const hexToCssHSLA = v => {
73
+ const hsla = rgbaUint8ToHsla(hexToUint8RGBA(v)).map((v, i) => i === 3 ? f3(v) : f0(v));
74
+ return `hsl(${hsla[0]} ${hsla[1]}% ${hsla[2]}% / ${hsla[3]})`;
75
+ };
76
+ const cssHSLRegex = /^\s*hsl\(\s*(\d+)(?:deg|)\s*(?:,|)\s*(\d+)%\s*(?:,|)\s*(\d+)%\s*\)\s*$/;
77
+ const cssHSLARegex = /^\s*hsl\(\s*(\d+)(?:deg|)\s*(?:,|)\s*(\d+)%\s*(?:,|)\s*(\d+)%\s*\/\s*(\d+\.\d+|\d+)\s*\)\s*$/;
43
78
 
44
79
  const hex3DigitTo6Digit = v => `${v[0]}${v[0]}${v[1]}${v[1]}${v[2]}${v[2]}`;
45
80
  const cssHSLToHex = v => {
@@ -47,6 +82,11 @@ const cssHSLToHex = v => {
47
82
  const rgb = hslToRgbUint8([m[1], m[2], m[3]].map(v => parseFloat(v)));
48
83
  return uint8RGBToHex(rgb);
49
84
  };
85
+ const cssHSLAToHex = v => {
86
+ const m = cssHSLARegex.exec(v);
87
+ const rgba = hslaToRgbaUint8([m[1], m[2], m[3], m[4]].map(v => parseFloat(v)));
88
+ return uint8RGBAToHex(rgba);
89
+ };
50
90
 
51
91
  const euclideanModulo = (v, n) => ((v % n) + n) % n;
52
92
 
@@ -65,6 +105,11 @@ export function hslToRgbUint8([h, s, l]) {
65
105
  return [f(0), f(8), f(4)].map(v => Math.round(v * 255));
66
106
  }
67
107
 
108
+ export function hslaToRgbaUint8([h, s, l, a]) {
109
+ const rgb = hslToRgbUint8([h, s, l]);
110
+ return [...rgb, a * 255 | 0];
111
+ }
112
+
68
113
  export function rgbFloatToHsl01([r, g, b]) {
69
114
  const max = Math.max(r, g, b);
70
115
  const min = Math.min(r, g, b);
@@ -88,11 +133,21 @@ export function rgbFloatToHsl01([r, g, b]) {
88
133
  return [h / 6, s, l];
89
134
  }
90
135
 
136
+ export function rgbaFloatToHsla01([r, g, b, a]) {
137
+ const hsl = rgbFloatToHsl01([r, g, b]);
138
+ return [...hsl, a];
139
+ }
140
+
91
141
  export const rgbUint8ToHsl = (rgb) => {
92
142
  const [h, s, l] = rgbFloatToHsl01(rgb.map(v => v / 255));
93
143
  return [h * 360, s * 100, l * 100];
94
144
  };
95
145
 
146
+ export const rgbaUint8ToHsla = (rgba) => {
147
+ const [h, s, l, a] = rgbaFloatToHsla01(rgba.map(v => v / 255));
148
+ return [h * 360, s * 100, l * 100, a];
149
+ };
150
+
96
151
  export function hsv01ToRGBFloat([hue, sat, val]) {
97
152
  sat = clamp(sat, 0, 1);
98
153
  val = clamp(val, 0, 1);
@@ -101,6 +156,11 @@ export function hsv01ToRGBFloat([hue, sat, val]) {
101
156
  );
102
157
  }
103
158
 
159
+ export function hsva01ToRGBAFloat([hue, sat, val, alpha]) {
160
+ const rgb = hsv01ToRGBFloat([hue, sat, val]);
161
+ return [...rgb, alpha];
162
+ }
163
+
104
164
  const round3 = v => Math.round(v * 1000) / 1000;
105
165
 
106
166
  export function rgbFloatToHSV01([r, g, b]) {
@@ -118,16 +178,28 @@ export function rgbFloatToHSV01([r, g, b]) {
118
178
  ].map(round3);
119
179
  }
120
180
 
121
- window.hsv01ToRGBFloat = hsv01ToRGBFloat;
122
- window.rgbFloatToHSV01 = rgbFloatToHSV01;
181
+ export function rgbaFloatToHSVA01([r, g, b, a]) {
182
+ const hsv = rgbFloatToHSV01([r, g, b]);
183
+ return [...hsv, a];
184
+ }
185
+
186
+ // window.hsv01ToRGBFloat = hsv01ToRGBFloat;
187
+ // window.rgbFloatToHSV01 = rgbFloatToHSV01;
188
+
189
+ // Yea, meh!
190
+ export const hasAlpha = format => format.endsWith('a') || format.startsWith('hex8');
123
191
 
124
192
  const cssStringFormats = [
125
193
  { re: /^#(?:[0-9a-f]){6}$/i, format: 'hex6' },
126
194
  { re: /^(?:[0-9a-f]){6}$/i, format: 'hex6-no-hash' },
195
+ { re: /^#(?:[0-9a-f]){8}$/i, format: 'hex8' },
196
+ { re: /^(?:[0-9a-f]){8}$/i, format: 'hex8-no-hash' },
127
197
  { re: /^#(?:[0-9a-f]){3}$/i, format: 'hex3' },
128
198
  { re: /^(?:[0-9a-f]){3}$/i, format: 'hex3-no-hash' },
129
199
  { re: cssRGBRegex, format: 'css-rgb' },
130
200
  { re: cssHSLRegex, format: 'css-hsl' },
201
+ { re: cssRGBARegex, format: 'css-rgba' },
202
+ { re: cssHSLARegex, format: 'css-hsla' },
131
203
  ];
132
204
 
133
205
  function guessStringColorFormat(v) {
@@ -142,7 +214,8 @@ function guessStringColorFormat(v) {
142
214
  export function guessFormat(v) {
143
215
  switch (typeof v) {
144
216
  case 'number':
145
- return 'uint32-rgb';
217
+ console.warn('can not reliably guess format based on a number. You should pass in a format like {format: "uint32-rgb"} or {format: "uint32-rgb"}');
218
+ return v <= 0xFFFFFF ? 'uint32-rgb' : 'uint32-rgba';
146
219
  case 'string': {
147
220
  const formatInfo = guessStringColorFormat(v.trim());
148
221
  if (formatInfo) {
@@ -154,18 +227,28 @@ export function guessFormat(v) {
154
227
  if (v instanceof Uint8Array || v instanceof Uint8ClampedArray) {
155
228
  if (v.length === 3) {
156
229
  return 'uint8-rgb';
230
+ } else if (v.length === 4) {
231
+ return 'uint8-rgba';
157
232
  }
158
233
  } else if (v instanceof Float32Array) {
159
234
  if (v.length === 3) {
160
235
  return 'float-rgb';
236
+ } else if (v.length === 4) {
237
+ return 'float-rgba';
161
238
  }
162
239
  } else if (Array.isArray(v)) {
163
240
  if (v.length === 3) {
164
241
  return 'float-rgb';
242
+ } else if (v.length === 4) {
243
+ return 'float-rgba';
165
244
  }
166
245
  } else {
167
246
  if ('r' in v && 'g' in v && 'b' in v) {
168
- return 'object-rgb';
247
+ if ('a' in v) {
248
+ return 'object-rgba';
249
+ } else {
250
+ return 'object-rgb';
251
+ }
169
252
  }
170
253
  }
171
254
  }
@@ -179,6 +262,13 @@ function fixHex6(v) {
179
262
  //return fix(v.trim());
180
263
  }
181
264
 
265
+ function fixHex8(v) {
266
+ return v.trim(v);
267
+ //const formatInfo = guessStringColorFormat(v.trim());
268
+ //const fix = formatInfo ? formatInfo.fix : v => v;
269
+ //return fix(v.trim());
270
+ }
271
+
182
272
  function hex6ToHex3(hex6) {
183
273
  return (hex6[1] === hex6[2] &&
184
274
  hex6[3] === hex6[4] &&
@@ -214,6 +304,19 @@ const strToRGBObject = (s) => {
214
304
  }
215
305
  };
216
306
 
307
+ const strToRGBAObject = (s) => {
308
+ try {
309
+ const json = s.replace(/([a-z])/g, '"$1"');
310
+ const rgba = JSON.parse(json);
311
+ if (Number.isNaN(rgba.r) || Number.isNaN(rgba.g) || Number.isNaN(rgba.b) || Number.isNaN(rgba.a)) {
312
+ throw new Error('not {r, g, b, a}');
313
+ }
314
+ return [true, rgba];
315
+ } catch (e) {
316
+ return [false];
317
+ }
318
+ };
319
+
217
320
  const strToCssRGB = s => {
218
321
  const m = cssRGBRegex.exec(s);
219
322
  if (!m) {
@@ -224,6 +327,16 @@ const strToCssRGB = s => {
224
327
  return [!outOfRange, `rgb(${v.join(', ')})`];
225
328
  };
226
329
 
330
+ const strToCssRGBA = s => {
331
+ const m = cssRGBARegex.exec(s);
332
+ if (!m) {
333
+ return [false];
334
+ }
335
+ const v = [m[1], m[2], m[3], m[4]].map((v, i) => i === 3 ? parseFloat(v) : parseInt(v));
336
+ const outOfRange = v.find(v => v > 255);
337
+ return [!outOfRange, `rgba(${v.join(', ')})`];
338
+ };
339
+
227
340
  const strToCssHSL = s => {
228
341
  const m = cssHSLRegex.exec(s);
229
342
  if (!m) {
@@ -234,9 +347,22 @@ const strToCssHSL = s => {
234
347
  return [!outOfRange, `hsl(${v[0]}, ${v[1]}%, ${v[2]}%)`];
235
348
  };
236
349
 
350
+ const strToCssHSLA = s => {
351
+ const m = cssHSLARegex.exec(s);
352
+ if (!m) {
353
+ return [false];
354
+ }
355
+ const v = [m[1], m[2], m[3], m[4]].map(v => parseFloat(v));
356
+ const outOfRange = v.find(v => Number.isNaN(v));
357
+ return [!outOfRange, `hsl(${v[0]} ${v[1]}% ${v[2]}% / ${v[3]})`];
358
+ };
359
+
237
360
  const rgbObjectToStr = rgb => {
238
361
  return `{r:${f3(rgb.r)}, g:${f3(rgb.g)}, b:${f3(rgb.b)}}`;
239
362
  };
363
+ const rgbaObjectToStr = rgba => {
364
+ return `{r:${f3(rgba.r)}, g:${f3(rgba.g)}, b:${f3(rgba.b)}}, a:${f3(rgba.a)}}`;
365
+ };
240
366
 
241
367
  const strTo3IntsRE = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*$/;
242
368
  const strTo3Ints = s => {
@@ -249,6 +375,17 @@ const strTo3Ints = s => {
249
375
  return [!outOfRange, v];
250
376
  };
251
377
 
378
+ const strTo4IntsRE = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*$/;
379
+ const strTo4Ints = s => {
380
+ const m = strTo4IntsRE.exec(s);
381
+ if (!m) {
382
+ return [false];
383
+ }
384
+ const v = [m[1], m[2], m[3], m[4]].map(v => parseInt(v));
385
+ const outOfRange = v.find(v => v > 255);
386
+ return [!outOfRange, v];
387
+ };
388
+
252
389
  const strTo3Floats = s => {
253
390
  const numbers = s.split(',').map(s => s.trim());
254
391
  const v = numbers.map(v => parseFloat(v));
@@ -260,6 +397,17 @@ const strTo3Floats = s => {
260
397
  return [badNdx < 0, v.map(v => f3(v))];
261
398
  };
262
399
 
400
+ const strTo4Floats = s => {
401
+ const numbers = s.split(',').map(s => s.trim());
402
+ const v = numbers.map(v => parseFloat(v));
403
+ if (v.length !== 4) {
404
+ return [false];
405
+ }
406
+ // Note: using isNaN not Number.isNaN
407
+ const badNdx = numbers.findIndex(v => isNaN(v));
408
+ return [badNdx < 0, v.map(v => f3(v))];
409
+ };
410
+
263
411
  const strToUint32RGBRegex = /^\s*(?:0x){0,1}([0-9a-z]{1,6})\s*$/i;
264
412
  const strToUint32RGB = s => {
265
413
  const m = strToUint32RGBRegex.exec(s);
@@ -269,8 +417,19 @@ const strToUint32RGB = s => {
269
417
  return [true, parseInt(m[1], 16)];
270
418
  };
271
419
 
272
- const hexRE = /^\s*#[a-f0-9]{6}\s*$|^\s*#[a-f0-9]{3}\s*$/i;
273
- const hexNoHashRE = /^\s*[a-f0-9]{6}\s*$/i;
420
+ const strToUint32RGBARegex = /^\s*(?:0x){0,1}([0-9a-z]{1,8})\s*$/i;
421
+ const strToUint32RGBA = s => {
422
+ const m = strToUint32RGBARegex.exec(s);
423
+ if (!m) {
424
+ return [false];
425
+ }
426
+ return [true, parseInt(m[1], 16)];
427
+ };
428
+
429
+ const hex6RE = /^\s*#[a-f0-9]{6}\s*$|^\s*#[a-f0-9]{3}\s*$/i;
430
+ const hexNoHash6RE = /^\s*[a-f0-9]{6}\s*$/i;
431
+ const hex8RE = /^\s*#[a-f0-9]{8}\s*$/i;
432
+ const hexNoHash8RE = /^\s*[a-f0-9]{8}\s*$/i;
274
433
 
275
434
  // For each format converter
276
435
  //
@@ -308,7 +467,17 @@ export const colorFormatConverters = {
308
467
  to: fixHex6,
309
468
  },
310
469
  text: {
311
- from: v => [hexRE.test(v), v.trim()],
470
+ from: v => [hex6RE.test(v), v.trim()],
471
+ to: v => v,
472
+ },
473
+ },
474
+ 'hex8': {
475
+ color: {
476
+ from: v => [true, v],
477
+ to: fixHex8,
478
+ },
479
+ text: {
480
+ from: v => [hex8RE.test(v), v.trim()],
312
481
  to: v => v,
313
482
  },
314
483
  },
@@ -318,7 +487,7 @@ export const colorFormatConverters = {
318
487
  to: hex3ToHex6,
319
488
  },
320
489
  text: {
321
- from: v => [hexRE.test(v), hex6ToHex3(v.trim())],
490
+ from: v => [hex6RE.test(v), hex6ToHex3(v.trim())],
322
491
  to: v => v,
323
492
  },
324
493
  },
@@ -328,7 +497,17 @@ export const colorFormatConverters = {
328
497
  to: v => `#${fixHex6(v)}`,
329
498
  },
330
499
  text: {
331
- from: v => [hexNoHashRE.test(v), v.trim()],
500
+ from: v => [hexNoHash6RE.test(v), v.trim()],
501
+ to: v => v,
502
+ },
503
+ },
504
+ 'hex8-no-hash': {
505
+ color: {
506
+ from: v => [true, v.substring(1)],
507
+ to: v => `#${fixHex8(v)}`,
508
+ },
509
+ text: {
510
+ from: v => [hexNoHash8RE.test(v), v.trim()],
332
511
  to: v => v,
333
512
  },
334
513
  },
@@ -338,7 +517,7 @@ export const colorFormatConverters = {
338
517
  to: hex3ToHex6,
339
518
  },
340
519
  text: {
341
- from: v => [hexNoHashRE.test(v), hex6ToHex3(v.trim())],
520
+ from: v => [hexNoHash6RE.test(v), hex6ToHex3(v.trim())],
342
521
  to: v => v,
343
522
  },
344
523
  },
@@ -352,6 +531,16 @@ export const colorFormatConverters = {
352
531
  to: v => `0x${v.toString(16).padStart(6, '0')}`,
353
532
  },
354
533
  },
534
+ 'uint32-rgba': {
535
+ color: {
536
+ from: v => [true, hexToUint32RGBA(v)],
537
+ to: uint32RGBAToHex,
538
+ },
539
+ text: {
540
+ from: v => strToUint32RGBA(v),
541
+ to: v => `0x${v.toString(16).padStart(8, '0')}`,
542
+ },
543
+ },
355
544
  'uint8-rgb': {
356
545
  color: {
357
546
  from: v => [true, hexToUint8RGB(v)],
@@ -362,6 +551,16 @@ export const colorFormatConverters = {
362
551
  to: v => v.join(', '),
363
552
  },
364
553
  },
554
+ 'uint8-rgba': {
555
+ color: {
556
+ from: v => [true, hexToUint8RGBA(v)],
557
+ to: uint8RGBAToHex,
558
+ },
559
+ text: {
560
+ from: strTo4Ints,
561
+ to: v => v.join(', '),
562
+ },
563
+ },
365
564
  'float-rgb': {
366
565
  color: {
367
566
  from: v => [true, hexToFloatRGB(v)],
@@ -373,6 +572,17 @@ export const colorFormatConverters = {
373
572
  to: v => Array.from(v).map(v => f3(v)).join(', '),
374
573
  },
375
574
  },
575
+ 'float-rgba': {
576
+ color: {
577
+ from: v => [true, hexToFloatRGBA(v)],
578
+ to: floatRGBAToHex,
579
+ },
580
+ text: {
581
+ from: strTo4Floats,
582
+ // need Array.from because map of Float32Array makes a Float32Array
583
+ to: v => Array.from(v).map(v => f3(v)).join(', '),
584
+ },
585
+ },
376
586
  'object-rgb': {
377
587
  color: {
378
588
  from: v => [true, hexToObjectRGB(v)],
@@ -383,6 +593,16 @@ export const colorFormatConverters = {
383
593
  to: rgbObjectToStr,
384
594
  },
385
595
  },
596
+ 'object-rgba': {
597
+ color: {
598
+ from: v => [true, hexToObjectRGBA(v)],
599
+ to: objectRGBAToHex,
600
+ },
601
+ text: {
602
+ from: strToRGBAObject,
603
+ to: rgbaObjectToStr,
604
+ },
605
+ },
386
606
  'css-rgb': {
387
607
  color: {
388
608
  from: v => [true, hexToCssRGB(v)],
@@ -393,6 +613,16 @@ export const colorFormatConverters = {
393
613
  to: v => strToCssRGB(v)[1],
394
614
  },
395
615
  },
616
+ 'css-rgba': {
617
+ color: {
618
+ from: v => [true, hexToCssRGBA(v)],
619
+ to: cssRGBAToHex,
620
+ },
621
+ text: {
622
+ from: strToCssRGBA,
623
+ to: v => strToCssRGBA(v)[1],
624
+ },
625
+ },
396
626
  'css-hsl': {
397
627
  color: {
398
628
  from: v => [true, hexToCssHSL(v)],
@@ -403,4 +633,14 @@ export const colorFormatConverters = {
403
633
  to: v => strToCssHSL(v)[1],
404
634
  },
405
635
  },
636
+ 'css-hsla': {
637
+ color: {
638
+ from: v => [true, hexToCssHSLA(v)],
639
+ to: cssHSLAToHex,
640
+ },
641
+ text: {
642
+ from: strToCssHSLA,
643
+ to: v => strToCssHSLA(v)[1],
644
+ },
645
+ },
406
646
  };
package/src/libs/elem.js CHANGED
@@ -29,4 +29,9 @@ export function addElem(tag, parent, attrs = {}, children = []) {
29
29
  const elem = createElem(tag, attrs, children);
30
30
  parent.appendChild(elem);
31
31
  return elem;
32
- }
32
+ }
33
+
34
+ let nextId = 0;
35
+ export function getNewId() {
36
+ return `muigui-id-${nextId++}`;
37
+ }
@@ -10,7 +10,7 @@ const keyDirections = {
10
10
 
11
11
  // This probably needs to be global
12
12
  export function addKeyboardEvents(elem, {onDown = noop, onUp = noop}) {
13
- const keyDown = function(event) {
13
+ const keyDown = function (event) {
14
14
  const mult = event.shiftKey ? 10 : 1;
15
15
  const [dx, dy] = (keyDirections[event.key] || [0, 0]).map(v => v * mult);
16
16
  const fn = event.type === 'keydown' ? onDown : onUp;
@@ -25,7 +25,7 @@ export function addKeyboardEvents(elem, {onDown = noop, onUp = noop}) {
25
25
  elem.addEventListener('keydown', keyDown);
26
26
  elem.addEventListener('keyup', keyDown);
27
27
 
28
- return function() {
28
+ return function () {
29
29
  elem.removeEventListener('keydown', keyDown);
30
30
  elem.removeEventListener('keyup', keyDown);
31
31
  };
package/src/libs/touch.js CHANGED
@@ -17,7 +17,7 @@ export function computeRelativePosition(elem, event, start) {
17
17
 
18
18
  export function addTouchEvents(elem, {onDown = noop, onMove = noop, onUp = noop}) {
19
19
  let start;
20
- const pointerMove = function(event) {
20
+ const pointerMove = function (event) {
21
21
  const e = {
22
22
  type: 'move',
23
23
  ...computeRelativePosition(elem, event, start),
@@ -25,17 +25,17 @@ export function addTouchEvents(elem, {onDown = noop, onMove = noop, onUp = noop}
25
25
  onMove(e);
26
26
  };
27
27
 
28
- const pointerUp = function(event) {
28
+ const pointerUp = function (event) {
29
29
  elem.releasePointerCapture(event.pointerId);
30
30
  elem.removeEventListener('pointermove', pointerMove);
31
31
  elem.removeEventListener('pointerup', pointerUp);
32
-
32
+
33
33
  document.body.style.backgroundColor = '';
34
-
34
+
35
35
  onUp('up');
36
36
  };
37
37
 
38
- const pointerDown = function(event) {
38
+ const pointerDown = function (event) {
39
39
  elem.addEventListener('pointermove', pointerMove);
40
40
  elem.addEventListener('pointerup', pointerUp);
41
41
  elem.setPointerCapture(event.pointerId);
@@ -50,7 +50,7 @@ export function addTouchEvents(elem, {onDown = noop, onMove = noop, onUp = noop}
50
50
 
51
51
  elem.addEventListener('pointerdown', pointerDown);
52
52
 
53
- return function() {
53
+ return function () {
54
54
  elem.removeEventListener('pointerdown', pointerDown);
55
55
  };
56
56
  }
package/src/libs/utils.js CHANGED
@@ -72,3 +72,34 @@ export const makeRangeOptions = ({from, to, step}) => {
72
72
  };
73
73
  };
74
74
 
75
+ // TODO: remove an use one in conversions. Move makeRangeConverters there?
76
+ export const identity = {
77
+ to: v => v,
78
+ from: v => [true, v],
79
+ };
80
+ export function makeMinMaxPair(gui, properties, minPropName, maxPropName, options) {
81
+ const { converters: { from } = identity } = options;
82
+ const { min, max } = options;
83
+ const guiMinRange = options.minRange || 0;
84
+ const valueMinRange = from(guiMinRange)[1];
85
+ const minGui = gui
86
+ .add(properties, minPropName, {
87
+ ...options,
88
+ min,
89
+ max: max - guiMinRange,
90
+ })
91
+ .onChange(v => {
92
+ maxGui.setValue(Math.min(max, Math.max(v + valueMinRange, properties[maxPropName])));
93
+ });
94
+ const maxGui = gui
95
+ .add(properties, maxPropName, {
96
+ ...options,
97
+ min: min + guiMinRange,
98
+ max,
99
+ })
100
+ .onChange(v => {
101
+ minGui.setValue(Math.max(min, Math.min(v - valueMinRange, properties[minPropName])));
102
+ });
103
+ return [ minGui, maxGui ];
104
+ }
105
+
package/src/libs/wheel.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export function createWheelHelper() {
2
2
  let wheelAccum = 0;
3
- return function(e, step, wheelScale = 5) {
3
+ return function (e, step, wheelScale = 5) {
4
4
  wheelAccum -= e.deltaY * step / wheelScale;
5
5
  const wheelSteps = Math.floor(Math.abs(wheelAccum) / step) * Math.sign(wheelAccum);
6
6
  const delta = wheelSteps * step;