muigui 0.0.10 → 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.
package/package.json CHANGED
@@ -1,17 +1,22 @@
1
1
  {
2
2
  "name": "muigui",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "A Simple GUI",
5
5
  "main": "muigui.js",
6
6
  "module": "src/muigui.js",
7
7
  "type": "module",
8
8
  "scripts": {
9
9
  "build": "npm run build-normal",
10
+ "build-ci": "npm run build && node build/prep-for-deploy.js",
10
11
  "build-normal": "rollup -c",
11
12
  "build-min": "rollup -c",
13
+ "check-ci": "npm run pre-push",
12
14
  "eslint": "eslint \"**/*.js\"",
13
- "pre-push": "npm run eslint",
14
- "test": "echo \"Error: no test specified\" && exit 1"
15
+ "fix": "eslint --fix",
16
+ "pre-push": "npm run eslint && npm run build && npm run test",
17
+ "start": "node build/serve.js",
18
+ "test": "node test/puppeteer.js",
19
+ "watch": "npm run start"
15
20
  },
16
21
  "repository": {
17
22
  "type": "git",
@@ -35,10 +40,20 @@
35
40
  "devDependencies": {
36
41
  "@rollup/plugin-node-resolve": "^15.0.1",
37
42
  "@rollup/plugin-terser": "^0.4.0",
43
+ "@rollup/plugin-typescript": "^11.1.5",
44
+ "@tsconfig/recommended": "^1.0.3",
45
+ "@typescript-eslint/eslint-plugin": "^6.12.0",
46
+ "@typescript-eslint/parser": "^6.12.0",
47
+ "chokidar": "^3.5.3",
38
48
  "eslint": "^8.20.0",
39
49
  "eslint-plugin-html": "^7.1.0",
50
+ "eslint-plugin-one-variable-per-var": "^0.0.3",
40
51
  "eslint-plugin-optional-comma-spacing": "0.0.4",
41
52
  "eslint-plugin-require-trailing-comma": "0.0.1",
42
- "rollup": "^3.20.2"
53
+ "puppeteer": "^21.5.2",
54
+ "rollup": "^3.20.2",
55
+ "servez": "^2.1.2",
56
+ "tslib": "^2.6.2",
57
+ "typescript": "5.2"
43
58
  }
44
59
  }
@@ -1,12 +1,52 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+ import {
3
+ colorFormatConverters,
4
+ guessFormat,
5
+ hasAlpha,
6
+ hexToUint8RGB,
7
+ hslToRgbUint8,
8
+ rgbUint8ToHsl,
9
+ uint8RGBToHex,
10
+ } from '../libs/color-utils.js';
1
11
  import ColorChooserView from '../views/ColorChooserView.js';
2
12
  import TextView from '../views/TextView.js';
3
13
  import PopDownController from './PopDownController.js';
4
14
 
5
15
  export default class ColorChooser extends PopDownController {
6
- constructor(object, property) {
16
+ #colorView;
17
+ #textView;
18
+ #to;
19
+ #setKnobHelper;
20
+
21
+ constructor(object, property, options = {}) {
7
22
  super(object, property, 'muigui-color-chooser');
8
- this.addTop(new TextView(this));
9
- this.addBottom(new ColorChooserView(this));
23
+ const format = options.format || guessFormat(this.getValue());
24
+ const {color, text} = colorFormatConverters[format];
25
+ this.#to = color.to;
26
+ this.#textView = new TextView(this, {converters: text, alpha: hasAlpha(format)});
27
+ this.#colorView = new ColorChooserView(this, {converters: color, alpha: hasAlpha(format)});
28
+ this.addTop(this.#textView);
29
+ this.addBottom(this.#colorView);
30
+ // WTF! FIX!
31
+ this.#setKnobHelper = () => {
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
+ };
10
40
  this.updateDisplay();
11
41
  }
42
+ updateDisplay() {
43
+ super.updateDisplay();
44
+ if (this.#setKnobHelper) {
45
+ this.#setKnobHelper();
46
+ }
47
+ }
48
+ setOptions(options) {
49
+ super.setOptions(options);
50
+ return this;
51
+ }
12
52
  }
@@ -43,14 +43,14 @@ export default class Container extends Controller {
43
43
  }
44
44
  return this;
45
45
  }
46
- _addControllerImpl(controller) {
46
+ #addControllerImpl(controller) {
47
47
  this.domElement.appendChild(controller.domElement);
48
48
  this.#controllers.push(controller);
49
49
  controller.setParent(this);
50
50
  return controller;
51
51
  }
52
52
  addController(controller) {
53
- return this.#childDestController._addControllerImpl(controller);
53
+ return this.#childDestController.#addControllerImpl(controller);
54
54
  }
55
55
  pushContainer(container) {
56
56
  this.addController(container);
@@ -29,8 +29,11 @@ pc.addBottom
29
29
  export default class PopDownController extends ValueController {
30
30
  #top;
31
31
  #valuesView;
32
+ #checkboxElem;
32
33
  #bottom;
33
- #options = {open: false};
34
+ #options = {
35
+ open: false,
36
+ };
34
37
 
35
38
  constructor(object, property, options = {}) {
36
39
  super(object, property, 'muigui-pop-down-controller');
@@ -46,12 +49,22 @@ export default class PopDownController extends ValueController {
46
49
  type: 'checkbox',
47
50
  onChange: () => {
48
51
  this.#options.open = checkboxElem.checked;
52
+ this.updateDisplay();
49
53
  },
50
54
  }));
55
+ this.#checkboxElem = checkboxElem;
51
56
  this.#valuesView = this.#top.add(new ElementView('div', 'muigui-pop-down-values'));
52
57
  this.#bottom = this.add(new ElementView('div', 'muigui-pop-down-bottom'));
53
58
  this.setOptions(options);
54
59
  }
60
+ setKnobColor(bgCssColor/*, fgCssColor*/) {
61
+ if (this.#checkboxElem) {
62
+ this.#checkboxElem.style = `
63
+ --range-color: ${bgCssColor};
64
+ --value-bg-color: ${bgCssColor};
65
+ `;
66
+ }
67
+ }
55
68
  updateDisplay() {
56
69
  super.updateDisplay();
57
70
  const {open} = this.#options;
@@ -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,14 +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
  };
37
- const cssRGBARegex = /^\s*rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/;
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
+ };
38
67
 
39
68
  const hexToCssHSL = v => {
40
69
  const hsl = rgbUint8ToHsl(hexToUint8RGB(v)).map(v => f0(v));
41
70
  return `hsl(${hsl[0]}, ${hsl[1]}%, ${hsl[2]}%)`;
42
71
  };
43
- const cssHSLRegex = /^\s*hsl\(\s*(\d+)(?:deg|)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)\s*$/;
44
- const cssHSLARegex = /^\s*hsl\(\s*(\d+)(?:deg|)\s*,\s*(\d+)%\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*$/;
45
78
 
46
79
  const hex3DigitTo6Digit = v => `${v[0]}${v[0]}${v[1]}${v[1]}${v[2]}${v[2]}`;
47
80
  const cssHSLToHex = v => {
@@ -49,6 +82,11 @@ const cssHSLToHex = v => {
49
82
  const rgb = hslToRgbUint8([m[1], m[2], m[3]].map(v => parseFloat(v)));
50
83
  return uint8RGBToHex(rgb);
51
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
+ };
52
90
 
53
91
  const euclideanModulo = (v, n) => ((v % n) + n) % n;
54
92
 
@@ -67,6 +105,11 @@ export function hslToRgbUint8([h, s, l]) {
67
105
  return [f(0), f(8), f(4)].map(v => Math.round(v * 255));
68
106
  }
69
107
 
108
+ export function hslaToRgbaUint8([h, s, l, a]) {
109
+ const rgb = hslToRgbUint8([h, s, l]);
110
+ return [...rgb, a * 255 | 0];
111
+ }
112
+
70
113
  export function rgbFloatToHsl01([r, g, b]) {
71
114
  const max = Math.max(r, g, b);
72
115
  const min = Math.min(r, g, b);
@@ -90,11 +133,21 @@ export function rgbFloatToHsl01([r, g, b]) {
90
133
  return [h / 6, s, l];
91
134
  }
92
135
 
136
+ export function rgbaFloatToHsla01([r, g, b, a]) {
137
+ const hsl = rgbFloatToHsl01([r, g, b]);
138
+ return [...hsl, a];
139
+ }
140
+
93
141
  export const rgbUint8ToHsl = (rgb) => {
94
142
  const [h, s, l] = rgbFloatToHsl01(rgb.map(v => v / 255));
95
143
  return [h * 360, s * 100, l * 100];
96
144
  };
97
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
+
98
151
  export function hsv01ToRGBFloat([hue, sat, val]) {
99
152
  sat = clamp(sat, 0, 1);
100
153
  val = clamp(val, 0, 1);
@@ -103,6 +156,11 @@ export function hsv01ToRGBFloat([hue, sat, val]) {
103
156
  );
104
157
  }
105
158
 
159
+ export function hsva01ToRGBAFloat([hue, sat, val, alpha]) {
160
+ const rgb = hsv01ToRGBFloat([hue, sat, val]);
161
+ return [...rgb, alpha];
162
+ }
163
+
106
164
  const round3 = v => Math.round(v * 1000) / 1000;
107
165
 
108
166
  export function rgbFloatToHSV01([r, g, b]) {
@@ -120,8 +178,13 @@ export function rgbFloatToHSV01([r, g, b]) {
120
178
  ].map(round3);
121
179
  }
122
180
 
123
- window.hsv01ToRGBFloat = hsv01ToRGBFloat;
124
- 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;
125
188
 
126
189
  // Yea, meh!
127
190
  export const hasAlpha = format => format.endsWith('a') || format.startsWith('hex8');
@@ -199,6 +262,13 @@ function fixHex6(v) {
199
262
  //return fix(v.trim());
200
263
  }
201
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
+
202
272
  function hex6ToHex3(hex6) {
203
273
  return (hex6[1] === hex6[2] &&
204
274
  hex6[3] === hex6[4] &&
@@ -234,6 +304,19 @@ const strToRGBObject = (s) => {
234
304
  }
235
305
  };
236
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
+
237
320
  const strToCssRGB = s => {
238
321
  const m = cssRGBRegex.exec(s);
239
322
  if (!m) {
@@ -244,6 +327,16 @@ const strToCssRGB = s => {
244
327
  return [!outOfRange, `rgb(${v.join(', ')})`];
245
328
  };
246
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
+
247
340
  const strToCssHSL = s => {
248
341
  const m = cssHSLRegex.exec(s);
249
342
  if (!m) {
@@ -254,9 +347,22 @@ const strToCssHSL = s => {
254
347
  return [!outOfRange, `hsl(${v[0]}, ${v[1]}%, ${v[2]}%)`];
255
348
  };
256
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
+
257
360
  const rgbObjectToStr = rgb => {
258
361
  return `{r:${f3(rgb.r)}, g:${f3(rgb.g)}, b:${f3(rgb.b)}}`;
259
362
  };
363
+ const rgbaObjectToStr = rgba => {
364
+ return `{r:${f3(rgba.r)}, g:${f3(rgba.g)}, b:${f3(rgba.b)}}, a:${f3(rgba.a)}}`;
365
+ };
260
366
 
261
367
  const strTo3IntsRE = /^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*$/;
262
368
  const strTo3Ints = s => {
@@ -269,6 +375,17 @@ const strTo3Ints = s => {
269
375
  return [!outOfRange, v];
270
376
  };
271
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
+
272
389
  const strTo3Floats = s => {
273
390
  const numbers = s.split(',').map(s => s.trim());
274
391
  const v = numbers.map(v => parseFloat(v));
@@ -280,6 +397,17 @@ const strTo3Floats = s => {
280
397
  return [badNdx < 0, v.map(v => f3(v))];
281
398
  };
282
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
+
283
411
  const strToUint32RGBRegex = /^\s*(?:0x){0,1}([0-9a-z]{1,6})\s*$/i;
284
412
  const strToUint32RGB = s => {
285
413
  const m = strToUint32RGBRegex.exec(s);
@@ -289,8 +417,19 @@ const strToUint32RGB = s => {
289
417
  return [true, parseInt(m[1], 16)];
290
418
  };
291
419
 
292
- const hexRE = /^\s*#[a-f0-9]{6}\s*$|^\s*#[a-f0-9]{3}\s*$/i;
293
- 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;
294
433
 
295
434
  // For each format converter
296
435
  //
@@ -328,7 +467,17 @@ export const colorFormatConverters = {
328
467
  to: fixHex6,
329
468
  },
330
469
  text: {
331
- 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()],
332
481
  to: v => v,
333
482
  },
334
483
  },
@@ -338,7 +487,7 @@ export const colorFormatConverters = {
338
487
  to: hex3ToHex6,
339
488
  },
340
489
  text: {
341
- from: v => [hexRE.test(v), hex6ToHex3(v.trim())],
490
+ from: v => [hex6RE.test(v), hex6ToHex3(v.trim())],
342
491
  to: v => v,
343
492
  },
344
493
  },
@@ -348,7 +497,17 @@ export const colorFormatConverters = {
348
497
  to: v => `#${fixHex6(v)}`,
349
498
  },
350
499
  text: {
351
- 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()],
352
511
  to: v => v,
353
512
  },
