muigui 0.0.10 → 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.
@@ -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: #68C;
14
- --range-color: #888888;
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-top: 0.2em;
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
- height: var(--line-height);
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
- content: "ⓧ"; /*"▼";*/
268
+ background-image: var(--image-open);
249
269
  }
250
270
  .muigui-closed>button>label::before {
251
- content: "⨁"; /*"▶";*/
271
+ background-image: var(--image-closed);
252
272
  }
253
- .muigui-open>*:nth-child(2) {
254
- transition: max-height 0.2s ease-out,
255
- opacity 0.5s ease-out;
256
- max-height: 100vh;
273
+
274
+ .muigui-open>.muigui-open-container {
275
+ transition: all 0.1s ease-out;
257
276
  overflow: auto;
258
- opacity: 1;
277
+ height: 100%;
259
278
  }
260
-
261
- .muigui-closed>*:nth-child(2) {
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) - 2px) / -2);
612
- width: calc(var(--line-height) - 2px);
613
- height: calc(var(--line-height) - 2px);
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 {
@@ -670,6 +704,17 @@ export default {
670
704
  }
671
705
  */
672
706
 
707
+ .muigui-checkered-background {
708
+ background-color: #404040;
709
+ background-image:
710
+ linear-gradient(45deg, #808080 25%, transparent 25%),
711
+ linear-gradient(-45deg, #808080 25%, transparent 25%),
712
+ linear-gradient(45deg, transparent 75%, #808080 75%),
713
+ linear-gradient(-45deg, transparent 75%, #808080 75%);
714
+ background-size: 16px 16px;
715
+ background-position: 0 0, 0 8px, 8px -8px, -8px 0px;
716
+ }
717
+
673
718
  /* ---------------------------------------------------------- */
674
719
 
675
720
  /* needs to be at bottom to take precedence */
@@ -683,8 +728,14 @@ export default {
683
728
 
684
729
  `,
685
730
  themes: {
686
- default: '',
687
- float: `
731
+ default: {
732
+ include: ['default'],
733
+ css: `
734
+ `,
735
+ },
736
+ float: {
737
+ include: ['default'],
738
+ css: `
688
739
  :root {
689
740
  color-scheme: light dark,
690
741
  }
@@ -741,5 +792,57 @@ themes: {
741
792
  --range-color: rgba(0, 0, 0, 0.125);
742
793
  }
743
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
+ },
744
847
  },
745
848
  };
@@ -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
  }
@@ -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: () => this.#handleInput(setValue, true),
26
- onChange: () => this.#handleInput(setFinalValue, false),
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
- setter.setValue(newV);
37
+ const [valid, outV] = this.#from(newV);
38
+ if (valid) {
39
+ setter.setValue(outV);
40
+ }
34
41
  },
35
42
  }));
36
43
  this.setOptions(options);
@@ -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);
@@ -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: () => this.#handleInput(setValue, true),
20
- onChange: () => this.#handleInput(setFinalValue, false),
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
  }
@@ -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
  }