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.
package/src/muigui.js CHANGED
@@ -5,16 +5,22 @@ import {
5
5
  mapRange,
6
6
  makeRangeConverters,
7
7
  makeRangeOptions,
8
+ makeMinMaxPair,
8
9
  } from './libs/utils.js';
9
10
  import {
10
11
  converters
11
12
  } from './libs/conversions.js';
13
+ import {
14
+ hasAlpha,
15
+ guessFormat,
16
+ } from './libs/color-utils.js';
12
17
  import Canvas from './controllers/Canvas.js';
13
18
  import Color from './controllers/Color.js';
14
19
  import Divider from './controllers/Divider.js';
15
20
  import Folder from './controllers/Folder.js';
16
21
  import Label from './controllers/Label.js';
17
22
  import Controller from './controllers/Controller.js';
23
+ import ColorChooser from './controllers/ColorChooser.js';
18
24
 
19
25
  import Column from './layout/Column.js';
20
26
  import Frame from './layout/Frame.js';
@@ -28,9 +34,6 @@ export {
28
34
  Row,
29
35
  };
30
36
 
31
- let stylesInjected = false;
32
- const styleElem = createElem('style');
33
-
34
37
  export class GUIFolder extends Folder {
35
38
  add(object, property, ...args) {
36
39
  const controller = object instanceof Controller
@@ -41,8 +44,13 @@ export class GUIFolder extends Folder {
41
44
  addCanvas(name) {
42
45
  return this.addController(new Canvas(name));
43
46
  }
44
- addColor(object, property, ...args) {
45
- return this.addController(new Color(object, property, ...args));
47
+ addColor(object, property, options = {}) {
48
+ const value = object[property];
49
+ if (hasAlpha(options.format || guessFormat(value))) {
50
+ return this.addController(new ColorChooser(object, property, options));
51
+ } else {
52
+ return this.addController(new Color(object, property, options));
53
+ }
46
54
  }
47
55
  addDivider() {
48
56
  return this.addController(new Divider());
@@ -55,11 +63,50 @@ export class GUIFolder extends Folder {
55
63
  }
56
64
  }
57
65
 
66
+ class MuiguiElement extends HTMLElement {
67
+ constructor() {
68
+ super();
69
+ this.shadow = this.attachShadow({mode: 'open'});
70
+ }
71
+ }
72
+
73
+ customElements.define('muigui-element', MuiguiElement);
74
+
75
+ const baseStyleSheet = new CSSStyleSheet();
76
+ baseStyleSheet.replaceSync(css.default);
77
+ const userStyleSheet = new CSSStyleSheet();
78
+
79
+ function makeStyleSheetUpdater(styleSheet) {
80
+ let newCss;
81
+ let newCssPromise;
82
+
83
+ function updateStyle() {
84
+ if (newCss && !newCssPromise) {
85
+ const s = newCss;
86
+ newCss = undefined;
87
+ newCssPromise = styleSheet.replace(s).then(() => {
88
+ newCssPromise = undefined;
89
+ updateStyle();
90
+ });
91
+ }
92
+ }
93
+
94
+ return function updateStyleSheet(css) {
95
+ newCss = css;
96
+ updateStyle();
97
+ };
98
+ }
99
+
100
+ const updateBaseStyle = makeStyleSheetUpdater(baseStyleSheet);
101
+ const updateUserStyle = makeStyleSheetUpdater(userStyleSheet);
102
+
58
103
  export class GUI extends GUIFolder {
59
104
  static converters = converters;
60
105
  static mapRange = mapRange;
61
106
  static makeRangeConverters = makeRangeConverters;
62
107
  static makeRangeOptions = makeRangeOptions;
108
+ static makeMinMaxPair = makeMinMaxPair;
109
+ #localStyleSheet = new CSSStyleSheet();
63
110
 
64
111
  constructor(options = {}) {
65
112
  super('Controls', 'muigui-root');
@@ -70,16 +117,11 @@ export class GUI extends GUIFolder {
70
117
  autoPlace = true,
71
118
  width,
72
119
  title = 'Controls',
73
- injectStyles = true,
74
120
  } = options;
75
121
  let {
76
122
  parent,
77
123
  } = options;
78
- if (injectStyles && !stylesInjected) {
79
- stylesInjected = true;
80
- (document.head || document.documentElement).appendChild(styleElem);
81
- styleElem.textContent = css;
82
- }
124
+
83
125
  if (width) {
84
126
  this.domElement.style.width = /^\d+$/.test(width) ? `${width}px` : width;
85
127
  }
@@ -88,13 +130,34 @@ export class GUI extends GUIFolder {
88
130
  this.domElement.classList.add('muigui-auto-place');
89
131
  }
90
132
  if (parent) {
91
- parent.appendChild(this.domElement);
133
+ const muiguiElement = createElem('muigui-element');
134
+ muiguiElement.shadowRoot.adoptedStyleSheets = [baseStyleSheet, userStyleSheet, this.#localStyleSheet];
135
+ muiguiElement.shadow.appendChild(this.domElement);
136
+ parent.appendChild(muiguiElement);
92
137
  }
93
138
  if (title) {
94
139
  this.title(title);
95
140
  }
96
141
  this.domElement.classList.add('muigui', 'muigui-colors');
97
142
  }
143
+ setStyle(css) {
144
+ this.#localStyleSheet.replace(css);
145
+ }
146
+ static setBaseStyles(css) {
147
+ updateBaseStyle(css);
148
+ }
149
+ static getBaseStyleSheet() {
150
+ return baseStyleSheet;
151
+ }
152
+ static setUserStyles(css) {
153
+ updateUserStyle(css);
154
+ }
155
+ static getUserStyleSheet() {
156
+ return userStyleSheet;
157
+ }
158
+ static setTheme(name) {
159
+ GUI.setBaseStyles(`${css.default}\n${css.themes[name] || ''}`);
160
+ }
98
161
  }
99
162
 
100
163
  export default GUI;
@@ -1,7 +1,9 @@
1
- export default `
2
- .muigui-colors {
1
+ export default {
2
+ default: `
3
+ .muigui {
3
4
  --bg-color: #ddd;
4
5
  --color: #222;
6
+ --contrast-color: #eee;
5
7
  --value-color: #145 ;
6
8
  --value-bg-color: #eeee;
7
9
  --disabled-color: #999;
@@ -24,9 +26,10 @@ export default `
24
26
  }
25
27
 
26
28
  @media (prefers-color-scheme: dark) {
27
- .muigui-colors {
29
+ .muigui {
28
30
  --bg-color: #222222;
29
31
  --color: #dddddd;
32
+ --contrast-color: #000;
30
33
  --value-color: #43e5f7;
31
34
  --value-bg-color: #444444;
32
35
  --disabled-color: #666666;
@@ -667,6 +670,17 @@ export default `
667
670
  }
668
671
  */
669
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
+
670
684
  /* ---------------------------------------------------------- */
671
685
 
672
686
  /* needs to be at bottom to take precedence */
@@ -678,5 +692,65 @@ export default `
678
692
  z-index: 100001;
679
693
  }
680
694
 
681
- `;
695
+ `,
696
+ themes: {
697
+ default: '',
698
+ float: `
699
+ :root {
700
+ color-scheme: light dark,
701
+ }
702
+
703
+ .muigui {
704
+ --width: 400px;
705
+ --bg-color: initial;
706
+ --label-width: 25%;
707
+ --number-width: 20%;
708
+ }
709
+
710
+ input,
711
+ .muigui-label-controller>label {
712
+ text-shadow:
713
+ -1px -1px 0 var(--contrast-color),
714
+ 1px -1px 0 var(--contrast-color),
715
+ -1px 1px 0 var(--contrast-color),
716
+ 1px 1px 0 var(--contrast-color);
717
+ }
718
+
719
+ .muigui-controller > label:nth-child(1) {
720
+ place-content: center end;
721
+ margin-right: 1em;
722
+ }
682
723
 
724
+ .muigui-value > :nth-child(2) {
725
+ margin-left: 1em;
726
+ }
727
+
728
+ .muigui-root>*:nth-child(1) {
729
+ display: none;
730
+ }
731
+
732
+ .muigui-range input[type=range]::-webkit-slider-thumb {
733
+ border-radius: 1em;
734
+ }
735
+
736
+ .muigui-range input[type=range]::-webkit-slider-runnable-track {
737
+ -webkit-appearance: initial;
738
+ appearance: none;
739
+ border: 1px solid rgba(0, 0, 0, 0.25);
740
+ height: 2px;
741
+ }
742
+
743
+ .muigui-colors {
744
+ --value-color: var(--color );
745
+ --value-bg-color: rgba(0, 0, 0, 0.1);
746
+ --disabled-color: #cccccc;
747
+ --menu-bg-color: rgba(0, 0, 0, 0.1);
748
+ --menu-sep-color: #bbbbbb;
749
+ --hover-bg-color: rgba(0, 0, 0, 0);
750
+ --invalid-color: #FF0000;
751
+ --selected-color: rgba(0, 0, 0, 0.3);
752
+ --range-color: rgba(0, 0, 0, 0.125);
753
+ }
754
+ `,
755
+ },
756
+ };
@@ -2,6 +2,7 @@ import { createElem } from '../libs/elem.js';
2
2
  import EditView from './EditView.js';
3
3
 
4
4
  export default class CheckboxView extends EditView {
5
+ #checkboxElem;
5
6
  constructor(setter, id) {
6
7
  const checkboxElem = createElem('input', {
7
8
  type: 'checkbox',
@@ -14,8 +15,9 @@ export default class CheckboxView extends EditView {
14
15
  },
15
16
  });
16
17
  super(createElem('label', {}, [checkboxElem]));
18
+ this.#checkboxElem = checkboxElem;
17
19
  }
18
20
  updateDisplay(v) {
19
- this.domElement.checked = v;
21
+ this.#checkboxElem.checked = v;
20
22
  }
21
23
  }
@@ -1,36 +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
- hexToUint8RGB,
7
7
  hexToFloatRGB,
8
- hslToRgbUint8,
8
+ hexToFloatRGBA,
9
9
  hsv01ToRGBFloat,
10
+ hsva01ToRGBAFloat,
10
11
  rgbFloatToHSV01,
11
- rgbUint8ToHsl,
12
+ rgbaFloatToHSVA01,
12
13
  floatRGBToHex,
13
- uint8RGBToHex,
14
+ floatRGBAToHex,
15
+ rgbaFloatToHsla01,
14
16
  } from '../libs/color-utils.js';
17
+ import { copyExistingProperties } from '../libs/utils.js';
15
18
 
16
19
  const svg = `
17
-
18
- <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;">
19
- <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">
20
22
  <stop stop-color="rgba(0,0,0,0)" offset="0%"/>
21
23
  <stop stop-color="#000" offset="100%"/>
22
24
  </linearGradient>
23
- <linearGradient id="muigui-color-chooser-hue">
24
- <stop stop-color="hsl(60, 0%, 100%)" offset="0%"/>
25
- <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%"/>
26
28
  </linearGradient>
27
29
 
28
- <rect width="64" height="48" fill="url(#muigui-color-chooser-hue)"/>
29
- <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"/>
30
32
  <circle r="4" class="muigui-color-chooser-circle"/>
31
33
  </svg>
32
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;">
33
- <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">
34
36
  <stop stop-color="hsl(0,100%,50%)" offset="0%"/>
35
37
  <stop stop-color="hsl(60,100%,50%)" offset="16.666%"/>
36
38
  <stop stop-color="hsl(120,100%,50%)" offset="33.333%"/>
@@ -39,48 +41,108 @@ const svg = `
39
41
  <stop stop-color="hsl(300,100%,50%)" offset="83.333%"/>
40
42
  <stop stop-color="hsl(360,100%,50%)" offset="100%"/>
41
43
  </linearGradient>
42
- <rect y="1" width="64" height="4" fill="url('#muigui-color-chooser-hues')"/>
43
- <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">
44
56
  <rect x="-3" width="6" height="6" />
45
57
  </g>
46
58
  </svg>
47
59
  `;
48
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?
49
74
  export default class ColorChooserView extends EditView {
75
+ #to;
76
+ #from;
50
77
  #satLevelElem;
51
- #hueUIElem;
52
78
  #circleElem;
79
+ #hueUIElem;
53
80
  #hueElem;
54
81
  #hueCursorElem;
55
- #hsv;
82
+ #alphaUIElem;
83
+ #alphaElem;
84
+ #alphaCursorElem;
85
+ #hsva;
56
86
  #skipHueUpdate;
57
87
  #skipSatLevelUpdate;
88
+ #skipAlphaUpdate;
89
+ #options = {
90
+ converters: identity,
91
+ alpha: false,
92
+ };
93
+ #convertInternalToHex;
94
+ #convertHexToInternal;
58
95
 
59
- constructor(setter) {
96
+ constructor(setter, options) {
60
97
  super(createElem('div', {
61
98
  innerHTML: svg,
62
99
  className: 'muigui-no-scroll',
63
100
  }));
64
101
  this.#satLevelElem = this.domElement.children[0];
65
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);
66
107
  this.#circleElem = this.$('.muigui-color-chooser-circle');
67
- this.#hueElem = this.$('#muigui-color-chooser-hue');
68
- 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');
69
112
 
70
113
  const handleSatLevelChange = (e) => {
71
114
  const s = clamp(e.nx, 0, 1);
72
115
  const v = clamp(e.ny, 0, 1);
73
- this.#hsv[1] = s;
74
- this.#hsv[2] = (1 - v);
116
+ this.#hsva[1] = s;
117
+ this.#hsva[2] = (1 - v);
75
118
  this.#skipHueUpdate = true;
76
- 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
+ }
77
124
  };
78
125
 
79
126
  const handleHueChange = (e) => {
80
127
  const h = clamp(e.nx, 0, 1);
81
- this.#hsv[0] = h;
128
+ this.#hsva[0] = h;
82
129
  this.#skipSatLevelUpdate = true;
83
- 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
+ }
84
146
  };
85
147
 
86
148
  addTouchEvents(this.#satLevelElem, {
@@ -91,29 +153,45 @@ export default class ColorChooserView extends EditView {
91
153
  onDown: handleHueChange,
92
154
  onMove: handleHueChange,
93
155
  });
156
+ addTouchEvents(this.#alphaUIElem, {
157
+ onDown: handleAlphaChange,
158
+ onMove: handleAlphaChange,
159
+ });
160
+ this.setOptions(options);
94
161
  }
95
162
  updateDisplay(newV) {
96
- if (!this.#hsv) {
97
- this.#hsv = rgbFloatToHSV01(hexToFloatRGB(newV));
163
+ if (!this.#hsva) {
164
+ this.#hsva = this.#convertHexToInternal(this.#to(newV));
98
165
  }
99
166
  {
100
- const [h, s, v] = rgbFloatToHSV01(hexToFloatRGB(newV));
167
+ const [h, s, v, a = 1] = this.#convertHexToInternal(this.#to(newV));
101
168
  // Don't copy the hue if it was un-computable.
102
169
  if (!this.#skipHueUpdate) {
103
- 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];
104
171
  }
105
172
  if (!this.#skipSatLevelUpdate) {
106
- this.#hsv[1] = s;
107
- this.#hsv[2] = v;
173
+ this.#hsva[1] = s;
174
+ this.#hsva[2] = v;
175
+ }
176
+ if (!this.#skipAlphaUpdate) {
177
+ this.#hsva[3] = a;
108
178
  }
109
179
  }
110
180
  {
111
- const [h, s, v] = this.#hsv;
181
+ const [h, s, v, a] = this.#hsva;
182
+ const [hue, sat, lum] = rgbaFloatToHsla01(hsva01ToRGBAFloat(this.#hsva));
183
+
112
184
  if (!this.#skipHueUpdate) {
113
185
  this.#hueCursorElem.setAttribute('transform', `translate(${h * 64}, 0)`);
114
- this.#hueElem.children[0].setAttribute('stop-color', `hsl(${h * 360}, 0%, 100%)`);
115
- this.#hueElem.children[1].setAttribute('stop-color', `hsl(${h * 360}, 100%, 50%)`);
116
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
+
117
195
  if (!this.#skipSatLevelUpdate) {
118
196
  this.#circleElem.setAttribute('cx', `${s * 64}`);
119
197
  this.#circleElem.setAttribute('cy', `${(1 - v) * 48}`);
@@ -121,5 +199,20 @@ export default class ColorChooserView extends EditView {
121
199
  }
122
200
  this.#skipHueUpdate = false;
123
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;
124
217
  }
125
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);
@@ -22,16 +22,22 @@ export default class RangeView extends EditView {
22
22
  type: 'range',
23
23
  onInput: () => {
24
24
  this.#skipUpdate = true;
25
- const [valid, v] = this.#from(parseFloat(this.domElement.value));
25
+ const {min, max, step} = this.#options;
26
+ const v = parseFloat(this.domElement.value);
27
+ const newV = clamp(stepify(v, v => v, step), min, max);
28
+ const [valid, validV] = this.#from(newV);
26
29
  if (valid) {
27
- setter.setValue(v);
30
+ setter.setValue(validV);
28
31
  }
29
32
  },
30
33
  onChange: () => {
31
34
  this.#skipUpdate = true;
32
- const [valid, v] = this.#from(parseFloat(this.domElement.value));
35
+ const {min, max, step} = this.#options;
36
+ const v = parseFloat(this.domElement.value);
37
+ const newV = clamp(stepify(v, v => v, step), min, max);
38
+ const [valid, validV] = this.#from(newV);
33
39
  if (valid) {
34
- setter.setFinalValue(v);
40
+ setter.setFinalValue(validV);
35
41
  }
36
42
  },
37
43
  onWheel: e => {
@@ -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