354
513
  },
@@ -358,7 +517,7 @@ export const colorFormatConverters = {
358
517
  to: hex3ToHex6,
359
518
  },
360
519
  text: {
361
- from: v => [hexNoHashRE.test(v), hex6ToHex3(v.trim())],
520
+ from: v => [hexNoHash6RE.test(v), hex6ToHex3(v.trim())],
362
521
  to: v => v,
363
522
  },
364
523
  },
@@ -372,6 +531,16 @@ export const colorFormatConverters = {
372
531
  to: v => `0x${v.toString(16).padStart(6, '0')}`,
373
532
  },
374
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
+ },
375
544
  'uint8-rgb': {
376
545
  color: {
377
546
  from: v => [true, hexToUint8RGB(v)],
@@ -382,6 +551,16 @@ export const colorFormatConverters = {
382
551
  to: v => v.join(', '),
383
552
  },
384
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
+ },
385
564
  'float-rgb': {
386
565
  color: {
387
566
  from: v => [true, hexToFloatRGB(v)],
@@ -393,6 +572,17 @@ export const colorFormatConverters = {
393
572
  to: v => Array.from(v).map(v => f3(v)).join(', '),
394
573
  },
395
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
+ },
396
586
  'object-rgb': {
397
587
  color: {
398
588
  from: v => [true, hexToObjectRGB(v)],
@@ -403,6 +593,16 @@ export const colorFormatConverters = {
403
593
  to: rgbObjectToStr,
404
594
  },
405
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
+ },
406
606
  'css-rgb': {
407
607
  color: {
408
608
  from: v => [true, hexToCssRGB(v)],
@@ -413,6 +613,16 @@ export const colorFormatConverters = {
413
613
  to: v => strToCssRGB(v)[1],
414
614
  },
415
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
+ },
416
626
  'css-hsl': {
417
627
  color: {
418
628
  from: v => [true, hexToCssHSL(v)],
@@ -423,4 +633,14 @@ export const colorFormatConverters = {
423
633
  to: v => strToCssHSL(v)[1],
424
634
  },
425
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
+ },
426
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,7 +25,7 @@ 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);
@@ -35,7 +35,7 @@ export function addTouchEvents(elem, {onDown = noop, onMove = noop, onUp = noop}
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/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;
@@ -670,6 +670,17 @@ export default {
670
670
  }
671
671
  */
672
672
 
673
+ .muigui-checkered-background {
674
+ background-color: #404040;
675
+ background-image:
676
+ linear-gradient(45deg, #808080 25%, transparent 25%),
677
+ linear-gradient(-45deg, #808080 25%, transparent 25%),
678
+ linear-gradient(45deg, transparent 75%, #808080 75%),
679
+ linear-gradient(-45deg, transparent 75%, #808080 75%);
680
+ background-size: 16px 16px;
681
+ background-position: 0 0, 0 8px, 8px -8px, -8px 0px;
682
+ }
683
+
673
684
  /* ---------------------------------------------------------- */
674
685
 
675
686
  /* needs to be at bottom to take precedence */
@@ -1,32 +1,38 @@
1
- import { createElem } from '../libs/elem.js';
1
+ import { createElem, getNewId } from '../libs/elem.js';
2
2
  import { addTouchEvents } from '../libs/touch.js';
3
+ import { identity } from '../libs/conversions.js';
3
4
  import { clamp } from '../libs/utils.js';
4
5
  import EditView from './EditView.js';
5
6
  import {
6
7
  hexToFloatRGB,
8
+ hexToFloatRGBA,
7
9
  hsv01ToRGBFloat,
10
+ hsva01ToRGBAFloat,
8
11
  rgbFloatToHSV01,
12
+ rgbaFloatToHSVA01,
9
13
  floatRGBToHex,
14
+ floatRGBAToHex,
15
+ rgbaFloatToHsla01,
10
16
  } from '../libs/color-utils.js';
17
+ import { copyExistingProperties } from '../libs/utils.js';
11
18
 
12
19
  const svg = `
13
-
14
- <svg tabindex="0" viewBox="0 0 64 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
15
- <linearGradient id="muigui-color-chooser-light-dark" x1="0" x2="0" y1="0" y2="1">
20
+ <svg class="muigui-checkered-background" tabindex="0" viewBox="0 0 64 48" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
21
+ <linearGradient data-src="muigui-color-chooser-light-dark" x1="0" x2="0" y1="0" y2="1">
16
22
  <stop stop-color="rgba(0,0,0,0)" offset="0%"/>
17
23
  <stop stop-color="#000" offset="100%"/>
18
24
  </linearGradient>
19
- <linearGradient id="muigui-color-chooser-hue">
20
- <stop stop-color="hsl(60, 0%, 100%)" offset="0%"/>
21
- <stop stop-color="hsl(60, 100%, 50%)" offset="100%"/>
25
+ <linearGradient data-src="muigui-color-chooser-hue">
26
+ <stop stop-color="hsl(60 0% 100% / 1)" offset="0%"/>
27
+ <stop stop-color="hsl(60 100% 50% / 1)" offset="100%"/>
22
28
  </linearGradient>
23
29
 
24
- <rect width="64" height="48" fill="url(#muigui-color-chooser-hue)"/>
25
- <rect width="64" height="48" fill="url(#muigui-color-chooser-light-dark)"/>
30
+ <rect width="64" height="48" data-target="muigui-color-chooser-hue"/>
31
+ <rect width="64" height="48" data-target="muigui-color-chooser-light-dark"/>
26
32
  <circle r="4" class="muigui-color-chooser-circle"/>
27
33
  </svg>
28
34
  <svg tabindex="0" viewBox="0 0 64 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
29
- <linearGradient id="muigui-color-chooser-hues" x1="0" x2="1" y1="0" y2="0">
35
+ <linearGradient data-src="muigui-color-chooser-hues" x1="0" x2="1" y1="0" y2="0">
30
36
  <stop stop-color="hsl(0,100%,50%)" offset="0%"/>
31
37
  <stop stop-color="hsl(60,100%,50%)" offset="16.666%"/>
32
38
  <stop stop-color="hsl(120,100%,50%)" offset="33.333%"/>
@@ -35,48 +41,108 @@ const svg = `
35
41
  <stop stop-color="hsl(300,100%,50%)" offset="83.333%"/>
36
42
  <stop stop-color="hsl(360,100%,50%)" offset="100%"/>
37
43
  </linearGradient>
38
- <rect y="1" width="64" height="4" fill="url('#muigui-color-chooser-hues')"/>
39
- <g class="muigui-color-chooser-cursor">
44
+ <rect y="1" width="64" height="4" data-target="muigui-color-chooser-hues"/>
45
+ <g class="muigui-color-chooser-hue-cursor">
46
+ <rect x="-3" width="6" height="6" />
47
+ </g>
48
+ </svg>
49
+ <svg class="muigui-checkered-background" tabindex="0" viewBox="0 0 64 6" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
50
+ <linearGradient data-src="muigui-color-chooser-alpha" x1="0" x2="1" y1="0" y2="0">
51
+ <stop stop-color="hsla(0,100%,100%,0)" offset="0%"/>
52
+ <stop stop-color="hsla(0,100%,100%,1)" offset="100%"/>
53
+ </linearGradient>
54
+ <rect y="1" width="64" height="4" data-target="muigui-color-chooser-alpha"/>
55
+ <g class="muigui-color-chooser-alpha-cursor">
40
56
  <rect x="-3" width="6" height="6" />
41
57
  </g>
42
58
  </svg>
43
59
  `;
44
60
 
61
+ function connectFillTargets(elem) {
62
+ elem.querySelectorAll('[data-src]').forEach(srcElem => {
63
+ const id = getNewId();
64
+ srcElem.id = id;
65
+ elem.querySelectorAll(`[data-target=${srcElem.dataset.src}]`).forEach(targetElem => {
66
+ targetElem.setAttribute('fill', `url(#${id})`);
67
+ });
68
+ });
69
+ return elem;
70
+ }
71
+
72
+ // Was originally going to make alpha an option. Issue is
73
+ // hard coded conversions?
45
74
  export default class ColorChooserView extends EditView {
75
+ #to;
76
+ #from;
46
77
  #satLevelElem;
47
- #hueUIElem;
48
78
  #circleElem;
79
+ #hueUIElem;
49
80
  #hueElem;
50
81
  #hueCursorElem;
51
- #hsv;
82
+ #alphaUIElem;
83
+ #alphaElem;
84
+ #alphaCursorElem;
85
+ #hsva;
52
86
  #skipHueUpdate;
53
87
  #skipSatLevelUpdate;
88
+ #skipAlphaUpdate;
89
+ #options = {
90
+ converters: identity,
91
+ alpha: false,
92
+ };
93
+ #convertInternalToHex;
94
+ #convertHexToInternal;
54
95
 
55
- constructor(setter) {
96
+ constructor(setter, options) {
56
97
  super(createElem('div', {
57
98
  innerHTML: svg,
58
99
  className: 'muigui-no-scroll',
59
100
  }));
60
101
  this.#satLevelElem = this.domElement.children[0];
61
102
  this.#hueUIElem = this.domElement.children[1];
103
+ this.#alphaUIElem = this.domElement.children[2];
104
+ connectFillTargets(this.#satLevelElem);
105
+ connectFillTargets(this.#hueUIElem);
106
+ connectFillTargets(this.#alphaUIElem);
62
107
  this.#circleElem = this.$('.muigui-color-chooser-circle');
63
- this.#hueElem = this.$('#muigui-color-chooser-hue');
64
- this.#hueCursorElem = this.$('.muigui-color-chooser-cursor');
108
+ this.#hueElem = this.$('[data-src=muigui-color-chooser-hue]');
109
+ this.#hueCursorElem = this.$('.muigui-color-chooser-hue-cursor');
110
+ this.#alphaElem = this.$('[data-src=muigui-color-chooser-alpha]');
111
+ this.#alphaCursorElem = this.$('.muigui-color-chooser-alpha-cursor');
65
112
 
66
113
  const handleSatLevelChange = (e) => {
67
114
  const s = clamp(e.nx, 0, 1);
68
115
  const v = clamp(e.ny, 0, 1);
69
- this.#hsv[1] = s;
70
- this.#hsv[2] = (1 - v);
116
+ this.#hsva[1] = s;
117
+ this.#hsva[2] = (1 - v);
71
118
  this.#skipHueUpdate = true;
72
- setter.setValue(floatRGBToHex(hsv01ToRGBFloat(this.#hsv)));
119
+ this.#skipAlphaUpdate = true;
120
+ const [valid, newV] = this.#from(this.#convertInternalToHex(this.#hsva));
121
+ if (valid) {
122
+ setter.setValue(newV);
123
+ }
73
124
  };
74
125
 
75
126
  const handleHueChange = (e) => {
76
127
  const h = clamp(e.nx, 0, 1);
77
- this.#hsv[0] = h;
128
+ this.#hsva[0] = h;
78
129
  this.#skipSatLevelUpdate = true;
79
- setter.setValue(floatRGBToHex(hsv01ToRGBFloat(this.#hsv)));
130
+ this.#skipAlphaUpdate = true;
131
+ const [valid, newV] = this.#from(this.#convertInternalToHex(this.#hsva));
132
+ if (valid) {
133
+ setter.setValue(newV);
134
+ }
135
+ };
136
+
137
+ const handleAlphaChange = (e) => {
138
+ const a = clamp(e.nx, 0, 1);
139
+ this.#hsva[3] = a;
140
+ this.#skipHueUpdate = true;
141
+ this.#skipSatLevelUpdate = true;
142
+ const [valid, newV] = this.#from(this.#convertInternalToHex(this.#hsva));
143
+ if (valid) {
144
+ setter.setValue(newV);
145
+ }
80
146
  };
81
147
 
82
148
  addTouchEvents(this.#satLevelElem, {
@@ -87,29 +153,45 @@ export default class ColorChooserView extends EditView {
87
153
  onDown: handleHueChange,
88
154
  onMove: handleHueChange,
89
155
  });
156
+ addTouchEvents(this.#alphaUIElem, {
157
+ onDown: handleAlphaChange,
158
+ onMove: handleAlphaChange,
159
+ });
160
+ this.setOptions(options);
90
161
  }
91
162
  updateDisplay(newV) {
92
- if (!this.#hsv) {
93
- this.#hsv = rgbFloatToHSV01(hexToFloatRGB(newV));
163
+ if (!this.#hsva) {
164
+ this.#hsva = this.#convertHexToInternal(this.#to(newV));
94
165
  }
95
166
  {
96
- const [h, s, v] = rgbFloatToHSV01(hexToFloatRGB(newV));
167
+ const [h, s, v, a = 1] = this.#convertHexToInternal(this.#to(newV));
97
168
  // Don't copy the hue if it was un-computable.
98
169
  if (!this.#skipHueUpdate) {
99
- this.#hsv[0] = s > 0.001 && v > 0.001 ? h : this.#hsv[0];
170
+ this.#hsva[0] = s > 0.001 && v > 0.001 ? h : this.#hsva[0];
100
171
  }
101
172
  if (!this.#skipSatLevelUpdate) {
102
- this.#hsv[1] = s;
103
- this.#hsv[2] = v;
173
+ this.#hsva[1] = s;
174
+ this.#hsva[2] = v;
175
+ }
176
+ if (!this.#skipAlphaUpdate) {
177
+ this.#hsva[3] = a;
104
178
  }
105
179
  }
106
180
  {
107
- const [h, s, v] = this.#hsv;
181
+ const [h, s, v, a] = this.#hsva;
182
+ const [hue, sat, lum] = rgbaFloatToHsla01(hsva01ToRGBAFloat(this.#hsva));
183
+
108
184
  if (!this.#skipHueUpdate) {
109
185
  this.#hueCursorElem.setAttribute('transform', `translate(${h * 64}, 0)`);
110
- this.#hueElem.children[0].setAttribute('stop-color', `hsl(${h * 360}, 0%, 100%)`);
111
- this.#hueElem.children[1].setAttribute('stop-color', `hsl(${h * 360}, 100%, 50%)`);
112
186
  }
187
+ this.#hueElem.children[0].setAttribute('stop-color', `hsl(${hue * 360} 0% 100% / ${a})`);
188
+ this.#hueElem.children[1].setAttribute('stop-color', `hsl(${hue * 360} 100% 50% / ${a})`);
189
+ if (!this.#skipAlphaUpdate) {
190
+ this.#alphaCursorElem.setAttribute('transform', `translate(${a * 64}, 0)`);
191
+ }
192
+ this.#alphaElem.children[0].setAttribute('stop-color', `hsl(${hue * 360} ${sat * 100}% ${lum * 100}% / 0)`);
193
+ this.#alphaElem.children[1].setAttribute('stop-color', `hsl(${hue * 360} ${sat * 100}% ${lum * 100}% / 1)`);
194
+
113
195
  if (!this.#skipSatLevelUpdate) {
114
196
  this.#circleElem.setAttribute('cx', `${s * 64}`);
115
197
  this.#circleElem.setAttribute('cy', `${(1 - v) * 48}`);
@@ -117,5 +199,20 @@ export default class ColorChooserView extends EditView {
117
199
  }
118
200
  this.#skipHueUpdate = false;
119
201
  this.#skipSatLevelUpdate = false;
202
+ this.#skipAlphaUpdate = false;
203
+ }
204
+ setOptions(options) {
205
+ copyExistingProperties(this.#options, options);
206
+ const {converters: {to, from}, alpha} = this.#options;
207
+ this.#alphaUIElem.style.display = alpha ? '' : 'none';
208
+ this.#convertInternalToHex = alpha
209
+ ? v => floatRGBAToHex(hsva01ToRGBAFloat(v))
210
+ : v => floatRGBToHex(hsv01ToRGBFloat(v));
211
+ this.#convertHexToInternal = alpha
212
+ ? v => rgbaFloatToHSVA01(hexToFloatRGBA(v))
213
+ : v => rgbFloatToHSV01(hexToFloatRGB(v));
214
+ this.#to = to;
215
+ this.#from = from;
216
+ return this;
120
217
  }
121
218
  }
@@ -15,7 +15,7 @@ export default class RadioGridView extends EditView {
15
15
  type: 'radio',
16
16
  name,
17
17
  value: ndx,
18
- onChange: function() {
18
+ onChange: function () {
19
19
  if (this.checked) {
20
20
  setter.setFinalValue(that.#values[this.value]);
21
21
  }
@@ -24,12 +24,13 @@ export default class RadioGridView extends EditView {
24
24
  createElem('button', {
25
25
  type: 'button',
26
26
  textContent: key,
27
- onClick: function() {
27
+ onClick: function () {
28
28
  this.previousElementSibling.click();
29
29
  },
30
30
  }),
31
31
  ]);
32
32
  })));
33
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
33
34
  const that = this;
34
35
  this.#values = values;
35
36
  this.cols(cols);
@@ -1,56 +1,58 @@
1
1
  import { removeArrayElem } from '../libs/utils.js';
2
2
 
3
3
  export default class View {
4
- #childDestElem;
5
- #views = [];
4
+ domElement: HTMLElement;
6
5
 
7
- constructor(elem) {
6
+ #childDestElem: HTMLElement;
7
+ #views: View[] = [];
8
+
9
+ constructor(elem: HTMLElement) {
8
10
  this.domElement = elem;
9
11
  this.#childDestElem = elem;
10
12
  }
11
- addElem(elem) {
13
+ addElem(elem: HTMLElement) {
12
14
  this.#childDestElem.appendChild(elem);
13
15
  return elem;
14
16
  }
15
- removeElem(elem) {
17
+ removeElem(elem: HTMLElement) {
16
18
  this.#childDestElem.removeChild(elem);
17
19
  return elem;
18
20
  }
19
- pushSubElem(elem) {
21
+ pushSubElem(elem: HTMLElement) {
20
22
  this.#childDestElem.appendChild(elem);
21
23
  this.#childDestElem = elem;
22
24
  }
23
25
  popSubElem() {
24
- this.#childDestElem = this.#childDestElem.parentElement;
26
+ this.#childDestElem = this.#childDestElem.parentElement!;
25
27
  }
26
- add(view) {
28
+ add(view: View) {
27
29
  this.#views.push(view);
28
30
  this.addElem(view.domElement);
29
31
  return view;
30
32
  }
31
- remove(view) {
33
+ remove(view: View) {
32
34
  this.removeElem(view.domElement);
33
35
  removeArrayElem(this.#views, view);
34
36
  return view;
35
37
  }
36
- pushSubView(view) {
38
+ pushSubView(view: View) {
37
39
  this.pushSubElem(view.domElement);
38
40
  }
39
41
  popSubView() {
40
42
  this.popSubElem();
41
43
  }
42
- setOptions(options) {
44
+ setOptions(options: any) {
43
45
  for (const view of this.#views) {
44
46
  view.setOptions(options);
45
47
  }
46
48
  }
47
- updateDisplayIfNeeded(newV, ignoreCache) {
49
+ updateDisplayIfNeeded(newV: any, ignoreCache?: boolean) {
48
50
  for (const view of this.#views) {
49
51
  view.updateDisplayIfNeeded(newV, ignoreCache);
50
52
  }
51
53
  return this;
52
54
  }
53
- $(selector) {
55
+ $(selector: string) {
54
56
  return this.domElement.querySelector(selector);
55
57
  }
56
58
  }
File without changes