@thednp/color-picker 0.0.2-alpha1 → 0.0.2-alpha2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * ColorPicker v0.0.2alpha1 (http://thednp.github.io/color-picker)
2
+ * ColorPicker v0.0.2alpha2 (http://thednp.github.io/color-picker)
3
3
  * Copyright 2022 © thednp
4
4
  * Licensed under MIT (https://github.com/thednp/color-picker/blob/master/LICENSE)
5
5
  */
@@ -843,6 +843,8 @@
843
843
  if (nonColors.includes(color)
844
844
  || ['#', ...COLOR_FORMAT].some((f) => color.includes(f))) return false;
845
845
 
846
+ if (['black', 'white'].includes(color)) return true;
847
+
846
848
  return ['rgb(255, 255, 255)', 'rgb(0, 0, 0)'].every((c) => {
847
849
  setElementStyle(documentHead, { color });
848
850
  const computedColor = getElementStyle(documentHead, 'color');
@@ -869,6 +871,11 @@
869
871
  */
870
872
  function bound01(N, max) {
871
873
  let n = N;
874
+
875
+ if (typeof N === 'number'
876
+ && Math.min(N, 0) === 0 // round values to 6 decimals Math.round(N * (10 ** 6)) / 10 ** 6
877
+ && Math.max(N, 1) === 1) return N;
878
+
872
879
  if (isOnePointZero(N)) n = '100%';
873
880
 
874
881
  const processPercent = isPercentage(n);
@@ -972,15 +979,12 @@
972
979
  /**
973
980
  * Converts an RGB colour value to HSL.
974
981
  *
975
- * @param {number} R Red component [0, 255]
976
- * @param {number} G Green component [0, 255]
977
- * @param {number} B Blue component [0, 255]
982
+ * @param {number} r Red component [0, 1]
983
+ * @param {number} g Green component [0, 1]
984
+ * @param {number} b Blue component [0, 1]
978
985
  * @returns {CP.HSL} {h,s,l} object with [0, 1] ranged values
979
986
  */
980
- function rgbToHsl(R, G, B) {
981
- const r = R / 255;
982
- const g = G / 255;
983
- const b = B / 255;
987
+ function rgbToHsl(r, g, b) {
984
988
  const max = Math.max(r, g, b);
985
989
  const min = Math.min(r, g, b);
986
990
  let h = 0;
@@ -992,17 +996,10 @@
992
996
  } else {
993
997
  const d = max - min;
994
998
  s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
995
- switch (max) {
996
- case r:
997
- h = (g - b) / d + (g < b ? 6 : 0);
998
- break;
999
- case g:
1000
- h = (b - r) / d + 2;
1001
- break;
1002
- case b:
1003
- h = (r - g) / d + 4;
1004
- break;
1005
- }
999
+ if (max === r) h = (g - b) / d + (g < b ? 6 : 0);
1000
+ if (max === g) h = (b - r) / d + 2;
1001
+ if (max === b) h = (r - g) / d + 4;
1002
+
1006
1003
  h /= 6;
1007
1004
  }
1008
1005
  return { h, s, l };
@@ -1031,7 +1028,7 @@
1031
1028
  * @param {number} h Hue Angle [0, 1]
1032
1029
  * @param {number} s Saturation [0, 1]
1033
1030
  * @param {number} l Lightness Angle [0, 1]
1034
- * @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
1031
+ * @returns {CP.RGB} {r,g,b} object with [0, 1] ranged values
1035
1032
  */
1036
1033
  function hslToRgb(h, s, l) {
1037
1034
  let r = 0;
@@ -1050,7 +1047,6 @@
1050
1047
  g = hueToRgb(p, q, h);
1051
1048
  b = hueToRgb(p, q, h - 1 / 3);
1052
1049
  }
1053
- [r, g, b] = [r, g, b].map((x) => x * 255);
1054
1050
 
1055
1051
  return { r, g, b };
1056
1052
  }
@@ -1060,16 +1056,12 @@
1060
1056
  * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
1061
1057
  * @link http://alvyray.com/Papers/CG/hwb2rgb.htm
1062
1058
  *
1063
- * @param {number} R Red component [0, 255]
1064
- * @param {number} G Green [0, 255]
1065
- * @param {number} B Blue [0, 255]
1059
+ * @param {number} r Red component [0, 1]
1060
+ * @param {number} g Green [0, 1]
1061
+ * @param {number} b Blue [0, 1]
1066
1062
  * @return {CP.HWB} {h,w,b} object with [0, 1] ranged values
1067
1063
  */
1068
- function rgbToHwb(R, G, B) {
1069
- const r = R / 255;
1070
- const g = G / 255;
1071
- const b = B / 255;
1072
-
1064
+ function rgbToHwb(r, g, b) {
1073
1065
  let f = 0;
1074
1066
  let i = 0;
1075
1067
  const whiteness = Math.min(r, g, b);
@@ -1099,20 +1091,18 @@
1099
1091
  * @param {number} H Hue Angle [0, 1]
1100
1092
  * @param {number} W Whiteness [0, 1]
1101
1093
  * @param {number} B Blackness [0, 1]
1102
- * @return {CP.RGB} {r,g,b} object with [0, 255] ranged values
1094
+ * @return {CP.RGB} {r,g,b} object with [0, 1] ranged values
1103
1095
  *
1104
1096
  * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
1105
1097
  * @link http://alvyray.com/Papers/CG/hwb2rgb.htm
1106
1098
  */
1107
1099
  function hwbToRgb(H, W, B) {
1108
1100
  if (W + B >= 1) {
1109
- const gray = (W / (W + B)) * 255;
1101
+ const gray = W / (W + B);
1110
1102
  return { r: gray, g: gray, b: gray };
1111
1103
  }
1112
1104
  let { r, g, b } = hslToRgb(H, 1, 0.5);
1113
- [r, g, b] = [r, g, b]
1114
- .map((v) => (v / 255) * (1 - W - B) + W)
1115
- .map((v) => v * 255);
1105
+ [r, g, b] = [r, g, b].map((v) => v * (1 - W - B) + W);
1116
1106
 
1117
1107
  return { r, g, b };
1118
1108
  }
@@ -1120,15 +1110,12 @@
1120
1110
  /**
1121
1111
  * Converts an RGB colour value to HSV.
1122
1112
  *
1123
- * @param {number} R Red component [0, 255]
1124
- * @param {number} G Green [0, 255]
1125
- * @param {number} B Blue [0, 255]
1113
+ * @param {number} r Red component [0, 1]
1114
+ * @param {number} g Green [0, 1]
1115
+ * @param {number} b Blue [0, 1]
1126
1116
  * @returns {CP.HSV} {h,s,v} object with [0, 1] ranged values
1127
1117
  */
1128
- function rgbToHsv(R, G, B) {
1129
- const r = R / 255;
1130
- const g = G / 255;
1131
- const b = B / 255;
1118
+ function rgbToHsv(r, g, b) {
1132
1119
  const max = Math.max(r, g, b);
1133
1120
  const min = Math.min(r, g, b);
1134
1121
  let h = 0;
@@ -1138,17 +1125,10 @@
1138
1125
  if (max === min) {
1139
1126
  h = 0; // achromatic
1140
1127
  } else {
1141
- switch (max) {
1142
- case r:
1143
- h = (g - b) / d + (g < b ? 6 : 0);
1144
- break;
1145
- case g:
1146
- h = (b - r) / d + 2;
1147
- break;
1148
- case b:
1149
- h = (r - g) / d + 4;
1150
- break;
1151
- }
1128
+ if (r === max) h = (g - b) / d + (g < b ? 6 : 0);
1129
+ if (g === max) h = (b - r) / d + 2;
1130
+ if (b === max) h = (r - g) / d + 4;
1131
+
1152
1132
  h /= 6;
1153
1133
  }
1154
1134
  return { h, s, v };
@@ -1172,10 +1152,9 @@
1172
1152
  const q = v * (1 - f * s);
1173
1153
  const t = v * (1 - (1 - f) * s);
1174
1154
  const mod = i % 6;
1175
- let r = [v, q, p, p, t, v][mod];
1176
- let g = [t, v, v, q, p, p][mod];
1177
- let b = [p, p, t, v, v, q][mod];
1178
- [r, g, b] = [r, g, b].map((n) => n * 255);
1155
+ const r = [v, q, p, p, t, v][mod];
1156
+ const g = [t, v, v, q, p, p][mod];
1157
+ const b = [p, p, t, v, v, q][mod];
1179
1158
  return { r, g, b };
1180
1159
  }
1181
1160
 
@@ -1243,15 +1222,15 @@
1243
1222
  */
1244
1223
  function stringInputToObject(input) {
1245
1224
  let color = toLowerCase(input.trim());
1225
+
1246
1226
  if (color.length === 0) {
1247
1227
  return {
1248
1228
  r: 0, g: 0, b: 0, a: 1,
1249
1229
  };
1250
1230
  }
1251
- let named = false;
1231
+
1252
1232
  if (isColorName(color)) {
1253
1233
  color = getRGBFromName(color);
1254
- named = true;
1255
1234
  } else if (nonColors.includes(color)) {
1256
1235
  const a = color === 'transparent' ? 0 : 1;
1257
1236
  return {
@@ -1270,24 +1249,28 @@
1270
1249
  r: m1, g: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'rgb',
1271
1250
  };
1272
1251
  }
1252
+
1273
1253
  [, m1, m2, m3, m4] = matchers.hsl.exec(color) || [];
1274
1254
  if (m1 && m2 && m3/* && m4 */) {
1275
1255
  return {
1276
1256
  h: m1, s: m2, l: m3, a: m4 !== undefined ? m4 : 1, format: 'hsl',
1277
1257
  };
1278
1258
  }
1259
+
1279
1260
  [, m1, m2, m3, m4] = matchers.hsv.exec(color) || [];
1280
1261
  if (m1 && m2 && m3/* && m4 */) {
1281
1262
  return {
1282
1263
  h: m1, s: m2, v: m3, a: m4 !== undefined ? m4 : 1, format: 'hsv',
1283
1264
  };
1284
1265
  }
1266
+
1285
1267
  [, m1, m2, m3, m4] = matchers.hwb.exec(color) || [];
1286
1268
  if (m1 && m2 && m3) {
1287
1269
  return {
1288
1270
  h: m1, w: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'hwb',
1289
1271
  };
1290
1272
  }
1273
+
1291
1274
  [, m1, m2, m3, m4] = matchers.hex8.exec(color) || [];
1292
1275
  if (m1 && m2 && m3 && m4) {
1293
1276
  return {
@@ -1295,18 +1278,20 @@
1295
1278
  g: parseIntFromHex(m2),
1296
1279
  b: parseIntFromHex(m3),
1297
1280
  a: convertHexToDecimal(m4),
1298
- format: named ? 'rgb' : 'hex',
1281
+ format: 'hex',
1299
1282
  };
1300
1283
  }
1284
+
1301
1285
  [, m1, m2, m3] = matchers.hex6.exec(color) || [];
1302
1286
  if (m1 && m2 && m3) {
1303
1287
  return {
1304
1288
  r: parseIntFromHex(m1),
1305
1289
  g: parseIntFromHex(m2),
1306
1290
  b: parseIntFromHex(m3),
1307
- format: named ? 'rgb' : 'hex',
1291
+ format: 'hex',
1308
1292
  };
1309
1293
  }
1294
+
1310
1295
  [, m1, m2, m3, m4] = matchers.hex4.exec(color) || [];
1311
1296
  if (m1 && m2 && m3 && m4) {
1312
1297
  return {
@@ -1314,19 +1299,20 @@
1314
1299
  g: parseIntFromHex(m2 + m2),
1315
1300
  b: parseIntFromHex(m3 + m3),
1316
1301
  a: convertHexToDecimal(m4 + m4),
1317
- // format: named ? 'rgb' : 'hex8',
1318
- format: named ? 'rgb' : 'hex',
1302
+ format: 'hex',
1319
1303
  };
1320
1304
  }
1305
+
1321
1306
  [, m1, m2, m3] = matchers.hex3.exec(color) || [];
1322
1307
  if (m1 && m2 && m3) {
1323
1308
  return {
1324
1309
  r: parseIntFromHex(m1 + m1),
1325
1310
  g: parseIntFromHex(m2 + m2),
1326
1311
  b: parseIntFromHex(m3 + m3),
1327
- format: named ? 'rgb' : 'hex',
1312
+ format: 'hex',
1328
1313
  };
1329
1314
  }
1315
+
1330
1316
  return false;
1331
1317
  }
1332
1318
 
@@ -1357,6 +1343,7 @@
1357
1343
  */
1358
1344
  function inputToRGB(input) {
1359
1345
  let rgb = { r: 0, g: 0, b: 0 };
1346
+ /** @type {*} */
1360
1347
  let color = input;
1361
1348
  /** @type {string | number} */
1362
1349
  let a = 1;
@@ -1373,39 +1360,41 @@
1373
1360
  let format = inputFormat && COLOR_FORMAT.includes(inputFormat) ? inputFormat : 'rgb';
1374
1361
 
1375
1362
  if (typeof input === 'string') {
1376
- // @ts-ignore -- this now is converted to object
1377
1363
  color = stringInputToObject(input);
1378
1364
  if (color) ok = true;
1379
1365
  }
1380
1366
  if (typeof color === 'object') {
1381
1367
  if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
1382
1368
  ({ r, g, b } = color);
1383
- // RGB values now are all in [0, 255] range
1384
- [r, g, b] = [r, g, b].map((n) => bound01(n, isPercentage(n) ? 100 : 255) * 255);
1369
+ // RGB values now are all in [0, 1] range
1370
+ [r, g, b] = [r, g, b].map((n) => bound01(n, isPercentage(n) ? 100 : 255));
1385
1371
  rgb = { r, g, b };
1386
1372
  ok = true;
1387
- format = 'rgb';
1388
- } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
1373
+ format = color.format || 'rgb';
1374
+ }
1375
+ if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
1389
1376
  ({ h, s, v } = color);
1390
- h = typeof h === 'number' ? h : bound01(h, 360); // hue can be `5deg` or a [0, 1] value
1391
- s = typeof s === 'number' ? s : bound01(s, 100); // saturation can be `5%` or a [0, 1] value
1392
- v = typeof v === 'number' ? v : bound01(v, 100); // brightness can be `5%` or a [0, 1] value
1377
+ h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
1378
+ s = bound01(s, 100); // saturation can be `5%` or a [0, 1] value
1379
+ v = bound01(v, 100); // brightness can be `5%` or a [0, 1] value
1393
1380
  rgb = hsvToRgb(h, s, v);
1394
1381
  ok = true;
1395
1382
  format = 'hsv';
1396
- } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
1383
+ }
1384
+ if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
1397
1385
  ({ h, s, l } = color);
1398
- h = typeof h === 'number' ? h : bound01(h, 360); // hue can be `5deg` or a [0, 1] value
1399
- s = typeof s === 'number' ? s : bound01(s, 100); // saturation can be `5%` or a [0, 1] value
1400
- l = typeof l === 'number' ? l : bound01(l, 100); // lightness can be `5%` or a [0, 1] value
1386
+ h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
1387
+ s = bound01(s, 100); // saturation can be `5%` or a [0, 1] value
1388
+ l = bound01(l, 100); // lightness can be `5%` or a [0, 1] value
1401
1389
  rgb = hslToRgb(h, s, l);
1402
1390
  ok = true;
1403
1391
  format = 'hsl';
1404
- } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.w) && isValidCSSUnit(color.b)) {
1392
+ }
1393
+ if (isValidCSSUnit(color.h) && isValidCSSUnit(color.w) && isValidCSSUnit(color.b)) {
1405
1394
  ({ h, w, b } = color);
1406
- h = typeof h === 'number' ? h : bound01(h, 360); // hue can be `5deg` or a [0, 1] value
1407
- w = typeof w === 'number' ? w : bound01(w, 100); // whiteness can be `5%` or a [0, 1] value
1408
- b = typeof b === 'number' ? b : bound01(b, 100); // blackness can be `5%` or a [0, 1] value
1395
+ h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
1396
+ w = bound01(w, 100); // whiteness can be `5%` or a [0, 1] value
1397
+ b = bound01(b, 100); // blackness can be `5%` or a [0, 1] value
1409
1398
  rgb = hwbToRgb(h, w, b);
1410
1399
  ok = true;
1411
1400
  format = 'hwb';
@@ -1422,9 +1411,12 @@
1422
1411
  return {
1423
1412
  ok,
1424
1413
  format,
1425
- r: Math.min(255, Math.max(rgb.r, 0)),
1426
- g: Math.min(255, Math.max(rgb.g, 0)),
1427
- b: Math.min(255, Math.max(rgb.b, 0)),
1414
+ // r: Math.min(255, Math.max(rgb.r, 0)),
1415
+ // g: Math.min(255, Math.max(rgb.g, 0)),
1416
+ // b: Math.min(255, Math.max(rgb.b, 0)),
1417
+ r: rgb.r,
1418
+ g: rgb.g,
1419
+ b: rgb.b,
1428
1420
  a: boundAlpha(a),
1429
1421
  };
1430
1422
  }
@@ -1443,16 +1435,13 @@
1443
1435
  constructor(input, config) {
1444
1436
  let color = input;
1445
1437
  const configFormat = config && COLOR_FORMAT.includes(config)
1446
- ? config : 'rgb';
1438
+ ? config : '';
1447
1439
 
1448
- // If input is already a `Color`, return itself
1440
+ // If input is already a `Color`, clone its values
1449
1441
  if (color instanceof Color) {
1450
1442
  color = inputToRGB(color);
1451
1443
  }
1452
- if (typeof color === 'number') {
1453
- const len = `${color}`.length;
1454
- color = `#${(len === 2 ? '0' : '00')}${color}`;
1455
- }
1444
+
1456
1445
  const {
1457
1446
  r, g, b, a, ok, format,
1458
1447
  } = inputToRGB(color);
@@ -1502,24 +1491,21 @@
1502
1491
  let R = 0;
1503
1492
  let G = 0;
1504
1493
  let B = 0;
1505
- const rp = r / 255;
1506
- const rg = g / 255;
1507
- const rb = b / 255;
1508
1494
 
1509
- if (rp <= 0.03928) {
1510
- R = rp / 12.92;
1495
+ if (r <= 0.03928) {
1496
+ R = r / 12.92;
1511
1497
  } else {
1512
- R = ((rp + 0.055) / 1.055) ** 2.4;
1498
+ R = ((r + 0.055) / 1.055) ** 2.4;
1513
1499
  }
1514
- if (rg <= 0.03928) {
1515
- G = rg / 12.92;
1500
+ if (g <= 0.03928) {
1501
+ G = g / 12.92;
1516
1502
  } else {
1517
- G = ((rg + 0.055) / 1.055) ** 2.4;
1503
+ G = ((g + 0.055) / 1.055) ** 2.4;
1518
1504
  }
1519
- if (rb <= 0.03928) {
1520
- B = rb / 12.92;
1505
+ if (b <= 0.03928) {
1506
+ B = b / 12.92;
1521
1507
  } else {
1522
- B = ((rb + 0.055) / 1.055) ** 2.4;
1508
+ B = ((b + 0.055) / 1.055) ** 2.4;
1523
1509
  }
1524
1510
  return 0.2126 * R + 0.7152 * G + 0.0722 * B;
1525
1511
  }
@@ -1529,7 +1515,7 @@
1529
1515
  * @returns {number} a number in the [0, 255] range
1530
1516
  */
1531
1517
  get brightness() {
1532
- const { r, g, b } = this;
1518
+ const { r, g, b } = this.toRgb();
1533
1519
  return (r * 299 + g * 587 + b * 114) / 1000;
1534
1520
  }
1535
1521
 
@@ -1538,12 +1524,14 @@
1538
1524
  * @returns {CP.RGBA} an {r,g,b,a} object with [0, 255] ranged values
1539
1525
  */
1540
1526
  toRgb() {
1541
- const {
1527
+ let {
1542
1528
  r, g, b, a,
1543
1529
  } = this;
1544
1530
 
1531
+ [r, g, b] = [r, g, b].map((n) => roundPart(n * 255 * 100) / 100);
1532
+ a = roundPart(a * 100) / 100;
1545
1533
  return {
1546
- r, g, b, a: roundPart(a * 100) / 100,
1534
+ r, g, b, a,
1547
1535
  };
1548
1536
  }
1549
1537
 
@@ -1637,7 +1625,7 @@
1637
1625
  toHsv() {
1638
1626
  const {
1639
1627
  r, g, b, a,
1640
- } = this.toRgb();
1628
+ } = this;
1641
1629
  const { h, s, v } = rgbToHsv(r, g, b);
1642
1630
 
1643
1631
  return {
@@ -1652,7 +1640,7 @@
1652
1640
  toHsl() {
1653
1641
  const {
1654
1642
  r, g, b, a,
1655
- } = this.toRgb();
1643
+ } = this;
1656
1644
  const { h, s, l } = rgbToHsl(r, g, b);
1657
1645
 
1658
1646
  return {
@@ -1737,6 +1725,7 @@
1737
1725
  */
1738
1726
  setAlpha(alpha) {
1739
1727
  const self = this;
1728
+ if (typeof alpha !== 'number') return self;
1740
1729
  self.a = boundAlpha(alpha);
1741
1730
  return self;
1742
1731
  }
@@ -1903,26 +1892,23 @@
1903
1892
  } else if (args.length === 2) {
1904
1893
  [hueSteps, lightSteps] = args;
1905
1894
  if ([hueSteps, lightSteps].some((n) => n < 1)) {
1906
- throw TypeError('ColorPalette: when 2 arguments used, both must be larger than 0.');
1895
+ throw TypeError('ColorPalette: both arguments must be higher than 0.');
1907
1896
  }
1908
- } else {
1909
- throw TypeError('ColorPalette requires minimum 2 arguments');
1910
1897
  }
1911
1898
 
1912
- /** @type {Color[]} */
1899
+ /** @type {*} */
1913
1900
  const colors = [];
1914
-
1915
1901
  const hueStep = 360 / hueSteps;
1916
1902
  const half = roundPart((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
1917
- const estimatedStep = 100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100;
1903
+ const steps1To13 = [0.25, 0.2, 0.15, 0.11, 0.09, 0.075];
1904
+ const lightSets = [[1, 2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]];
1905
+ const closestSet = lightSets.find((set) => set.includes(lightSteps));
1918
1906
 
1919
- let lightStep = 0.25;
1920
- lightStep = [4, 5].includes(lightSteps) ? 0.2 : lightStep;
1921
- lightStep = [6, 7].includes(lightSteps) ? 0.15 : lightStep;
1922
- lightStep = [8, 9].includes(lightSteps) ? 0.11 : lightStep;
1923
- lightStep = [10, 11].includes(lightSteps) ? 0.09 : lightStep;
1924
- lightStep = [12, 13].includes(lightSteps) ? 0.075 : lightStep;
1925
- lightStep = lightSteps > 13 ? estimatedStep : lightStep;
1907
+ // find a lightStep that won't go beyond black and white
1908
+ // something within the [10-90] range of lightness
1909
+ const lightStep = closestSet
1910
+ ? steps1To13[lightSets.indexOf(closestSet)]
1911
+ : (100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100);
1926
1912
 
1927
1913
  // light tints
1928
1914
  for (let i = 1; i < half + 1; i += 1) {
@@ -2437,7 +2423,7 @@
2437
2423
  setAttribute(input, tabIndex, '-1');
2438
2424
  }
2439
2425
 
2440
- var version = "0.0.2alpha1";
2426
+ var version = "0.0.2alpha2";
2441
2427
 
2442
2428
  // @ts-ignore
2443
2429
 
@@ -2480,8 +2466,6 @@
2480
2466
  fn(input, focusinEvent, self.showPicker);
2481
2467
  fn(pickerToggle, mouseclickEvent, self.togglePicker);
2482
2468
 
2483
- fn(input, keydownEvent, self.keyToggle);
2484
-
2485
2469
  if (menuToggle) {
2486
2470
  fn(menuToggle, mouseclickEvent, self.toggleMenu);
2487
2471
  }
@@ -2519,8 +2503,7 @@
2519
2503
  fn(doc, pointerEvents.move, self.pointerMove);
2520
2504
  fn(doc, pointerEvents.up, self.pointerUp);
2521
2505
  fn(parent, focusoutEvent, self.handleFocusOut);
2522
- // @ts-ignore -- this is `Window`
2523
- fn(win, keyupEvent, self.handleDismiss);
2506
+ fn(doc, keyupEvent, self.handleDismiss);
2524
2507
  }
2525
2508
 
2526
2509
  /**
@@ -2604,7 +2587,7 @@
2604
2587
  const input = querySelector(target);
2605
2588
 
2606
2589
  // invalidate
2607
- if (!input) throw new TypeError(`ColorPicker target ${target} cannot be found.`);
2590
+ if (!input) throw new TypeError(`ColorPicker target "${target}" cannot be found.`);
2608
2591
  self.input = input;
2609
2592
 
2610
2593
  const parent = closest(input, colorPickerParentSelector);
@@ -2651,15 +2634,14 @@
2651
2634
  });
2652
2635
 
2653
2636
  // update and expose component labels
2654
- const tempLabels = ObjectAssign({}, colorPickerLabels);
2655
- const jsonLabels = componentLabels && isValidJSON(componentLabels)
2656
- ? JSON.parse(componentLabels) : componentLabels || {};
2637
+ const tempComponentLabels = componentLabels && isValidJSON(componentLabels)
2638
+ ? JSON.parse(componentLabels) : componentLabels;
2657
2639
 
2658
2640
  /** @type {Record<string, string>} */
2659
- self.componentLabels = ObjectAssign(tempLabels, jsonLabels);
2641
+ self.componentLabels = ObjectAssign(colorPickerLabels, tempComponentLabels);
2660
2642
 
2661
2643
  /** @type {Color} */
2662
- self.color = new Color('white', format);
2644
+ self.color = new Color(input.value || '#fff', format);
2663
2645
 
2664
2646
  /** @type {CP.ColorFormats} */
2665
2647
  self.format = format;
@@ -2668,7 +2650,7 @@
2668
2650
  if (colorKeywords instanceof Array) {
2669
2651
  self.colorKeywords = colorKeywords;
2670
2652
  } else if (typeof colorKeywords === 'string' && colorKeywords.length) {
2671
- self.colorKeywords = colorKeywords.split(',');
2653
+ self.colorKeywords = colorKeywords.split(',').map((x) => x.trim());
2672
2654
  }
2673
2655
 
2674
2656
  // set colour presets
@@ -2697,7 +2679,6 @@
2697
2679
  self.handleFocusOut = self.handleFocusOut.bind(self);
2698
2680
  self.changeHandler = self.changeHandler.bind(self);
2699
2681
  self.handleDismiss = self.handleDismiss.bind(self);
2700
- self.keyToggle = self.keyToggle.bind(self);
2701
2682
  self.handleKnobs = self.handleKnobs.bind(self);
2702
2683
 
2703
2684
  // generate markup
@@ -2789,76 +2770,83 @@
2789
2770
  return inputValue !== '' && new Color(inputValue).isValid;
2790
2771
  }
2791
2772
 
2773
+ /** Returns the colour appearance, usually the closest colour name for the current value. */
2774
+ get appearance() {
2775
+ const {
2776
+ colorLabels, hsl, hsv, format,
2777
+ } = this;
2778
+
2779
+ const hue = roundPart(hsl.h * 360);
2780
+ const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
2781
+ const saturation = roundPart(saturationSource * 100);
2782
+ const lightness = roundPart(hsl.l * 100);
2783
+ const hsvl = hsv.v * 100;
2784
+
2785
+ let colorName;
2786
+
2787
+ // determine color appearance
2788
+ if (lightness === 100 && saturation === 0) {
2789
+ colorName = colorLabels.white;
2790
+ } else if (lightness === 0) {
2791
+ colorName = colorLabels.black;
2792
+ } else if (saturation === 0) {
2793
+ colorName = colorLabels.grey;
2794
+ } else if (hue < 15 || hue >= 345) {
2795
+ colorName = colorLabels.red;
2796
+ } else if (hue >= 15 && hue < 45) {
2797
+ colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
2798
+ } else if (hue >= 45 && hue < 75) {
2799
+ const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
2800
+ const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
2801
+ colorName = isGold ? colorLabels.gold : colorLabels.yellow;
2802
+ colorName = isOlive ? colorLabels.olive : colorName;
2803
+ } else if (hue >= 75 && hue < 155) {
2804
+ colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
2805
+ } else if (hue >= 155 && hue < 175) {
2806
+ colorName = colorLabels.teal;
2807
+ } else if (hue >= 175 && hue < 195) {
2808
+ colorName = colorLabels.cyan;
2809
+ } else if (hue >= 195 && hue < 255) {
2810
+ colorName = colorLabels.blue;
2811
+ } else if (hue >= 255 && hue < 270) {
2812
+ colorName = colorLabels.violet;
2813
+ } else if (hue >= 270 && hue < 295) {
2814
+ colorName = colorLabels.magenta;
2815
+ } else if (hue >= 295 && hue < 345) {
2816
+ colorName = colorLabels.pink;
2817
+ }
2818
+ return colorName;
2819
+ }
2820
+
2792
2821
  /** Updates `ColorPicker` visuals. */
2793
2822
  updateVisuals() {
2794
2823
  const self = this;
2795
2824
  const {
2796
- format, controlPositions, visuals,
2825
+ controlPositions, visuals,
2797
2826
  } = self;
2798
2827
  const [v1, v2, v3] = visuals;
2799
- const { offsetWidth, offsetHeight } = v1;
2800
- const hue = format === 'hsl'
2801
- ? controlPositions.c1x / offsetWidth
2802
- : controlPositions.c2y / offsetHeight;
2803
- // @ts-ignore - `hslToRgb` is assigned to `Color` as static method
2804
- const { r, g, b } = Color.hslToRgb(hue, 1, 0.5);
2828
+ const { offsetHeight } = v1;
2829
+ const hue = controlPositions.c2y / offsetHeight;
2830
+ const { r, g, b } = new Color({ h: hue, s: 1, l: 0.5 }).toRgb();
2805
2831
  const whiteGrad = 'linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)';
2806
2832
  const alpha = 1 - controlPositions.c3y / offsetHeight;
2807
2833
  const roundA = roundPart((alpha * 100)) / 100;
2808
2834
 
2809
- if (format !== 'hsl') {
2810
- const fill = new Color({
2811
- h: hue, s: 1, l: 0.5, a: alpha,
2812
- }).toRgbString();
2813
- const hueGradient = `linear-gradient(
2814
- rgb(255,0,0) 0%, rgb(255,255,0) 16.67%,
2815
- rgb(0,255,0) 33.33%, rgb(0,255,255) 50%,
2816
- rgb(0,0,255) 66.67%, rgb(255,0,255) 83.33%,
2817
- rgb(255,0,0) 100%)`;
2818
- setElementStyle(v1, {
2819
- background: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,${roundA}) 100%),
2820
- linear-gradient(to right, rgba(255,255,255,${roundA}) 0%, ${fill} 100%),
2821
- ${whiteGrad}`,
2822
- });
2823
- setElementStyle(v2, { background: hueGradient });
2824
- } else {
2825
- const saturation = roundPart((controlPositions.c2y / offsetHeight) * 100);
2826
- const fill0 = new Color({
2827
- r: 255, g: 0, b: 0, a: alpha,
2828
- }).saturate(-saturation).toRgbString();
2829
- const fill1 = new Color({
2830
- r: 255, g: 255, b: 0, a: alpha,
2831
- }).saturate(-saturation).toRgbString();
2832
- const fill2 = new Color({
2833
- r: 0, g: 255, b: 0, a: alpha,
2834
- }).saturate(-saturation).toRgbString();
2835
- const fill3 = new Color({
2836
- r: 0, g: 255, b: 255, a: alpha,
2837
- }).saturate(-saturation).toRgbString();
2838
- const fill4 = new Color({
2839
- r: 0, g: 0, b: 255, a: alpha,
2840
- }).saturate(-saturation).toRgbString();
2841
- const fill5 = new Color({
2842
- r: 255, g: 0, b: 255, a: alpha,
2843
- }).saturate(-saturation).toRgbString();
2844
- const fill6 = new Color({
2845
- r: 255, g: 0, b: 0, a: alpha,
2846
- }).saturate(-saturation).toRgbString();
2847
- const fillGradient = `linear-gradient(to right,
2848
- ${fill0} 0%, ${fill1} 16.67%, ${fill2} 33.33%, ${fill3} 50%,
2849
- ${fill4} 66.67%, ${fill5} 83.33%, ${fill6} 100%)`;
2850
- const lightGrad = `linear-gradient(rgba(255,255,255,${roundA}) 0%, rgba(255,255,255,0) 50%),
2851
- linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,${roundA}) 100%)`;
2852
-
2853
- setElementStyle(v1, { background: `${lightGrad},${fillGradient},${whiteGrad}` });
2854
- const {
2855
- r: gr, g: gg, b: gb,
2856
- } = new Color({ r, g, b }).greyscale().toRgb();
2835
+ const fill = new Color({
2836
+ h: hue, s: 1, l: 0.5, a: alpha,
2837
+ }).toRgbString();
2838
+ const hueGradient = `linear-gradient(
2839
+ rgb(255,0,0) 0%, rgb(255,255,0) 16.67%,
2840
+ rgb(0,255,0) 33.33%, rgb(0,255,255) 50%,
2841
+ rgb(0,0,255) 66.67%, rgb(255,0,255) 83.33%,
2842
+ rgb(255,0,0) 100%)`;
2843
+ setElementStyle(v1, {
2844
+ background: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,${roundA}) 100%),
2845
+ linear-gradient(to right, rgba(255,255,255,${roundA}) 0%, ${fill} 100%),
2846
+ ${whiteGrad}`,
2847
+ });
2848
+ setElementStyle(v2, { background: hueGradient });
2857
2849
 
2858
- setElementStyle(v2, {
2859
- background: `linear-gradient(rgb(${r},${g},${b}) 0%, rgb(${gr},${gg},${gb}) 100%)`,
2860
- });
2861
- }
2862
2850
  setElementStyle(v3, {
2863
2851
  background: `linear-gradient(rgba(${r},${g},${b},1) 0%,rgba(${r},${g},${b},0) 100%)`,
2864
2852
  });
@@ -3008,13 +2996,13 @@
3008
2996
  const [v1, v2, v3] = visuals;
3009
2997
  const [c1, c2, c3] = controlKnobs;
3010
2998
  /** @type {HTMLElement} */
3011
- const visual = hasClass(target, 'visual-control')
3012
- ? target : querySelector('.visual-control', target.parentElement);
2999
+ const visual = controlKnobs.includes(target) ? target.previousElementSibling : target;
3013
3000
  const visualRect = getBoundingClientRect(visual);
3001
+ const html = getDocumentElement(v1);
3014
3002
  const X = type === 'touchstart' ? touches[0].pageX : pageX;
3015
3003
  const Y = type === 'touchstart' ? touches[0].pageY : pageY;
3016
- const offsetX = X - window.pageXOffset - visualRect.left;
3017
- const offsetY = Y - window.pageYOffset - visualRect.top;
3004
+ const offsetX = X - html.scrollLeft - visualRect.left;
3005
+ const offsetY = Y - html.scrollTop - visualRect.top;
3018
3006
 
3019
3007
  if (target === v1 || target === c1) {
3020
3008
  self.dragElement = visual;
@@ -3074,10 +3062,11 @@
3074
3062
  if (!dragElement) return;
3075
3063
 
3076
3064
  const controlRect = getBoundingClientRect(dragElement);
3077
- const X = type === 'touchmove' ? touches[0].pageX : pageX;
3078
- const Y = type === 'touchmove' ? touches[0].pageY : pageY;
3079
- const offsetX = X - window.pageXOffset - controlRect.left;
3080
- const offsetY = Y - window.pageYOffset - controlRect.top;
3065
+ const win = getDocumentElement(v1);
3066
+ const X = type === touchmoveEvent ? touches[0].pageX : pageX;
3067
+ const Y = type === touchmoveEvent ? touches[0].pageY : pageY;
3068
+ const offsetX = X - win.scrollLeft - controlRect.left;
3069
+ const offsetY = Y - win.scrollTop - controlRect.top;
3081
3070
 
3082
3071
  if (dragElement === v1) {
3083
3072
  self.changeControl1(offsetX, offsetY);
@@ -3104,19 +3093,19 @@
3104
3093
  if (![keyArrowUp, keyArrowDown, keyArrowLeft, keyArrowRight].includes(code)) return;
3105
3094
  e.preventDefault();
3106
3095
 
3107
- const { format, controlKnobs, visuals } = self;
3096
+ const { controlKnobs, visuals } = self;
3108
3097
  const { offsetWidth, offsetHeight } = visuals[0];
3109
3098
  const [c1, c2, c3] = controlKnobs;
3110
3099
  const { activeElement } = getDocument(c1);
3111
3100
  const currentKnob = controlKnobs.find((x) => x === activeElement);
3112
- const yRatio = offsetHeight / (format === 'hsl' ? 100 : 360);
3101
+ const yRatio = offsetHeight / 360;
3113
3102
 
3114
3103
  if (currentKnob) {
3115
3104
  let offsetX = 0;
3116
3105
  let offsetY = 0;
3117
3106
 
3118
3107
  if (target === c1) {
3119
- const xRatio = offsetWidth / (format === 'hsl' ? 360 : 100);
3108
+ const xRatio = offsetWidth / 100;
3120
3109
 
3121
3110
  if ([keyArrowLeft, keyArrowRight].includes(code)) {
3122
3111
  self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
@@ -3166,7 +3155,7 @@
3166
3155
  if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
3167
3156
  if (activeElement === input) {
3168
3157
  if (isNonColorValue) {
3169
- colorSource = 'white';
3158
+ colorSource = currentValue === 'transparent' ? 'rgba(0,0,0,0)' : 'rgb(0,0,0)';
3170
3159
  } else {
3171
3160
  colorSource = currentValue;
3172
3161
  }
@@ -3217,9 +3206,7 @@
3217
3206
  changeControl1(X, Y) {
3218
3207
  const self = this;
3219
3208
  let [offsetX, offsetY] = [0, 0];
3220
- const {
3221
- format, controlPositions, visuals,
3222
- } = self;
3209
+ const { controlPositions, visuals } = self;
3223
3210
  const { offsetHeight, offsetWidth } = visuals[0];
3224
3211
 
3225
3212
  if (X > offsetWidth) offsetX = offsetWidth;
@@ -3228,29 +3215,19 @@
3228
3215
  if (Y > offsetHeight) offsetY = offsetHeight;
3229
3216
  else if (Y >= 0) offsetY = Y;
3230
3217
 
3231
- const hue = format === 'hsl'
3232
- ? offsetX / offsetWidth
3233
- : controlPositions.c2y / offsetHeight;
3218
+ const hue = controlPositions.c2y / offsetHeight;
3234
3219
 
3235
- const saturation = format === 'hsl'
3236
- ? 1 - controlPositions.c2y / offsetHeight
3237
- : offsetX / offsetWidth;
3220
+ const saturation = offsetX / offsetWidth;
3238
3221
 
3239
3222
  const lightness = 1 - offsetY / offsetHeight;
3240
3223
  const alpha = 1 - controlPositions.c3y / offsetHeight;
3241
3224
 
3242
- const colorObject = format === 'hsl'
3243
- ? {
3244
- h: hue, s: saturation, l: lightness, a: alpha,
3245
- }
3246
- : {
3247
- h: hue, s: saturation, v: lightness, a: alpha,
3248
- };
3249
-
3250
3225
  // new color
3251
3226
  const {
3252
3227
  r, g, b, a,
3253
- } = new Color(colorObject);
3228
+ } = new Color({
3229
+ h: hue, s: saturation, v: lightness, a: alpha,
3230
+ });
3254
3231
 
3255
3232
  ObjectAssign(self.color, {
3256
3233
  r, g, b, a,
@@ -3277,7 +3254,7 @@
3277
3254
  changeControl2(Y) {
3278
3255
  const self = this;
3279
3256
  const {
3280
- format, controlPositions, visuals,
3257
+ controlPositions, visuals,
3281
3258
  } = self;
3282
3259
  const { offsetHeight, offsetWidth } = visuals[0];
3283
3260
 
@@ -3286,26 +3263,17 @@
3286
3263
  if (Y > offsetHeight) offsetY = offsetHeight;
3287
3264
  else if (Y >= 0) offsetY = Y;
3288
3265
 
3289
- const hue = format === 'hsl'
3290
- ? controlPositions.c1x / offsetWidth
3291
- : offsetY / offsetHeight;
3292
- const saturation = format === 'hsl'
3293
- ? 1 - offsetY / offsetHeight
3294
- : controlPositions.c1x / offsetWidth;
3266
+ const hue = offsetY / offsetHeight;
3267
+ const saturation = controlPositions.c1x / offsetWidth;
3295
3268
  const lightness = 1 - controlPositions.c1y / offsetHeight;
3296
3269
  const alpha = 1 - controlPositions.c3y / offsetHeight;
3297
- const colorObject = format === 'hsl'
3298
- ? {
3299
- h: hue, s: saturation, l: lightness, a: alpha,
3300
- }
3301
- : {
3302
- h: hue, s: saturation, v: lightness, a: alpha,
3303
- };
3304
3270
 
3305
3271
  // new color
3306
3272
  const {
3307
3273
  r, g, b, a,
3308
- } = new Color(colorObject);
3274
+ } = new Color({
3275
+ h: hue, s: saturation, v: lightness, a: alpha,
3276
+ });
3309
3277
 
3310
3278
  ObjectAssign(self.color, {
3311
3279
  r, g, b, a,
@@ -3392,18 +3360,18 @@
3392
3360
  setControlPositions() {
3393
3361
  const self = this;
3394
3362
  const {
3395
- format, visuals, color, hsl, hsv,
3363
+ visuals, color, hsv,
3396
3364
  } = self;
3397
3365
  const { offsetHeight, offsetWidth } = visuals[0];
3398
3366
  const alpha = color.a;
3399
- const hue = hsl.h;
3367
+ const hue = hsv.h;
3400
3368
 
3401
- const saturation = format !== 'hsl' ? hsv.s : hsl.s;
3402
- const lightness = format !== 'hsl' ? hsv.v : hsl.l;
3369
+ const saturation = hsv.s;
3370
+ const lightness = hsv.v;
3403
3371
 
3404
- self.controlPositions.c1x = format !== 'hsl' ? saturation * offsetWidth : hue * offsetWidth;
3372
+ self.controlPositions.c1x = saturation * offsetWidth;
3405
3373
  self.controlPositions.c1y = (1 - lightness) * offsetHeight;
3406
- self.controlPositions.c2y = format !== 'hsl' ? hue * offsetHeight : (1 - saturation) * offsetHeight;
3374
+ self.controlPositions.c2y = hue * offsetHeight;
3407
3375
  self.controlPositions.c3y = (1 - alpha) * offsetHeight;
3408
3376
  }
3409
3377
 
@@ -3411,78 +3379,40 @@
3411
3379
  updateAppearance() {
3412
3380
  const self = this;
3413
3381
  const {
3414
- componentLabels, colorLabels, color, parent,
3415
- hsl, hsv, hex, format, controlKnobs,
3382
+ componentLabels, color, parent,
3383
+ hsv, hex, format, controlKnobs,
3416
3384
  } = self;
3417
3385
  const {
3418
3386
  appearanceLabel, hexLabel, valueLabel,
3419
3387
  } = componentLabels;
3420
- const { r, g, b } = color.toRgb();
3388
+ let { r, g, b } = color.toRgb();
3421
3389
  const [knob1, knob2, knob3] = controlKnobs;
3422
- const hue = roundPart(hsl.h * 360);
3390
+ const hue = roundPart(hsv.h * 360);
3423
3391
  const alpha = color.a;
3424
- const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
3425
- const saturation = roundPart(saturationSource * 100);
3426
- const lightness = roundPart(hsl.l * 100);
3427
- const hsvl = hsv.v * 100;
3428
- let colorName;
3429
-
3430
- // determine color appearance
3431
- if (lightness === 100 && saturation === 0) {
3432
- colorName = colorLabels.white;
3433
- } else if (lightness === 0) {
3434
- colorName = colorLabels.black;
3435
- } else if (saturation === 0) {
3436
- colorName = colorLabels.grey;
3437
- } else if (hue < 15 || hue >= 345) {
3438
- colorName = colorLabels.red;
3439
- } else if (hue >= 15 && hue < 45) {
3440
- colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
3441
- } else if (hue >= 45 && hue < 75) {
3442
- const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
3443
- const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
3444
- colorName = isGold ? colorLabels.gold : colorLabels.yellow;
3445
- colorName = isOlive ? colorLabels.olive : colorName;
3446
- } else if (hue >= 75 && hue < 155) {
3447
- colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
3448
- } else if (hue >= 155 && hue < 175) {
3449
- colorName = colorLabels.teal;
3450
- } else if (hue >= 175 && hue < 195) {
3451
- colorName = colorLabels.cyan;
3452
- } else if (hue >= 195 && hue < 255) {
3453
- colorName = colorLabels.blue;
3454
- } else if (hue >= 255 && hue < 270) {
3455
- colorName = colorLabels.violet;
3456
- } else if (hue >= 270 && hue < 295) {
3457
- colorName = colorLabels.magenta;
3458
- } else if (hue >= 295 && hue < 345) {
3459
- colorName = colorLabels.pink;
3460
- }
3392
+ const saturation = roundPart(hsv.s * 100);
3393
+ const lightness = roundPart(hsv.v * 100);
3394
+ const colorName = self.appearance;
3461
3395
 
3462
3396
  let colorLabel = `${hexLabel} ${hex.split('').join(' ')}`;
3463
3397
 
3464
- if (format === 'hsl') {
3465
- colorLabel = `HSL: ${hue}°, ${saturation}%, ${lightness}%`;
3466
- setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3467
- setAttribute(knob1, ariaValueText, `${hue}° & ${lightness}%`);
3468
- setAttribute(knob1, ariaValueNow, `${hue}`);
3469
- setAttribute(knob2, ariaValueText, `${saturation}%`);
3470
- setAttribute(knob2, ariaValueNow, `${saturation}`);
3471
- } else if (format === 'hwb') {
3398
+ if (format === 'hwb') {
3472
3399
  const { hwb } = self;
3473
3400
  const whiteness = roundPart(hwb.w * 100);
3474
3401
  const blackness = roundPart(hwb.b * 100);
3475
3402
  colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
3476
- setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3477
3403
  setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
3478
3404
  setAttribute(knob1, ariaValueNow, `${whiteness}`);
3405
+ setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3479
3406
  setAttribute(knob2, ariaValueText, `${hue}%`);
3480
3407
  setAttribute(knob2, ariaValueNow, `${hue}`);
3481
3408
  } else {
3409
+ [r, g, b] = [r, g, b].map(roundPart);
3410
+ colorLabel = format === 'hsl' ? `HSL: ${hue}°, ${saturation}%, ${lightness}%` : colorLabel;
3482
3411
  colorLabel = format === 'rgb' ? `RGB: ${r}, ${g}, ${b}` : colorLabel;
3483
- setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3412
+
3484
3413
  setAttribute(knob1, ariaValueText, `${lightness}% & ${saturation}%`);
3485
3414
  setAttribute(knob1, ariaValueNow, `${lightness}`);
3415
+ setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3486
3416
  setAttribute(knob2, ariaValueText, `${hue}°`);
3487
3417
  setAttribute(knob2, ariaValueNow, `${hue}`);
3488
3418
  }
@@ -3577,37 +3507,13 @@
3577
3507
  }
3578
3508
  }
3579
3509
 
3580
- /**
3581
- * The `Space` & `Enter` keys specific event listener.
3582
- * Toggle visibility of the `ColorPicker` / the presets menu, showing one will hide the other.
3583
- * @param {KeyboardEvent} e
3584
- * @this {ColorPicker}
3585
- */
3586
- keyToggle(e) {
3587
- const self = this;
3588
- const { menuToggle } = self;
3589
- const { activeElement } = getDocument(menuToggle);
3590
- const { code } = e;
3591
-
3592
- if ([keyEnter, keySpace].includes(code)) {
3593
- if ((menuToggle && activeElement === menuToggle) || !activeElement) {
3594
- e.preventDefault();
3595
- if (!activeElement) {
3596
- self.togglePicker(e);
3597
- } else {
3598
- self.toggleMenu();
3599
- }
3600
- }
3601
- }
3602
- }
3603
-
3604
3510
  /**
3605
3511
  * Toggle the `ColorPicker` dropdown visibility.
3606
- * @param {Event} e
3512
+ * @param {Event=} e
3607
3513
  * @this {ColorPicker}
3608
3514
  */
3609
3515
  togglePicker(e) {
3610
- e.preventDefault();
3516
+ if (e) e.preventDefault();
3611
3517
  const self = this;
3612
3518
  const { colorPicker } = self;
3613
3519
 
@@ -3628,8 +3534,13 @@
3628
3534
  }
3629
3535
  }
3630
3536
 
3631
- /** Toggles the visibility of the `ColorPicker` presets menu. */
3632
- toggleMenu() {
3537
+ /**
3538
+ * Toggles the visibility of the `ColorPicker` presets menu.
3539
+ * @param {Event=} e
3540
+ * @this {ColorPicker}
3541
+ */
3542
+ toggleMenu(e) {
3543
+ if (e) e.preventDefault();
3633
3544
  const self = this;
3634
3545
  const { colorMenu } = self;
3635
3546
 
@@ -3655,6 +3566,10 @@
3655
3566
  const relatedBtn = openPicker ? pickerToggle : menuToggle;
3656
3567
  const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
3657
3568
 
3569
+ // if (!self.isValid) {
3570
+ self.value = self.color.toString(true);
3571
+ // }
3572
+
3658
3573
  if (openDropdown) {
3659
3574
  removeClass(openDropdown, 'show');
3660
3575
  setAttribute(relatedBtn, ariaExpanded, 'false');
@@ -3668,9 +3583,6 @@
3668
3583
  }, animationDuration);
3669
3584
  }
3670
3585
 
3671
- if (!self.isValid) {
3672
- self.value = self.color.toString();
3673
- }
3674
3586
  if (!focusPrevented) {
3675
3587
  focus(pickerToggle);
3676
3588
  }