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 +19 -4
- package/src/controllers/ColorChooser.js +43 -3
- package/src/controllers/Container.js +2 -2
- package/src/controllers/PopDownController.js +14 -1
- package/src/libs/color-utils.js +232 -12
- package/src/libs/elem.js +6 -1
- package/src/libs/keyboard.js +2 -2
- package/src/libs/touch.js +4 -4
- package/src/libs/wheel.js +1 -1
- package/src/styles/muigui.css.js +11 -0
- package/src/views/ColorChooserView.js +128 -31
- package/src/views/RadioGridView.js +3 -2
- package/src/views/{View.js → View.ts} +15 -13
- /package/src/{esm.js → esm.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muigui",
|
|
3
|
-
"version": "0.0.
|
|
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
|
-
"
|
|
14
|
-
"
|
|
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
|
-
"
|
|
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
|
-
|
|
16
|
+
#colorView;
|
|
17
|
+
#textView;
|
|
18
|
+
#to;
|
|
19
|
+
#setKnobHelper;
|
|
20
|
+
|
|
21
|
+
constructor(object, property, options = {}) {
|
|
7
22
|
super(object, property, 'muigui-color-chooser');
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
|
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 = {
|
|
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;
|
package/src/libs/color-utils.js
CHANGED
|
@@ -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
|
|
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
|
|
44
|
-
const
|
|
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
|
-
|
|
124
|
-
|
|
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
|
|
293
|
-
const
|
|
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 => [
|
|
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 => [
|
|
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 => [
|
|
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 => [
|
|
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
|
+
}
|
package/src/libs/keyboard.js
CHANGED
|
@@ -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;
|
package/src/styles/muigui.css.js
CHANGED
|
@@ -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
|
-
<
|
|
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
|
|
20
|
-
<stop stop-color="hsl(60
|
|
21
|
-
<stop stop-color="hsl(60
|
|
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"
|
|
25
|
-
<rect width="64" height="48"
|
|
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
|
|
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"
|
|
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
|
-
#
|
|
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.$('
|
|
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.#
|
|
70
|
-
this.#
|
|
116
|
+
this.#hsva[1] = s;
|
|
117
|
+
this.#hsva[2] = (1 - v);
|
|
71
118
|
this.#skipHueUpdate = true;
|
|
72
|
-
|
|
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.#
|
|
128
|
+
this.#hsva[0] = h;
|
|
78
129
|
this.#skipSatLevelUpdate = true;
|
|
79
|
-
|
|
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.#
|
|
93
|
-
this.#
|
|
163
|
+
if (!this.#hsva) {
|
|
164
|
+
this.#hsva = this.#convertHexToInternal(this.#to(newV));
|
|
94
165
|
}
|
|
95
166
|
{
|
|
96
|
-
const [h, s, v] =
|
|
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.#
|
|
170
|
+
this.#hsva[0] = s > 0.001 && v > 0.001 ? h : this.#hsva[0];
|
|
100
171
|
}
|
|
101
172
|
if (!this.#skipSatLevelUpdate) {
|
|
102
|
-
this.#
|
|
103
|
-
this.#
|
|
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.#
|
|
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
|
-
|
|
5
|
-
#views = [];
|
|
4
|
+
domElement: HTMLElement;
|
|
6
5
|
|
|
7
|
-
|
|
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
|
}
|
/package/src/{esm.js → esm.ts}
RENAMED
|
File without changes
|