@thednp/color-picker 0.0.1-alpha3 → 0.0.2-alpha2
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/README.md +3 -1
- package/dist/css/color-picker.css +3 -2
- package/dist/css/color-picker.min.css +2 -2
- package/dist/css/color-picker.rtl.css +3 -2
- package/dist/css/color-picker.rtl.min.css +2 -2
- package/dist/js/color-esm.js +1167 -0
- package/dist/js/color-esm.min.js +2 -0
- package/dist/js/color-palette-esm.js +1238 -0
- package/dist/js/color-palette-esm.min.js +2 -0
- package/dist/js/color-palette.js +1246 -0
- package/dist/js/color-palette.min.js +2 -0
- package/dist/js/color-picker-element-esm.js +567 -683
- package/dist/js/color-picker-element-esm.min.js +2 -2
- package/dist/js/color-picker-element.js +569 -685
- package/dist/js/color-picker-element.min.js +2 -2
- package/dist/js/color-picker-esm.js +782 -890
- package/dist/js/color-picker-esm.min.js +2 -2
- package/dist/js/color-picker.js +784 -892
- package/dist/js/color-picker.min.js +2 -2
- package/dist/js/color.js +1175 -0
- package/dist/js/color.min.js +2 -0
- package/package.json +22 -3
- package/src/js/color-palette.js +18 -14
- package/src/js/color-picker-element.js +47 -55
- package/src/js/color-picker.js +155 -329
- package/src/js/color.js +175 -193
- package/src/js/util/getColorMenu.js +12 -7
- package/src/js/util/setMarkup.js +122 -0
- package/src/js/util/version.js +6 -0
- package/src/scss/color-picker.scss +3 -7
- package/types/cp.d.ts +64 -32
- package/types/source/types.d.ts +1 -1
- package/src/js/util/templates.js +0 -10
package/dist/js/color-picker.js
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
/*!
|
2
|
-
* ColorPicker v0.0.
|
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
|
*/
|
6
6
|
(function (global, factory) {
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
8
8
|
typeof define === 'function' && define.amd ? define(factory) :
|
9
|
-
(global = global || self, global.ColorPicker = factory());
|
10
|
-
}(this, (function () { 'use strict';
|
9
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ColorPicker = factory());
|
10
|
+
})(this, (function () { 'use strict';
|
11
11
|
|
12
12
|
/** @type {Record<string, any>} */
|
13
13
|
const EventRegistry = {};
|
@@ -135,24 +135,6 @@
|
|
135
135
|
*/
|
136
136
|
const ariaValueNow = 'aria-valuenow';
|
137
137
|
|
138
|
-
/**
|
139
|
-
* A global namespace for aria-haspopup.
|
140
|
-
* @type {string}
|
141
|
-
*/
|
142
|
-
const ariaHasPopup = 'aria-haspopup';
|
143
|
-
|
144
|
-
/**
|
145
|
-
* A global namespace for aria-hidden.
|
146
|
-
* @type {string}
|
147
|
-
*/
|
148
|
-
const ariaHidden = 'aria-hidden';
|
149
|
-
|
150
|
-
/**
|
151
|
-
* A global namespace for aria-labelledby.
|
152
|
-
* @type {string}
|
153
|
-
*/
|
154
|
-
const ariaLabelledBy = 'aria-labelledby';
|
155
|
-
|
156
138
|
/**
|
157
139
|
* A global namespace for `ArrowDown` key.
|
158
140
|
* @type {string} e.which = 40 equivalent
|
@@ -279,37 +261,6 @@
|
|
279
261
|
*/
|
280
262
|
const focusoutEvent = 'focusout';
|
281
263
|
|
282
|
-
// @ts-ignore
|
283
|
-
const { userAgentData: uaDATA } = navigator;
|
284
|
-
|
285
|
-
/**
|
286
|
-
* A global namespace for `userAgentData` object.
|
287
|
-
*/
|
288
|
-
const userAgentData = uaDATA;
|
289
|
-
|
290
|
-
const { userAgent: userAgentString } = navigator;
|
291
|
-
|
292
|
-
/**
|
293
|
-
* A global namespace for `navigator.userAgent` string.
|
294
|
-
*/
|
295
|
-
const userAgent = userAgentString;
|
296
|
-
|
297
|
-
const mobileBrands = /iPhone|iPad|iPod|Android/i;
|
298
|
-
let isMobileCheck = false;
|
299
|
-
|
300
|
-
if (userAgentData) {
|
301
|
-
isMobileCheck = userAgentData.brands
|
302
|
-
.some((/** @type {Record<String, any>} */x) => mobileBrands.test(x.brand));
|
303
|
-
} else {
|
304
|
-
isMobileCheck = mobileBrands.test(userAgent);
|
305
|
-
}
|
306
|
-
|
307
|
-
/**
|
308
|
-
* A global `boolean` for mobile detection.
|
309
|
-
* @type {boolean}
|
310
|
-
*/
|
311
|
-
const isMobile = isMobileCheck;
|
312
|
-
|
313
264
|
/**
|
314
265
|
* Returns the `document` or the `#document` element.
|
315
266
|
* @see https://github.com/floating-ui/floating-ui
|
@@ -530,60 +481,6 @@
|
|
530
481
|
return lookUp.getElementsByClassName(selector);
|
531
482
|
}
|
532
483
|
|
533
|
-
/**
|
534
|
-
* Shortcut for `Object.assign()` static method.
|
535
|
-
* @param {Record<string, any>} obj a target object
|
536
|
-
* @param {Record<string, any>} source a source object
|
537
|
-
*/
|
538
|
-
const ObjectAssign = (obj, source) => Object.assign(obj, source);
|
539
|
-
|
540
|
-
/**
|
541
|
-
* This is a shortie for `document.createElement` method
|
542
|
-
* which allows you to create a new `HTMLElement` for a given `tagName`
|
543
|
-
* or based on an object with specific non-readonly attributes:
|
544
|
-
* `id`, `className`, `textContent`, `style`, etc.
|
545
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
|
546
|
-
*
|
547
|
-
* @param {Record<string, string> | string} param `tagName` or object
|
548
|
-
* @return {HTMLElement | Element} a new `HTMLElement` or `Element`
|
549
|
-
*/
|
550
|
-
function createElement(param) {
|
551
|
-
if (typeof param === 'string') {
|
552
|
-
return getDocument().createElement(param);
|
553
|
-
}
|
554
|
-
|
555
|
-
const { tagName } = param;
|
556
|
-
const attr = { ...param };
|
557
|
-
const newElement = createElement(tagName);
|
558
|
-
delete attr.tagName;
|
559
|
-
ObjectAssign(newElement, attr);
|
560
|
-
return newElement;
|
561
|
-
}
|
562
|
-
|
563
|
-
/**
|
564
|
-
* This is a shortie for `document.createElementNS` method
|
565
|
-
* which allows you to create a new `HTMLElement` for a given `tagName`
|
566
|
-
* or based on an object with specific non-readonly attributes:
|
567
|
-
* `id`, `className`, `textContent`, `style`, etc.
|
568
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
|
569
|
-
*
|
570
|
-
* @param {string} namespace `namespaceURI` to associate with the new `HTMLElement`
|
571
|
-
* @param {Record<string, string> | string} param `tagName` or object
|
572
|
-
* @return {HTMLElement | Element} a new `HTMLElement` or `Element`
|
573
|
-
*/
|
574
|
-
function createElementNS(namespace, param) {
|
575
|
-
if (typeof param === 'string') {
|
576
|
-
return getDocument().createElementNS(namespace, param);
|
577
|
-
}
|
578
|
-
|
579
|
-
const { tagName } = param;
|
580
|
-
const attr = { ...param };
|
581
|
-
const newElement = createElementNS(namespace, tagName);
|
582
|
-
delete attr.tagName;
|
583
|
-
ObjectAssign(newElement, attr);
|
584
|
-
return newElement;
|
585
|
-
}
|
586
|
-
|
587
484
|
/**
|
588
485
|
* Shortcut for the `Element.dispatchEvent(Event)` method.
|
589
486
|
*
|
@@ -592,6 +489,13 @@
|
|
592
489
|
*/
|
593
490
|
const dispatchEvent = (element, event) => element.dispatchEvent(event);
|
594
491
|
|
492
|
+
/**
|
493
|
+
* Shortcut for `Object.assign()` static method.
|
494
|
+
* @param {Record<string, any>} obj a target object
|
495
|
+
* @param {Record<string, any>} source a source object
|
496
|
+
*/
|
497
|
+
const ObjectAssign = (obj, source) => Object.assign(obj, source);
|
498
|
+
|
595
499
|
/** @type {Map<string, Map<HTMLElement | Element, Record<string, any>>>} */
|
596
500
|
const componentData = new Map();
|
597
501
|
/**
|
@@ -667,20 +571,13 @@
|
|
667
571
|
*/
|
668
572
|
const getInstance = (target, component) => Data.get(target, component);
|
669
573
|
|
670
|
-
/**
|
671
|
-
* Shortcut for `Object.keys()` static method.
|
672
|
-
* @param {Record<string, any>} obj a target object
|
673
|
-
* @returns {string[]}
|
674
|
-
*/
|
675
|
-
const ObjectKeys = (obj) => Object.keys(obj);
|
676
|
-
|
677
574
|
/**
|
678
575
|
* Shortcut for multiple uses of `HTMLElement.style.propertyName` method.
|
679
576
|
* @param {HTMLElement | Element} element target element
|
680
577
|
* @param {Partial<CSSStyleDeclaration>} styles attribute value
|
681
578
|
*/
|
682
579
|
// @ts-ignore
|
683
|
-
const setElementStyle = (element, styles) => ObjectAssign(element.style, styles);
|
580
|
+
const setElementStyle = (element, styles) => { ObjectAssign(element.style, styles); };
|
684
581
|
|
685
582
|
/**
|
686
583
|
* Shortcut for `HTMLElement.getAttribute()` method.
|
@@ -723,6 +620,13 @@
|
|
723
620
|
return value;
|
724
621
|
}
|
725
622
|
|
623
|
+
/**
|
624
|
+
* Shortcut for `Object.keys()` static method.
|
625
|
+
* @param {Record<string, any>} obj a target object
|
626
|
+
* @returns {string[]}
|
627
|
+
*/
|
628
|
+
const ObjectKeys = (obj) => Object.keys(obj);
|
629
|
+
|
726
630
|
/**
|
727
631
|
* Shortcut for `String.toLowerCase()`.
|
728
632
|
*
|
@@ -843,32 +747,10 @@
|
|
843
747
|
*/
|
844
748
|
const removeAttribute = (element, attribute) => element.removeAttribute(attribute);
|
845
749
|
|
846
|
-
/** @type {Record<string, string>} */
|
847
|
-
const colorPickerLabels = {
|
848
|
-
pickerLabel: 'Colour Picker',
|
849
|
-
appearanceLabel: 'Colour Appearance',
|
850
|
-
valueLabel: 'Colour Value',
|
851
|
-
toggleLabel: 'Select Colour',
|
852
|
-
presetsLabel: 'Colour Presets',
|
853
|
-
defaultsLabel: 'Colour Defaults',
|
854
|
-
formatLabel: 'Format',
|
855
|
-
alphaLabel: 'Alpha',
|
856
|
-
hexLabel: 'Hexadecimal',
|
857
|
-
hueLabel: 'Hue',
|
858
|
-
whitenessLabel: 'Whiteness',
|
859
|
-
blacknessLabel: 'Blackness',
|
860
|
-
saturationLabel: 'Saturation',
|
861
|
-
lightnessLabel: 'Lightness',
|
862
|
-
redLabel: 'Red',
|
863
|
-
greenLabel: 'Green',
|
864
|
-
blueLabel: 'Blue',
|
865
|
-
};
|
866
|
-
|
867
750
|
/**
|
868
|
-
* A
|
869
|
-
* @type {string[]}
|
751
|
+
* A global namespace for `document.head`.
|
870
752
|
*/
|
871
|
-
const
|
753
|
+
const { head: documentHead } = document;
|
872
754
|
|
873
755
|
/**
|
874
756
|
* A list of explicit default non-color values.
|
@@ -876,297 +758,99 @@
|
|
876
758
|
const nonColors = ['transparent', 'currentColor', 'inherit', 'revert', 'initial'];
|
877
759
|
|
878
760
|
/**
|
879
|
-
*
|
880
|
-
*
|
881
|
-
* @
|
882
|
-
* @returns {string} uppercase output string
|
761
|
+
* Round colour components, for all formats except HEX.
|
762
|
+
* @param {number} v one of the colour components
|
763
|
+
* @returns {number} the rounded number
|
883
764
|
*/
|
884
|
-
|
765
|
+
function roundPart(v) {
|
766
|
+
const floor = Math.floor(v);
|
767
|
+
return v - floor < 0.5 ? floor : Math.round(v);
|
768
|
+
}
|
885
769
|
|
886
|
-
|
770
|
+
// Color supported formats
|
771
|
+
const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsv', 'hwb'];
|
887
772
|
|
888
|
-
|
889
|
-
|
890
|
-
*
|
891
|
-
* @param {CP.ColorPicker} self the `ColorPicker` instance
|
892
|
-
* @returns {HTMLElement | Element} a new `<div>` element with color component `<input>`
|
893
|
-
*/
|
894
|
-
function getColorForm(self) {
|
895
|
-
const { format, id, componentLabels } = self;
|
896
|
-
const colorForm = createElement({
|
897
|
-
tagName: 'div',
|
898
|
-
className: `color-form ${format}`,
|
899
|
-
});
|
773
|
+
// Hue angles
|
774
|
+
const ANGLES = 'deg|rad|grad|turn';
|
900
775
|
|
901
|
-
|
902
|
-
|
903
|
-
else if (format === 'hsl') components = ['hue', 'saturation', 'lightness', 'alpha'];
|
904
|
-
else if (format === 'hwb') components = ['hue', 'whiteness', 'blackness', 'alpha'];
|
776
|
+
// <http://www.w3.org/TR/css3-values/#integers>
|
777
|
+
const CSS_INTEGER = '[-\\+]?\\d+%?';
|
905
778
|
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
const formatLabel = componentLabels[`${c}Label`];
|
910
|
-
const cInputLabel = createElement({ tagName: 'label' });
|
911
|
-
setAttribute(cInputLabel, 'for', cID);
|
912
|
-
cInputLabel.append(
|
913
|
-
createElement({ tagName: 'span', ariaHidden: 'true', innerText: `${C}:` }),
|
914
|
-
createElement({ tagName: 'span', className: vHidden, innerText: formatLabel }),
|
915
|
-
);
|
916
|
-
const cInput = createElement({
|
917
|
-
tagName: 'input',
|
918
|
-
id: cID,
|
919
|
-
// name: cID, - prevent saving the value to a form
|
920
|
-
type: format === 'hex' ? 'text' : 'number',
|
921
|
-
value: c === 'alpha' ? '100' : '0',
|
922
|
-
className: `color-input ${c}`,
|
923
|
-
});
|
924
|
-
setAttribute(cInput, 'autocomplete', 'off');
|
925
|
-
setAttribute(cInput, 'spellcheck', 'false');
|
779
|
+
// Include CSS3 Module
|
780
|
+
// <http://www.w3.org/TR/css3-values/#number-value>
|
781
|
+
const CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?';
|
926
782
|
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
|
783
|
+
// Include CSS4 Module Hue degrees unit
|
784
|
+
// <https://www.w3.org/TR/css3-values/#angle-value>
|
785
|
+
const CSS_ANGLE = `[-\\+]?\\d*\\.?\\d+(?:${ANGLES})?`;
|
786
|
+
|
787
|
+
// Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
|
788
|
+
const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;
|
789
|
+
|
790
|
+
// Add angles to the mix
|
791
|
+
const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
|
792
|
+
|
793
|
+
// Start & end
|
794
|
+
const START_MATCH = '(?:[\\s|\\(\\s|\\s\\(\\s]+)?';
|
795
|
+
const END_MATCH = '(?:[\\s|\\)\\s]+)?';
|
796
|
+
// Components separation
|
797
|
+
const SEP = '(?:[,|\\s]+)';
|
798
|
+
const SEP2 = '(?:[,|\\/\\s]*)?';
|
799
|
+
|
800
|
+
// Actual matching.
|
801
|
+
// Parentheses and commas are optional, but not required.
|
802
|
+
// Whitespace can take the place of commas or opening paren
|
803
|
+
const PERMISSIVE_MATCH = `${START_MATCH}(${CSS_UNIT2})${SEP}(${CSS_UNIT})${SEP}(${CSS_UNIT})${SEP2}(${CSS_UNIT})?${END_MATCH}`;
|
804
|
+
|
805
|
+
const matchers = {
|
806
|
+
CSS_UNIT: new RegExp(CSS_UNIT2),
|
807
|
+
hwb: new RegExp(`hwb${PERMISSIVE_MATCH}`),
|
808
|
+
rgb: new RegExp(`rgb(?:a)?${PERMISSIVE_MATCH}`),
|
809
|
+
hsl: new RegExp(`hsl(?:a)?${PERMISSIVE_MATCH}`),
|
810
|
+
hsv: new RegExp(`hsv(?:a)?${PERMISSIVE_MATCH}`),
|
811
|
+
hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
812
|
+
hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
813
|
+
hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
814
|
+
hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
815
|
+
};
|
946
816
|
|
947
817
|
/**
|
948
|
-
*
|
949
|
-
*
|
818
|
+
* Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
|
819
|
+
* <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
|
820
|
+
* @param {string} n testing number
|
821
|
+
* @returns {boolean} the query result
|
950
822
|
*/
|
951
|
-
|
823
|
+
function isOnePointZero(n) {
|
824
|
+
return `${n}`.includes('.') && parseFloat(n) === 1;
|
825
|
+
}
|
952
826
|
|
953
827
|
/**
|
954
|
-
*
|
955
|
-
* @
|
828
|
+
* Check to see if string passed in is a percentage
|
829
|
+
* @param {string} n testing number
|
830
|
+
* @returns {boolean} the query result
|
956
831
|
*/
|
957
|
-
|
958
|
-
|
959
|
-
|
960
|
-
* A global namespace for aria-valuemax.
|
961
|
-
* @type {string}
|
962
|
-
*/
|
963
|
-
const ariaValueMax = 'aria-valuemax';
|
964
|
-
|
965
|
-
const tabIndex = 'tabindex';
|
966
|
-
|
967
|
-
/**
|
968
|
-
* Returns all color controls for `ColorPicker`.
|
969
|
-
*
|
970
|
-
* @param {CP.ColorPicker} self the `ColorPicker` instance
|
971
|
-
* @returns {HTMLElement | Element} color controls
|
972
|
-
*/
|
973
|
-
function getColorControls(self) {
|
974
|
-
const { format, componentLabels } = self;
|
975
|
-
const {
|
976
|
-
hueLabel, alphaLabel, lightnessLabel, saturationLabel,
|
977
|
-
whitenessLabel, blacknessLabel,
|
978
|
-
} = componentLabels;
|
979
|
-
|
980
|
-
const max1 = format === 'hsl' ? 360 : 100;
|
981
|
-
const max2 = format === 'hsl' ? 100 : 360;
|
982
|
-
const max3 = 100;
|
983
|
-
|
984
|
-
let ctrl1Label = format === 'hsl'
|
985
|
-
? `${hueLabel} & ${lightnessLabel}`
|
986
|
-
: `${lightnessLabel} & ${saturationLabel}`;
|
987
|
-
|
988
|
-
ctrl1Label = format === 'hwb'
|
989
|
-
? `${whitenessLabel} & ${blacknessLabel}`
|
990
|
-
: ctrl1Label;
|
991
|
-
|
992
|
-
const ctrl2Label = format === 'hsl'
|
993
|
-
? `${saturationLabel}`
|
994
|
-
: `${hueLabel}`;
|
995
|
-
|
996
|
-
const colorControls = createElement({
|
997
|
-
tagName: 'div',
|
998
|
-
className: `color-controls ${format}`,
|
999
|
-
});
|
1000
|
-
|
1001
|
-
const colorPointer = 'color-pointer';
|
1002
|
-
const colorSlider = 'color-slider';
|
1003
|
-
|
1004
|
-
const controls = [
|
1005
|
-
{
|
1006
|
-
i: 1,
|
1007
|
-
c: colorPointer,
|
1008
|
-
l: ctrl1Label,
|
1009
|
-
min: 0,
|
1010
|
-
max: max1,
|
1011
|
-
},
|
1012
|
-
{
|
1013
|
-
i: 2,
|
1014
|
-
c: colorSlider,
|
1015
|
-
l: ctrl2Label,
|
1016
|
-
min: 0,
|
1017
|
-
max: max2,
|
1018
|
-
},
|
1019
|
-
{
|
1020
|
-
i: 3,
|
1021
|
-
c: colorSlider,
|
1022
|
-
l: alphaLabel,
|
1023
|
-
min: 0,
|
1024
|
-
max: max3,
|
1025
|
-
},
|
1026
|
-
];
|
1027
|
-
|
1028
|
-
controls.forEach((template) => {
|
1029
|
-
const {
|
1030
|
-
i, c, l, min, max,
|
1031
|
-
} = template;
|
1032
|
-
const control = createElement({
|
1033
|
-
tagName: 'div',
|
1034
|
-
className: 'color-control',
|
1035
|
-
});
|
1036
|
-
setAttribute(control, 'role', 'presentation');
|
1037
|
-
|
1038
|
-
control.append(
|
1039
|
-
createElement({
|
1040
|
-
tagName: 'div',
|
1041
|
-
className: `visual-control visual-control${i}`,
|
1042
|
-
}),
|
1043
|
-
);
|
1044
|
-
|
1045
|
-
const knob = createElement({
|
1046
|
-
tagName: 'div',
|
1047
|
-
className: `${c} knob`,
|
1048
|
-
ariaLive: 'polite',
|
1049
|
-
});
|
1050
|
-
|
1051
|
-
setAttribute(knob, ariaLabel, l);
|
1052
|
-
setAttribute(knob, 'role', 'slider');
|
1053
|
-
setAttribute(knob, tabIndex, '0');
|
1054
|
-
setAttribute(knob, ariaValueMin, `${min}`);
|
1055
|
-
setAttribute(knob, ariaValueMax, `${max}`);
|
1056
|
-
control.append(knob);
|
1057
|
-
colorControls.append(control);
|
1058
|
-
});
|
1059
|
-
|
1060
|
-
return colorControls;
|
1061
|
-
}
|
1062
|
-
|
1063
|
-
/**
|
1064
|
-
* Helps setting CSS variables to the color-menu.
|
1065
|
-
* @param {HTMLElement} element
|
1066
|
-
* @param {Record<string,any>} props
|
1067
|
-
*/
|
1068
|
-
function setCSSProperties(element, props) {
|
1069
|
-
ObjectKeys(props).forEach((prop) => {
|
1070
|
-
element.style.setProperty(prop, props[prop]);
|
1071
|
-
});
|
1072
|
-
}
|
1073
|
-
|
1074
|
-
/**
|
1075
|
-
* Returns the `document.head` or the `<head>` element.
|
1076
|
-
*
|
1077
|
-
* @param {(Node | HTMLElement | Element | globalThis)=} node
|
1078
|
-
* @returns {HTMLElement | HTMLHeadElement}
|
1079
|
-
*/
|
1080
|
-
function getDocumentHead(node) {
|
1081
|
-
return getDocument(node).head;
|
1082
|
-
}
|
1083
|
-
|
1084
|
-
/**
|
1085
|
-
* Round colour components, for all formats except HEX.
|
1086
|
-
* @param {number} v one of the colour components
|
1087
|
-
* @returns {number} the rounded number
|
1088
|
-
*/
|
1089
|
-
function roundPart(v) {
|
1090
|
-
const floor = Math.floor(v);
|
1091
|
-
return v - floor < 0.5 ? floor : Math.round(v);
|
1092
|
-
}
|
1093
|
-
|
1094
|
-
// Color supported formats
|
1095
|
-
const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsb', 'hwb'];
|
1096
|
-
|
1097
|
-
// Hue angles
|
1098
|
-
const ANGLES = 'deg|rad|grad|turn';
|
1099
|
-
|
1100
|
-
// <http://www.w3.org/TR/css3-values/#integers>
|
1101
|
-
const CSS_INTEGER = '[-\\+]?\\d+%?';
|
1102
|
-
|
1103
|
-
// Include CSS3 Module
|
1104
|
-
// <http://www.w3.org/TR/css3-values/#number-value>
|
1105
|
-
const CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?';
|
1106
|
-
|
1107
|
-
// Include CSS4 Module Hue degrees unit
|
1108
|
-
// <https://www.w3.org/TR/css3-values/#angle-value>
|
1109
|
-
const CSS_ANGLE = `[-\\+]?\\d*\\.?\\d+(?:${ANGLES})?`;
|
1110
|
-
|
1111
|
-
// Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
|
1112
|
-
const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;
|
1113
|
-
|
1114
|
-
// Add angles to the mix
|
1115
|
-
const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
|
1116
|
-
|
1117
|
-
// Actual matching.
|
1118
|
-
// Parentheses and commas are optional, but not required.
|
1119
|
-
// Whitespace can take the place of commas or opening paren
|
1120
|
-
const PERMISSIVE_MATCH = `[\\s|\\(]+(${CSS_UNIT2})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s|\\/\\s]*(${CSS_UNIT})?\\s*\\)?`;
|
1121
|
-
|
1122
|
-
const matchers = {
|
1123
|
-
CSS_UNIT: new RegExp(CSS_UNIT2),
|
1124
|
-
hwb: new RegExp(`hwb${PERMISSIVE_MATCH}`),
|
1125
|
-
rgb: new RegExp(`rgb(?:a)?${PERMISSIVE_MATCH}`),
|
1126
|
-
hsl: new RegExp(`hsl(?:a)?${PERMISSIVE_MATCH}`),
|
1127
|
-
hsv: new RegExp(`hsv(?:a)?${PERMISSIVE_MATCH}`),
|
1128
|
-
hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
1129
|
-
hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
1130
|
-
hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
1131
|
-
hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
1132
|
-
};
|
1133
|
-
|
1134
|
-
/**
|
1135
|
-
* Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
|
1136
|
-
* <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
|
1137
|
-
* @param {string} n testing number
|
1138
|
-
* @returns {boolean} the query result
|
1139
|
-
*/
|
1140
|
-
function isOnePointZero(n) {
|
1141
|
-
return `${n}`.includes('.') && parseFloat(n) === 1;
|
1142
|
-
}
|
1143
|
-
|
1144
|
-
/**
|
1145
|
-
* Check to see if string passed in is a percentage
|
1146
|
-
* @param {string} n testing number
|
1147
|
-
* @returns {boolean} the query result
|
1148
|
-
*/
|
1149
|
-
function isPercentage(n) {
|
1150
|
-
return `${n}`.includes('%');
|
1151
|
-
}
|
1152
|
-
|
1153
|
-
/**
|
1154
|
-
* Check to see if string passed in is an angle
|
1155
|
-
* @param {string} n testing string
|
1156
|
-
* @returns {boolean} the query result
|
1157
|
-
*/
|
1158
|
-
function isAngle(n) {
|
1159
|
-
return ANGLES.split('|').some((a) => `${n}`.includes(a));
|
1160
|
-
}
|
832
|
+
function isPercentage(n) {
|
833
|
+
return `${n}`.includes('%');
|
834
|
+
}
|
1161
835
|
|
1162
836
|
/**
|
1163
837
|
* Check to see if string passed is a web safe colour.
|
838
|
+
* @see https://stackoverflow.com/a/16994164
|
1164
839
|
* @param {string} color a colour name, EG: *red*
|
1165
840
|
* @returns {boolean} the query result
|
1166
841
|
*/
|
1167
842
|
function isColorName(color) {
|
1168
|
-
|
1169
|
-
|
843
|
+
if (nonColors.includes(color)
|
844
|
+
|| ['#', ...COLOR_FORMAT].some((f) => color.includes(f))) return false;
|
845
|
+
|
846
|
+
if (['black', 'white'].includes(color)) return true;
|
847
|
+
|
848
|
+
return ['rgb(255, 255, 255)', 'rgb(0, 0, 0)'].every((c) => {
|
849
|
+
setElementStyle(documentHead, { color });
|
850
|
+
const computedColor = getElementStyle(documentHead, 'color');
|
851
|
+
setElementStyle(documentHead, { color: '' });
|
852
|
+
return computedColor !== c;
|
853
|
+
});
|
1170
854
|
}
|
1171
855
|
|
1172
856
|
/**
|
@@ -1187,15 +871,20 @@
|
|
1187
871
|
*/
|
1188
872
|
function bound01(N, max) {
|
1189
873
|
let n = N;
|
1190
|
-
if (isOnePointZero(n)) n = '100%';
|
1191
874
|
|
1192
|
-
|
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
|
+
|
879
|
+
if (isOnePointZero(N)) n = '100%';
|
1193
880
|
|
1194
|
-
|
1195
|
-
|
881
|
+
const processPercent = isPercentage(n);
|
882
|
+
n = max === 360
|
883
|
+
? parseFloat(n)
|
884
|
+
: Math.min(max, Math.max(0, parseFloat(n)));
|
1196
885
|
|
1197
886
|
// Automatically convert percentage into number
|
1198
|
-
if (
|
887
|
+
if (processPercent) n = (n * max) / 100;
|
1199
888
|
|
1200
889
|
// Handle floating point rounding errors
|
1201
890
|
if (Math.abs(n - max) < 0.000001) {
|
@@ -1206,11 +895,11 @@
|
|
1206
895
|
// If n is a hue given in degrees,
|
1207
896
|
// wrap around out-of-range values into [0, 360] range
|
1208
897
|
// then convert into [0, 1].
|
1209
|
-
n = (n < 0 ? (n % max) + max : n % max) /
|
898
|
+
n = (n < 0 ? (n % max) + max : n % max) / max;
|
1210
899
|
} else {
|
1211
900
|
// If n not a hue given in degrees
|
1212
901
|
// Convert into [0, 1] range if it isn't already.
|
1213
|
-
n = (n % max) /
|
902
|
+
n = (n % max) / max;
|
1214
903
|
}
|
1215
904
|
return n;
|
1216
905
|
}
|
@@ -1245,7 +934,6 @@
|
|
1245
934
|
* @returns {string}
|
1246
935
|
*/
|
1247
936
|
function getRGBFromName(name) {
|
1248
|
-
const documentHead = getDocumentHead();
|
1249
937
|
setElementStyle(documentHead, { color: name });
|
1250
938
|
const colorName = getElementStyle(documentHead, 'color');
|
1251
939
|
setElementStyle(documentHead, { color: '' });
|
@@ -1291,15 +979,12 @@
|
|
1291
979
|
/**
|
1292
980
|
* Converts an RGB colour value to HSL.
|
1293
981
|
*
|
1294
|
-
* @param {number}
|
1295
|
-
* @param {number}
|
1296
|
-
* @param {number}
|
982
|
+
* @param {number} r Red component [0, 1]
|
983
|
+
* @param {number} g Green component [0, 1]
|
984
|
+
* @param {number} b Blue component [0, 1]
|
1297
985
|
* @returns {CP.HSL} {h,s,l} object with [0, 1] ranged values
|
1298
986
|
*/
|
1299
|
-
function rgbToHsl(
|
1300
|
-
const r = R / 255;
|
1301
|
-
const g = G / 255;
|
1302
|
-
const b = B / 255;
|
987
|
+
function rgbToHsl(r, g, b) {
|
1303
988
|
const max = Math.max(r, g, b);
|
1304
989
|
const min = Math.min(r, g, b);
|
1305
990
|
let h = 0;
|
@@ -1311,17 +996,10 @@
|
|
1311
996
|
} else {
|
1312
997
|
const d = max - min;
|
1313
998
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
case g:
|
1319
|
-
h = (b - r) / d + 2;
|
1320
|
-
break;
|
1321
|
-
case b:
|
1322
|
-
h = (r - g) / d + 4;
|
1323
|
-
break;
|
1324
|
-
}
|
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
|
+
|
1325
1003
|
h /= 6;
|
1326
1004
|
}
|
1327
1005
|
return { h, s, l };
|
@@ -1344,21 +1022,46 @@
|
|
1344
1022
|
return p;
|
1345
1023
|
}
|
1346
1024
|
|
1025
|
+
/**
|
1026
|
+
* Converts an HSL colour value to RGB.
|
1027
|
+
*
|
1028
|
+
* @param {number} h Hue Angle [0, 1]
|
1029
|
+
* @param {number} s Saturation [0, 1]
|
1030
|
+
* @param {number} l Lightness Angle [0, 1]
|
1031
|
+
* @returns {CP.RGB} {r,g,b} object with [0, 1] ranged values
|
1032
|
+
*/
|
1033
|
+
function hslToRgb(h, s, l) {
|
1034
|
+
let r = 0;
|
1035
|
+
let g = 0;
|
1036
|
+
let b = 0;
|
1037
|
+
|
1038
|
+
if (s === 0) {
|
1039
|
+
// achromatic
|
1040
|
+
g = l;
|
1041
|
+
b = l;
|
1042
|
+
r = l;
|
1043
|
+
} else {
|
1044
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
1045
|
+
const p = 2 * l - q;
|
1046
|
+
r = hueToRgb(p, q, h + 1 / 3);
|
1047
|
+
g = hueToRgb(p, q, h);
|
1048
|
+
b = hueToRgb(p, q, h - 1 / 3);
|
1049
|
+
}
|
1050
|
+
|
1051
|
+
return { r, g, b };
|
1052
|
+
}
|
1053
|
+
|
1347
1054
|
/**
|
1348
1055
|
* Returns an HWB colour object from an RGB colour object.
|
1349
1056
|
* @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
|
1350
1057
|
* @link http://alvyray.com/Papers/CG/hwb2rgb.htm
|
1351
1058
|
*
|
1352
|
-
* @param {number}
|
1353
|
-
* @param {number}
|
1354
|
-
* @param {number}
|
1059
|
+
* @param {number} r Red component [0, 1]
|
1060
|
+
* @param {number} g Green [0, 1]
|
1061
|
+
* @param {number} b Blue [0, 1]
|
1355
1062
|
* @return {CP.HWB} {h,w,b} object with [0, 1] ranged values
|
1356
1063
|
*/
|
1357
|
-
function rgbToHwb(
|
1358
|
-
const r = R / 255;
|
1359
|
-
const g = G / 255;
|
1360
|
-
const b = B / 255;
|
1361
|
-
|
1064
|
+
function rgbToHwb(r, g, b) {
|
1362
1065
|
let f = 0;
|
1363
1066
|
let i = 0;
|
1364
1067
|
const whiteness = Math.min(r, g, b);
|
@@ -1388,50 +1091,18 @@
|
|
1388
1091
|
* @param {number} H Hue Angle [0, 1]
|
1389
1092
|
* @param {number} W Whiteness [0, 1]
|
1390
1093
|
* @param {number} B Blackness [0, 1]
|
1391
|
-
* @return {CP.RGB} {r,g,b} object with [0,
|
1094
|
+
* @return {CP.RGB} {r,g,b} object with [0, 1] ranged values
|
1392
1095
|
*
|
1393
1096
|
* @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
|
1394
1097
|
* @link http://alvyray.com/Papers/CG/hwb2rgb.htm
|
1395
1098
|
*/
|
1396
1099
|
function hwbToRgb(H, W, B) {
|
1397
1100
|
if (W + B >= 1) {
|
1398
|
-
const gray =
|
1101
|
+
const gray = W / (W + B);
|
1399
1102
|
return { r: gray, g: gray, b: gray };
|
1400
1103
|
}
|
1401
1104
|
let { r, g, b } = hslToRgb(H, 1, 0.5);
|
1402
|
-
[r, g, b] = [r, g, b]
|
1403
|
-
.map((v) => (v / 255) * (1 - W - B) + W)
|
1404
|
-
.map((v) => v * 255);
|
1405
|
-
|
1406
|
-
return { r, g, b };
|
1407
|
-
}
|
1408
|
-
|
1409
|
-
/**
|
1410
|
-
* Converts an HSL colour value to RGB.
|
1411
|
-
*
|
1412
|
-
* @param {number} h Hue Angle [0, 1]
|
1413
|
-
* @param {number} s Saturation [0, 1]
|
1414
|
-
* @param {number} l Lightness Angle [0, 1]
|
1415
|
-
* @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
|
1416
|
-
*/
|
1417
|
-
function hslToRgb(h, s, l) {
|
1418
|
-
let r = 0;
|
1419
|
-
let g = 0;
|
1420
|
-
let b = 0;
|
1421
|
-
|
1422
|
-
if (s === 0) {
|
1423
|
-
// achromatic
|
1424
|
-
g = l;
|
1425
|
-
b = l;
|
1426
|
-
r = l;
|
1427
|
-
} else {
|
1428
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
1429
|
-
const p = 2 * l - q;
|
1430
|
-
r = hueToRgb(p, q, h + 1 / 3);
|
1431
|
-
g = hueToRgb(p, q, h);
|
1432
|
-
b = hueToRgb(p, q, h - 1 / 3);
|
1433
|
-
}
|
1434
|
-
[r, g, b] = [r, g, b].map((x) => x * 255);
|
1105
|
+
[r, g, b] = [r, g, b].map((v) => v * (1 - W - B) + W);
|
1435
1106
|
|
1436
1107
|
return { r, g, b };
|
1437
1108
|
}
|
@@ -1439,15 +1110,12 @@
|
|
1439
1110
|
/**
|
1440
1111
|
* Converts an RGB colour value to HSV.
|
1441
1112
|
*
|
1442
|
-
* @param {number}
|
1443
|
-
* @param {number}
|
1444
|
-
* @param {number}
|
1113
|
+
* @param {number} r Red component [0, 1]
|
1114
|
+
* @param {number} g Green [0, 1]
|
1115
|
+
* @param {number} b Blue [0, 1]
|
1445
1116
|
* @returns {CP.HSV} {h,s,v} object with [0, 1] ranged values
|
1446
1117
|
*/
|
1447
|
-
function rgbToHsv(
|
1448
|
-
const r = R / 255;
|
1449
|
-
const g = G / 255;
|
1450
|
-
const b = B / 255;
|
1118
|
+
function rgbToHsv(r, g, b) {
|
1451
1119
|
const max = Math.max(r, g, b);
|
1452
1120
|
const min = Math.min(r, g, b);
|
1453
1121
|
let h = 0;
|
@@ -1457,17 +1125,10 @@
|
|
1457
1125
|
if (max === min) {
|
1458
1126
|
h = 0; // achromatic
|
1459
1127
|
} else {
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
case g:
|
1465
|
-
h = (b - r) / d + 2;
|
1466
|
-
break;
|
1467
|
-
case b:
|
1468
|
-
h = (r - g) / d + 4;
|
1469
|
-
break;
|
1470
|
-
}
|
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
|
+
|
1471
1132
|
h /= 6;
|
1472
1133
|
}
|
1473
1134
|
return { h, s, v };
|
@@ -1494,7 +1155,7 @@
|
|
1494
1155
|
const r = [v, q, p, p, t, v][mod];
|
1495
1156
|
const g = [t, v, v, q, p, p][mod];
|
1496
1157
|
const b = [p, p, t, v, v, q][mod];
|
1497
|
-
return { r
|
1158
|
+
return { r, g, b };
|
1498
1159
|
}
|
1499
1160
|
|
1500
1161
|
/**
|
@@ -1518,7 +1179,7 @@
|
|
1518
1179
|
// Return a 3 character hex if possible
|
1519
1180
|
if (allow3Char && hex[0].charAt(0) === hex[0].charAt(1)
|
1520
1181
|
&& hex[1].charAt(0) === hex[1].charAt(1)
|
1521
|
-
|
1182
|
+
&& hex[2].charAt(0) === hex[2].charAt(1)) {
|
1522
1183
|
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
|
1523
1184
|
}
|
1524
1185
|
|
@@ -1546,51 +1207,34 @@
|
|
1546
1207
|
// Return a 4 character hex if possible
|
1547
1208
|
if (allow4Char && hex[0].charAt(0) === hex[0].charAt(1)
|
1548
1209
|
&& hex[1].charAt(0) === hex[1].charAt(1)
|
1549
|
-
|
1550
|
-
|
1210
|
+
&& hex[2].charAt(0) === hex[2].charAt(1)
|
1211
|
+
&& hex[3].charAt(0) === hex[3].charAt(1)) {
|
1551
1212
|
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
|
1552
1213
|
}
|
1553
1214
|
return hex.join('');
|
1554
1215
|
}
|
1555
1216
|
|
1556
|
-
/**
|
1557
|
-
* Returns a colour object corresponding to a given number.
|
1558
|
-
* @param {number} color input number
|
1559
|
-
* @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
|
1560
|
-
*/
|
1561
|
-
function numberInputToObject(color) {
|
1562
|
-
/* eslint-disable no-bitwise */
|
1563
|
-
return {
|
1564
|
-
r: color >> 16,
|
1565
|
-
g: (color & 0xff00) >> 8,
|
1566
|
-
b: color & 0xff,
|
1567
|
-
};
|
1568
|
-
/* eslint-enable no-bitwise */
|
1569
|
-
}
|
1570
|
-
|
1571
1217
|
/**
|
1572
1218
|
* Permissive string parsing. Take in a number of formats, and output an object
|
1573
1219
|
* based on detected format. Returns {r,g,b} or {h,s,l} or {h,s,v}
|
1574
1220
|
* @param {string} input colour value in any format
|
1575
|
-
* @returns {Record<string, (number | string)> | false} an object matching the RegExp
|
1221
|
+
* @returns {Record<string, (number | string | boolean)> | false} an object matching the RegExp
|
1576
1222
|
*/
|
1577
1223
|
function stringInputToObject(input) {
|
1578
|
-
let color = input.trim()
|
1224
|
+
let color = toLowerCase(input.trim());
|
1225
|
+
|
1579
1226
|
if (color.length === 0) {
|
1580
1227
|
return {
|
1581
|
-
r: 0, g: 0, b: 0, a:
|
1228
|
+
r: 0, g: 0, b: 0, a: 1,
|
1582
1229
|
};
|
1583
1230
|
}
|
1584
|
-
|
1231
|
+
|
1585
1232
|
if (isColorName(color)) {
|
1586
1233
|
color = getRGBFromName(color);
|
1587
|
-
named = true;
|
1588
1234
|
} else if (nonColors.includes(color)) {
|
1589
|
-
const
|
1590
|
-
const rgb = isTransparent ? 0 : 255;
|
1591
|
-
const a = isTransparent ? 0 : 1;
|
1235
|
+
const a = color === 'transparent' ? 0 : 1;
|
1592
1236
|
return {
|
1593
|
-
r:
|
1237
|
+
r: 0, g: 0, b: 0, a, format: 'rgb', ok: true,
|
1594
1238
|
};
|
1595
1239
|
}
|
1596
1240
|
|
@@ -1605,24 +1249,28 @@
|
|
1605
1249
|
r: m1, g: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'rgb',
|
1606
1250
|
};
|
1607
1251
|
}
|
1252
|
+
|
1608
1253
|
[, m1, m2, m3, m4] = matchers.hsl.exec(color) || [];
|
1609
1254
|
if (m1 && m2 && m3/* && m4 */) {
|
1610
1255
|
return {
|
1611
1256
|
h: m1, s: m2, l: m3, a: m4 !== undefined ? m4 : 1, format: 'hsl',
|
1612
1257
|
};
|
1613
1258
|
}
|
1259
|
+
|
1614
1260
|
[, m1, m2, m3, m4] = matchers.hsv.exec(color) || [];
|
1615
1261
|
if (m1 && m2 && m3/* && m4 */) {
|
1616
1262
|
return {
|
1617
1263
|
h: m1, s: m2, v: m3, a: m4 !== undefined ? m4 : 1, format: 'hsv',
|
1618
1264
|
};
|
1619
1265
|
}
|
1266
|
+
|
1620
1267
|
[, m1, m2, m3, m4] = matchers.hwb.exec(color) || [];
|
1621
1268
|
if (m1 && m2 && m3) {
|
1622
1269
|
return {
|
1623
1270
|
h: m1, w: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'hwb',
|
1624
1271
|
};
|
1625
1272
|
}
|
1273
|
+
|
1626
1274
|
[, m1, m2, m3, m4] = matchers.hex8.exec(color) || [];
|
1627
1275
|
if (m1 && m2 && m3 && m4) {
|
1628
1276
|
return {
|
@@ -1630,19 +1278,20 @@
|
|
1630
1278
|
g: parseIntFromHex(m2),
|
1631
1279
|
b: parseIntFromHex(m3),
|
1632
1280
|
a: convertHexToDecimal(m4),
|
1633
|
-
|
1634
|
-
format: named ? 'rgb' : 'hex',
|
1281
|
+
format: 'hex',
|
1635
1282
|
};
|
1636
1283
|
}
|
1284
|
+
|
1637
1285
|
[, m1, m2, m3] = matchers.hex6.exec(color) || [];
|
1638
1286
|
if (m1 && m2 && m3) {
|
1639
1287
|
return {
|
1640
1288
|
r: parseIntFromHex(m1),
|
1641
1289
|
g: parseIntFromHex(m2),
|
1642
1290
|
b: parseIntFromHex(m3),
|
1643
|
-
format:
|
1291
|
+
format: 'hex',
|
1644
1292
|
};
|
1645
1293
|
}
|
1294
|
+
|
1646
1295
|
[, m1, m2, m3, m4] = matchers.hex4.exec(color) || [];
|
1647
1296
|
if (m1 && m2 && m3 && m4) {
|
1648
1297
|
return {
|
@@ -1650,19 +1299,20 @@
|
|
1650
1299
|
g: parseIntFromHex(m2 + m2),
|
1651
1300
|
b: parseIntFromHex(m3 + m3),
|
1652
1301
|
a: convertHexToDecimal(m4 + m4),
|
1653
|
-
|
1654
|
-
format: named ? 'rgb' : 'hex',
|
1302
|
+
format: 'hex',
|
1655
1303
|
};
|
1656
1304
|
}
|
1305
|
+
|
1657
1306
|
[, m1, m2, m3] = matchers.hex3.exec(color) || [];
|
1658
1307
|
if (m1 && m2 && m3) {
|
1659
1308
|
return {
|
1660
1309
|
r: parseIntFromHex(m1 + m1),
|
1661
1310
|
g: parseIntFromHex(m2 + m2),
|
1662
1311
|
b: parseIntFromHex(m3 + m3),
|
1663
|
-
format:
|
1312
|
+
format: 'hex',
|
1664
1313
|
};
|
1665
1314
|
}
|
1315
|
+
|
1666
1316
|
return false;
|
1667
1317
|
}
|
1668
1318
|
|
@@ -1693,7 +1343,9 @@
|
|
1693
1343
|
*/
|
1694
1344
|
function inputToRGB(input) {
|
1695
1345
|
let rgb = { r: 0, g: 0, b: 0 };
|
1346
|
+
/** @type {*} */
|
1696
1347
|
let color = input;
|
1348
|
+
/** @type {string | number} */
|
1697
1349
|
let a = 1;
|
1698
1350
|
let s = null;
|
1699
1351
|
let v = null;
|
@@ -1704,58 +1356,67 @@
|
|
1704
1356
|
let r = null;
|
1705
1357
|
let g = null;
|
1706
1358
|
let ok = false;
|
1707
|
-
|
1359
|
+
const inputFormat = typeof color === 'object' && color.format;
|
1360
|
+
let format = inputFormat && COLOR_FORMAT.includes(inputFormat) ? inputFormat : 'rgb';
|
1708
1361
|
|
1709
1362
|
if (typeof input === 'string') {
|
1710
|
-
// @ts-ignore -- this now is converted to object
|
1711
1363
|
color = stringInputToObject(input);
|
1712
1364
|
if (color) ok = true;
|
1713
1365
|
}
|
1714
1366
|
if (typeof color === 'object') {
|
1715
1367
|
if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
|
1716
1368
|
({ r, g, b } = color);
|
1717
|
-
|
1718
|
-
|
1719
|
-
rgb = { r, g, b };
|
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));
|
1371
|
+
rgb = { r, g, b };
|
1720
1372
|
ok = true;
|
1721
|
-
format = 'rgb';
|
1722
|
-
}
|
1373
|
+
format = color.format || 'rgb';
|
1374
|
+
}
|
1375
|
+
if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
|
1723
1376
|
({ h, s, v } = color);
|
1724
|
-
h =
|
1725
|
-
s =
|
1726
|
-
v =
|
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
|
1727
1380
|
rgb = hsvToRgb(h, s, v);
|
1728
1381
|
ok = true;
|
1729
1382
|
format = 'hsv';
|
1730
|
-
}
|
1383
|
+
}
|
1384
|
+
if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
|
1731
1385
|
({ h, s, l } = color);
|
1732
|
-
h =
|
1733
|
-
s =
|
1734
|
-
l =
|
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
|
1735
1389
|
rgb = hslToRgb(h, s, l);
|
1736
1390
|
ok = true;
|
1737
1391
|
format = 'hsl';
|
1738
|
-
}
|
1392
|
+
}
|
1393
|
+
if (isValidCSSUnit(color.h) && isValidCSSUnit(color.w) && isValidCSSUnit(color.b)) {
|
1739
1394
|
({ h, w, b } = color);
|
1740
|
-
h =
|
1741
|
-
w =
|
1742
|
-
b =
|
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
|
1743
1398
|
rgb = hwbToRgb(h, w, b);
|
1744
1399
|
ok = true;
|
1745
1400
|
format = 'hwb';
|
1746
1401
|
}
|
1747
1402
|
if (isValidCSSUnit(color.a)) {
|
1748
|
-
a = color.a;
|
1749
|
-
a = isPercentage(`${a}`) ? bound01(a, 100) : a;
|
1403
|
+
a = color.a; // @ts-ignore -- `parseFloat` works with numbers too
|
1404
|
+
a = isPercentage(`${a}`) || parseFloat(a) > 1 ? bound01(a, 100) : a;
|
1750
1405
|
}
|
1751
1406
|
}
|
1407
|
+
if (typeof color === 'undefined') {
|
1408
|
+
ok = true;
|
1409
|
+
}
|
1752
1410
|
|
1753
1411
|
return {
|
1754
|
-
ok,
|
1755
|
-
format
|
1756
|
-
r: Math.min(255, Math.max(rgb.r, 0)),
|
1757
|
-
g: Math.min(255, Math.max(rgb.g, 0)),
|
1758
|
-
b: Math.min(255, Math.max(rgb.b, 0)),
|
1412
|
+
ok,
|
1413
|
+
format,
|
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,
|
1759
1420
|
a: boundAlpha(a),
|
1760
1421
|
};
|
1761
1422
|
}
|
@@ -1774,15 +1435,13 @@
|
|
1774
1435
|
constructor(input, config) {
|
1775
1436
|
let color = input;
|
1776
1437
|
const configFormat = config && COLOR_FORMAT.includes(config)
|
1777
|
-
? config : '
|
1438
|
+
? config : '';
|
1778
1439
|
|
1779
|
-
// If input is already a `Color`,
|
1440
|
+
// If input is already a `Color`, clone its values
|
1780
1441
|
if (color instanceof Color) {
|
1781
1442
|
color = inputToRGB(color);
|
1782
1443
|
}
|
1783
|
-
|
1784
|
-
color = numberInputToObject(color);
|
1785
|
-
}
|
1444
|
+
|
1786
1445
|
const {
|
1787
1446
|
r, g, b, a, ok, format,
|
1788
1447
|
} = inputToRGB(color);
|
@@ -1791,7 +1450,7 @@
|
|
1791
1450
|
const self = this;
|
1792
1451
|
|
1793
1452
|
/** @type {CP.ColorInput} */
|
1794
|
-
self.originalInput =
|
1453
|
+
self.originalInput = input;
|
1795
1454
|
/** @type {number} */
|
1796
1455
|
self.r = r;
|
1797
1456
|
/** @type {number} */
|
@@ -1832,24 +1491,21 @@
|
|
1832
1491
|
let R = 0;
|
1833
1492
|
let G = 0;
|
1834
1493
|
let B = 0;
|
1835
|
-
const rp = r / 255;
|
1836
|
-
const rg = g / 255;
|
1837
|
-
const rb = b / 255;
|
1838
1494
|
|
1839
|
-
if (
|
1840
|
-
R =
|
1495
|
+
if (r <= 0.03928) {
|
1496
|
+
R = r / 12.92;
|
1841
1497
|
} else {
|
1842
|
-
R = ((
|
1498
|
+
R = ((r + 0.055) / 1.055) ** 2.4;
|
1843
1499
|
}
|
1844
|
-
if (
|
1845
|
-
G =
|
1500
|
+
if (g <= 0.03928) {
|
1501
|
+
G = g / 12.92;
|
1846
1502
|
} else {
|
1847
|
-
G = ((
|
1503
|
+
G = ((g + 0.055) / 1.055) ** 2.4;
|
1848
1504
|
}
|
1849
|
-
if (
|
1850
|
-
B =
|
1505
|
+
if (b <= 0.03928) {
|
1506
|
+
B = b / 12.92;
|
1851
1507
|
} else {
|
1852
|
-
B = ((
|
1508
|
+
B = ((b + 0.055) / 1.055) ** 2.4;
|
1853
1509
|
}
|
1854
1510
|
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
|
1855
1511
|
}
|
@@ -1859,7 +1515,7 @@
|
|
1859
1515
|
* @returns {number} a number in the [0, 255] range
|
1860
1516
|
*/
|
1861
1517
|
get brightness() {
|
1862
|
-
const { r, g, b } = this;
|
1518
|
+
const { r, g, b } = this.toRgb();
|
1863
1519
|
return (r * 299 + g * 587 + b * 114) / 1000;
|
1864
1520
|
}
|
1865
1521
|
|
@@ -1868,16 +1524,14 @@
|
|
1868
1524
|
* @returns {CP.RGBA} an {r,g,b,a} object with [0, 255] ranged values
|
1869
1525
|
*/
|
1870
1526
|
toRgb() {
|
1871
|
-
|
1527
|
+
let {
|
1872
1528
|
r, g, b, a,
|
1873
1529
|
} = this;
|
1874
|
-
const [R, G, B] = [r, g, b].map((x) => roundPart(x));
|
1875
1530
|
|
1531
|
+
[r, g, b] = [r, g, b].map((n) => roundPart(n * 255 * 100) / 100);
|
1532
|
+
a = roundPart(a * 100) / 100;
|
1876
1533
|
return {
|
1877
|
-
r
|
1878
|
-
g: G,
|
1879
|
-
b: B,
|
1880
|
-
a: roundPart(a * 100) / 100,
|
1534
|
+
r, g, b, a,
|
1881
1535
|
};
|
1882
1536
|
}
|
1883
1537
|
|
@@ -1891,10 +1545,11 @@
|
|
1891
1545
|
const {
|
1892
1546
|
r, g, b, a,
|
1893
1547
|
} = this.toRgb();
|
1548
|
+
const [R, G, B] = [r, g, b].map(roundPart);
|
1894
1549
|
|
1895
1550
|
return a === 1
|
1896
|
-
? `rgb(${
|
1897
|
-
: `rgba(${
|
1551
|
+
? `rgb(${R}, ${G}, ${B})`
|
1552
|
+
: `rgba(${R}, ${G}, ${B}, ${a})`;
|
1898
1553
|
}
|
1899
1554
|
|
1900
1555
|
/**
|
@@ -1907,9 +1562,10 @@
|
|
1907
1562
|
const {
|
1908
1563
|
r, g, b, a,
|
1909
1564
|
} = this.toRgb();
|
1565
|
+
const [R, G, B] = [r, g, b].map(roundPart);
|
1910
1566
|
const A = a === 1 ? '' : ` / ${roundPart(a * 100)}%`;
|
1911
1567
|
|
1912
|
-
return `rgb(${
|
1568
|
+
return `rgb(${R} ${G} ${B}${A})`;
|
1913
1569
|
}
|
1914
1570
|
|
1915
1571
|
/**
|
@@ -1969,7 +1625,7 @@
|
|
1969
1625
|
toHsv() {
|
1970
1626
|
const {
|
1971
1627
|
r, g, b, a,
|
1972
|
-
} = this
|
1628
|
+
} = this;
|
1973
1629
|
const { h, s, v } = rgbToHsv(r, g, b);
|
1974
1630
|
|
1975
1631
|
return {
|
@@ -1984,7 +1640,7 @@
|
|
1984
1640
|
toHsl() {
|
1985
1641
|
const {
|
1986
1642
|
r, g, b, a,
|
1987
|
-
} = this
|
1643
|
+
} = this;
|
1988
1644
|
const { h, s, l } = rgbToHsl(r, g, b);
|
1989
1645
|
|
1990
1646
|
return {
|
@@ -2069,6 +1725,7 @@
|
|
2069
1725
|
*/
|
2070
1726
|
setAlpha(alpha) {
|
2071
1727
|
const self = this;
|
1728
|
+
if (typeof alpha !== 'number') return self;
|
2072
1729
|
self.a = boundAlpha(alpha);
|
2073
1730
|
return self;
|
2074
1731
|
}
|
@@ -2183,6 +1840,7 @@
|
|
2183
1840
|
isOnePointZero,
|
2184
1841
|
isPercentage,
|
2185
1842
|
isValidCSSUnit,
|
1843
|
+
isColorName,
|
2186
1844
|
pad2,
|
2187
1845
|
clamp01,
|
2188
1846
|
bound01,
|
@@ -2200,10 +1858,11 @@
|
|
2200
1858
|
hueToRgb,
|
2201
1859
|
hwbToRgb,
|
2202
1860
|
parseIntFromHex,
|
2203
|
-
numberInputToObject,
|
2204
1861
|
stringInputToObject,
|
2205
1862
|
inputToRGB,
|
2206
1863
|
roundPart,
|
1864
|
+
getElementStyle,
|
1865
|
+
setElementStyle,
|
2207
1866
|
ObjectAssign,
|
2208
1867
|
});
|
2209
1868
|
|
@@ -2212,7 +1871,7 @@
|
|
2212
1871
|
* Returns a color palette with a given set of parameters.
|
2213
1872
|
* @example
|
2214
1873
|
* new ColorPalette(0, 12, 10);
|
2215
|
-
* // => { hue: 0, hueSteps: 12, lightSteps: 10, colors:
|
1874
|
+
* // => { hue: 0, hueSteps: 12, lightSteps: 10, colors: Array<Color> }
|
2216
1875
|
*/
|
2217
1876
|
class ColorPalette {
|
2218
1877
|
/**
|
@@ -2232,48 +1891,352 @@
|
|
2232
1891
|
[hue, hueSteps, lightSteps] = args;
|
2233
1892
|
} else if (args.length === 2) {
|
2234
1893
|
[hueSteps, lightSteps] = args;
|
2235
|
-
|
2236
|
-
|
1894
|
+
if ([hueSteps, lightSteps].some((n) => n < 1)) {
|
1895
|
+
throw TypeError('ColorPalette: both arguments must be higher than 0.');
|
1896
|
+
}
|
2237
1897
|
}
|
2238
1898
|
|
2239
|
-
/** @type {
|
1899
|
+
/** @type {*} */
|
2240
1900
|
const colors = [];
|
2241
|
-
|
2242
1901
|
const hueStep = 360 / hueSteps;
|
2243
1902
|
const half = roundPart((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
|
2244
|
-
const
|
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));
|
1906
|
+
|
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);
|
1912
|
+
|
1913
|
+
// light tints
|
1914
|
+
for (let i = 1; i < half + 1; i += 1) {
|
1915
|
+
lightnessArray = [...lightnessArray, (0.5 + lightStep * (i))];
|
1916
|
+
}
|
1917
|
+
|
1918
|
+
// dark tints
|
1919
|
+
for (let i = 1; i < lightSteps - half; i += 1) {
|
1920
|
+
lightnessArray = [(0.5 - lightStep * (i)), ...lightnessArray];
|
1921
|
+
}
|
1922
|
+
|
1923
|
+
// feed `colors` Array
|
1924
|
+
for (let i = 0; i < hueSteps; i += 1) {
|
1925
|
+
const currentHue = ((hue + i * hueStep) % 360) / 360;
|
1926
|
+
lightnessArray.forEach((l) => {
|
1927
|
+
colors.push(new Color({ h: currentHue, s: 1, l }));
|
1928
|
+
});
|
1929
|
+
}
|
1930
|
+
|
1931
|
+
this.hue = hue;
|
1932
|
+
this.hueSteps = hueSteps;
|
1933
|
+
this.lightSteps = lightSteps;
|
1934
|
+
this.colors = colors;
|
1935
|
+
}
|
1936
|
+
}
|
1937
|
+
|
1938
|
+
ObjectAssign(ColorPalette, { Color });
|
1939
|
+
|
1940
|
+
/** @type {Record<string, string>} */
|
1941
|
+
const colorPickerLabels = {
|
1942
|
+
pickerLabel: 'Colour Picker',
|
1943
|
+
appearanceLabel: 'Colour Appearance',
|
1944
|
+
valueLabel: 'Colour Value',
|
1945
|
+
toggleLabel: 'Select Colour',
|
1946
|
+
presetsLabel: 'Colour Presets',
|
1947
|
+
defaultsLabel: 'Colour Defaults',
|
1948
|
+
formatLabel: 'Format',
|
1949
|
+
alphaLabel: 'Alpha',
|
1950
|
+
hexLabel: 'Hexadecimal',
|
1951
|
+
hueLabel: 'Hue',
|
1952
|
+
whitenessLabel: 'Whiteness',
|
1953
|
+
blacknessLabel: 'Blackness',
|
1954
|
+
saturationLabel: 'Saturation',
|
1955
|
+
lightnessLabel: 'Lightness',
|
1956
|
+
redLabel: 'Red',
|
1957
|
+
greenLabel: 'Green',
|
1958
|
+
blueLabel: 'Blue',
|
1959
|
+
};
|
1960
|
+
|
1961
|
+
/**
|
1962
|
+
* A list of 17 color names used for WAI-ARIA compliance.
|
1963
|
+
* @type {string[]}
|
1964
|
+
*/
|
1965
|
+
const colorNames = ['white', 'black', 'grey', 'red', 'orange', 'brown', 'gold', 'olive', 'yellow', 'lime', 'green', 'teal', 'cyan', 'blue', 'violet', 'magenta', 'pink'];
|
1966
|
+
|
1967
|
+
const tabIndex = 'tabindex';
|
1968
|
+
|
1969
|
+
/**
|
1970
|
+
* Check if a string is valid JSON string.
|
1971
|
+
* @param {string} str the string input
|
1972
|
+
* @returns {boolean} the query result
|
1973
|
+
*/
|
1974
|
+
function isValidJSON(str) {
|
1975
|
+
try {
|
1976
|
+
JSON.parse(str);
|
1977
|
+
} catch (e) {
|
1978
|
+
return false;
|
1979
|
+
}
|
1980
|
+
return true;
|
1981
|
+
}
|
1982
|
+
|
1983
|
+
/**
|
1984
|
+
* Shortcut for `String.toUpperCase()`.
|
1985
|
+
*
|
1986
|
+
* @param {string} source input string
|
1987
|
+
* @returns {string} uppercase output string
|
1988
|
+
*/
|
1989
|
+
const toUpperCase = (source) => source.toUpperCase();
|
1990
|
+
|
1991
|
+
/**
|
1992
|
+
* A global namespace for aria-haspopup.
|
1993
|
+
* @type {string}
|
1994
|
+
*/
|
1995
|
+
const ariaHasPopup = 'aria-haspopup';
|
1996
|
+
|
1997
|
+
/**
|
1998
|
+
* A global namespace for aria-hidden.
|
1999
|
+
* @type {string}
|
2000
|
+
*/
|
2001
|
+
const ariaHidden = 'aria-hidden';
|
2002
|
+
|
2003
|
+
/**
|
2004
|
+
* A global namespace for aria-labelledby.
|
2005
|
+
* @type {string}
|
2006
|
+
*/
|
2007
|
+
const ariaLabelledBy = 'aria-labelledby';
|
2008
|
+
|
2009
|
+
/**
|
2010
|
+
* This is a shortie for `document.createElement` method
|
2011
|
+
* which allows you to create a new `HTMLElement` for a given `tagName`
|
2012
|
+
* or based on an object with specific non-readonly attributes:
|
2013
|
+
* `id`, `className`, `textContent`, `style`, etc.
|
2014
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
|
2015
|
+
*
|
2016
|
+
* @param {Record<string, string> | string} param `tagName` or object
|
2017
|
+
* @return {HTMLElement | Element} a new `HTMLElement` or `Element`
|
2018
|
+
*/
|
2019
|
+
function createElement(param) {
|
2020
|
+
if (typeof param === 'string') {
|
2021
|
+
return getDocument().createElement(param);
|
2022
|
+
}
|
2023
|
+
|
2024
|
+
const { tagName } = param;
|
2025
|
+
const attr = { ...param };
|
2026
|
+
const newElement = createElement(tagName);
|
2027
|
+
delete attr.tagName;
|
2028
|
+
ObjectAssign(newElement, attr);
|
2029
|
+
return newElement;
|
2030
|
+
}
|
2031
|
+
|
2032
|
+
/**
|
2033
|
+
* This is a shortie for `document.createElementNS` method
|
2034
|
+
* which allows you to create a new `HTMLElement` for a given `tagName`
|
2035
|
+
* or based on an object with specific non-readonly attributes:
|
2036
|
+
* `id`, `className`, `textContent`, `style`, etc.
|
2037
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
|
2038
|
+
*
|
2039
|
+
* @param {string} namespace `namespaceURI` to associate with the new `HTMLElement`
|
2040
|
+
* @param {Record<string, string> | string} param `tagName` or object
|
2041
|
+
* @return {HTMLElement | Element} a new `HTMLElement` or `Element`
|
2042
|
+
*/
|
2043
|
+
function createElementNS(namespace, param) {
|
2044
|
+
if (typeof param === 'string') {
|
2045
|
+
return getDocument().createElementNS(namespace, param);
|
2046
|
+
}
|
2047
|
+
|
2048
|
+
const { tagName } = param;
|
2049
|
+
const attr = { ...param };
|
2050
|
+
const newElement = createElementNS(namespace, tagName);
|
2051
|
+
delete attr.tagName;
|
2052
|
+
ObjectAssign(newElement, attr);
|
2053
|
+
return newElement;
|
2054
|
+
}
|
2055
|
+
|
2056
|
+
const vHidden = 'v-hidden';
|
2057
|
+
|
2058
|
+
/**
|
2059
|
+
* Returns the color form for `ColorPicker`.
|
2060
|
+
*
|
2061
|
+
* @param {CP.ColorPicker} self the `ColorPicker` instance
|
2062
|
+
* @returns {HTMLElement | Element} a new `<div>` element with color component `<input>`
|
2063
|
+
*/
|
2064
|
+
function getColorForm(self) {
|
2065
|
+
const { format, id, componentLabels } = self;
|
2066
|
+
const colorForm = createElement({
|
2067
|
+
tagName: 'div',
|
2068
|
+
className: `color-form ${format}`,
|
2069
|
+
});
|
2070
|
+
|
2071
|
+
let components = ['hex'];
|
2072
|
+
if (format === 'rgb') components = ['red', 'green', 'blue', 'alpha'];
|
2073
|
+
else if (format === 'hsl') components = ['hue', 'saturation', 'lightness', 'alpha'];
|
2074
|
+
else if (format === 'hwb') components = ['hue', 'whiteness', 'blackness', 'alpha'];
|
2075
|
+
|
2076
|
+
components.forEach((c) => {
|
2077
|
+
const [C] = format === 'hex' ? ['#'] : toUpperCase(c).split('');
|
2078
|
+
const cID = `color_${format}_${c}_${id}`;
|
2079
|
+
const formatLabel = componentLabels[`${c}Label`];
|
2080
|
+
const cInputLabel = createElement({ tagName: 'label' });
|
2081
|
+
setAttribute(cInputLabel, 'for', cID);
|
2082
|
+
cInputLabel.append(
|
2083
|
+
createElement({ tagName: 'span', ariaHidden: 'true', innerText: `${C}:` }),
|
2084
|
+
createElement({ tagName: 'span', className: vHidden, innerText: formatLabel }),
|
2085
|
+
);
|
2086
|
+
const cInput = createElement({
|
2087
|
+
tagName: 'input',
|
2088
|
+
id: cID,
|
2089
|
+
// name: cID, - prevent saving the value to a form
|
2090
|
+
type: format === 'hex' ? 'text' : 'number',
|
2091
|
+
value: c === 'alpha' ? '100' : '0',
|
2092
|
+
className: `color-input ${c}`,
|
2093
|
+
});
|
2094
|
+
setAttribute(cInput, 'autocomplete', 'off');
|
2095
|
+
setAttribute(cInput, 'spellcheck', 'false');
|
2096
|
+
|
2097
|
+
// alpha
|
2098
|
+
let max = '100';
|
2099
|
+
let step = '1';
|
2100
|
+
if (c !== 'alpha') {
|
2101
|
+
if (format === 'rgb') {
|
2102
|
+
max = '255'; step = '1';
|
2103
|
+
} else if (c === 'hue') {
|
2104
|
+
max = '360'; step = '1';
|
2105
|
+
}
|
2106
|
+
}
|
2107
|
+
ObjectAssign(cInput, {
|
2108
|
+
min: '0',
|
2109
|
+
max,
|
2110
|
+
step,
|
2111
|
+
});
|
2112
|
+
colorForm.append(cInputLabel, cInput);
|
2113
|
+
});
|
2114
|
+
return colorForm;
|
2115
|
+
}
|
2116
|
+
|
2117
|
+
/**
|
2118
|
+
* A global namespace for aria-label.
|
2119
|
+
* @type {string}
|
2120
|
+
*/
|
2121
|
+
const ariaLabel = 'aria-label';
|
2122
|
+
|
2123
|
+
/**
|
2124
|
+
* A global namespace for aria-valuemin.
|
2125
|
+
* @type {string}
|
2126
|
+
*/
|
2127
|
+
const ariaValueMin = 'aria-valuemin';
|
2128
|
+
|
2129
|
+
/**
|
2130
|
+
* A global namespace for aria-valuemax.
|
2131
|
+
* @type {string}
|
2132
|
+
*/
|
2133
|
+
const ariaValueMax = 'aria-valuemax';
|
2134
|
+
|
2135
|
+
/**
|
2136
|
+
* Returns all color controls for `ColorPicker`.
|
2137
|
+
*
|
2138
|
+
* @param {CP.ColorPicker} self the `ColorPicker` instance
|
2139
|
+
* @returns {HTMLElement | Element} color controls
|
2140
|
+
*/
|
2141
|
+
function getColorControls(self) {
|
2142
|
+
const { format, componentLabels } = self;
|
2143
|
+
const {
|
2144
|
+
hueLabel, alphaLabel, lightnessLabel, saturationLabel,
|
2145
|
+
whitenessLabel, blacknessLabel,
|
2146
|
+
} = componentLabels;
|
2147
|
+
|
2148
|
+
const max1 = format === 'hsl' ? 360 : 100;
|
2149
|
+
const max2 = format === 'hsl' ? 100 : 360;
|
2150
|
+
const max3 = 100;
|
2151
|
+
|
2152
|
+
let ctrl1Label = format === 'hsl'
|
2153
|
+
? `${hueLabel} & ${lightnessLabel}`
|
2154
|
+
: `${lightnessLabel} & ${saturationLabel}`;
|
2155
|
+
|
2156
|
+
ctrl1Label = format === 'hwb'
|
2157
|
+
? `${whitenessLabel} & ${blacknessLabel}`
|
2158
|
+
: ctrl1Label;
|
2159
|
+
|
2160
|
+
const ctrl2Label = format === 'hsl'
|
2161
|
+
? `${saturationLabel}`
|
2162
|
+
: `${hueLabel}`;
|
2163
|
+
|
2164
|
+
const colorControls = createElement({
|
2165
|
+
tagName: 'div',
|
2166
|
+
className: `color-controls ${format}`,
|
2167
|
+
});
|
2168
|
+
|
2169
|
+
const colorPointer = 'color-pointer';
|
2170
|
+
const colorSlider = 'color-slider';
|
2171
|
+
|
2172
|
+
const controls = [
|
2173
|
+
{
|
2174
|
+
i: 1,
|
2175
|
+
c: colorPointer,
|
2176
|
+
l: ctrl1Label,
|
2177
|
+
min: 0,
|
2178
|
+
max: max1,
|
2179
|
+
},
|
2180
|
+
{
|
2181
|
+
i: 2,
|
2182
|
+
c: colorSlider,
|
2183
|
+
l: ctrl2Label,
|
2184
|
+
min: 0,
|
2185
|
+
max: max2,
|
2186
|
+
},
|
2187
|
+
{
|
2188
|
+
i: 3,
|
2189
|
+
c: colorSlider,
|
2190
|
+
l: alphaLabel,
|
2191
|
+
min: 0,
|
2192
|
+
max: max3,
|
2193
|
+
},
|
2194
|
+
];
|
2195
|
+
|
2196
|
+
controls.forEach((template) => {
|
2197
|
+
const {
|
2198
|
+
i, c, l, min, max,
|
2199
|
+
} = template;
|
2200
|
+
const control = createElement({
|
2201
|
+
tagName: 'div',
|
2202
|
+
className: 'color-control',
|
2203
|
+
});
|
2204
|
+
setAttribute(control, 'role', 'presentation');
|
2245
2205
|
|
2246
|
-
|
2247
|
-
|
2248
|
-
|
2249
|
-
|
2250
|
-
|
2251
|
-
|
2252
|
-
lightStep = lightSteps > 13 ? estimatedStep : lightStep;
|
2206
|
+
control.append(
|
2207
|
+
createElement({
|
2208
|
+
tagName: 'div',
|
2209
|
+
className: `visual-control visual-control${i}`,
|
2210
|
+
}),
|
2211
|
+
);
|
2253
2212
|
|
2254
|
-
|
2255
|
-
|
2256
|
-
|
2257
|
-
|
2213
|
+
const knob = createElement({
|
2214
|
+
tagName: 'div',
|
2215
|
+
className: `${c} knob`,
|
2216
|
+
ariaLive: 'polite',
|
2217
|
+
});
|
2258
2218
|
|
2259
|
-
|
2260
|
-
|
2261
|
-
|
2262
|
-
}
|
2219
|
+
setAttribute(knob, ariaLabel, l);
|
2220
|
+
setAttribute(knob, 'role', 'slider');
|
2221
|
+
setAttribute(knob, tabIndex, '0');
|
2222
|
+
setAttribute(knob, ariaValueMin, `${min}`);
|
2223
|
+
setAttribute(knob, ariaValueMax, `${max}`);
|
2224
|
+
control.append(knob);
|
2225
|
+
colorControls.append(control);
|
2226
|
+
});
|
2263
2227
|
|
2264
|
-
|
2265
|
-
|
2266
|
-
const currentHue = ((hue + i * hueStep) % 360) / 360;
|
2267
|
-
lightnessArray.forEach((l) => {
|
2268
|
-
colors.push(new Color({ h: currentHue, s: 1, l }).toHexString());
|
2269
|
-
});
|
2270
|
-
}
|
2228
|
+
return colorControls;
|
2229
|
+
}
|
2271
2230
|
|
2272
|
-
|
2273
|
-
|
2274
|
-
|
2275
|
-
|
2276
|
-
|
2231
|
+
/**
|
2232
|
+
* Helps setting CSS variables to the color-menu.
|
2233
|
+
* @param {HTMLElement} element
|
2234
|
+
* @param {Record<string,any>} props
|
2235
|
+
*/
|
2236
|
+
function setCSSProperties(element, props) {
|
2237
|
+
ObjectKeys(props).forEach((prop) => {
|
2238
|
+
element.style.setProperty(prop, props[prop]);
|
2239
|
+
});
|
2277
2240
|
}
|
2278
2241
|
|
2279
2242
|
/**
|
@@ -2309,7 +2272,8 @@
|
|
2309
2272
|
optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
|
2310
2273
|
const menuHeight = `${(rowCount || 1) * optionSize}rem`;
|
2311
2274
|
const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
|
2312
|
-
|
2275
|
+
/** @type {HTMLUListElement} */
|
2276
|
+
// @ts-ignore -- <UL> is an `HTMLElement`
|
2313
2277
|
const menu = createElement({
|
2314
2278
|
tagName: 'ul',
|
2315
2279
|
className: finalClass,
|
@@ -2317,7 +2281,7 @@
|
|
2317
2281
|
setAttribute(menu, 'role', 'listbox');
|
2318
2282
|
setAttribute(menu, ariaLabel, menuLabel);
|
2319
2283
|
|
2320
|
-
if (isScrollable) {
|
2284
|
+
if (isScrollable) {
|
2321
2285
|
setCSSProperties(menu, {
|
2322
2286
|
'--grid-item-size': `${optionSize}rem`,
|
2323
2287
|
'--grid-fit': fit,
|
@@ -2328,15 +2292,19 @@
|
|
2328
2292
|
}
|
2329
2293
|
|
2330
2294
|
colorsArray.forEach((x) => {
|
2331
|
-
|
2332
|
-
|
2333
|
-
|
2295
|
+
let [value, label] = typeof x === 'string' ? x.trim().split(':') : [];
|
2296
|
+
if (x instanceof Color) {
|
2297
|
+
value = x.toHexString();
|
2298
|
+
label = value;
|
2299
|
+
}
|
2300
|
+
const color = new Color(x instanceof Color ? x : value, format);
|
2301
|
+
const isActive = color.toString() === getAttribute(input, 'value');
|
2334
2302
|
const active = isActive ? ' active' : '';
|
2335
2303
|
|
2336
2304
|
const option = createElement({
|
2337
2305
|
tagName: 'li',
|
2338
2306
|
className: `color-option${active}`,
|
2339
|
-
innerText: `${label ||
|
2307
|
+
innerText: `${label || value}`,
|
2340
2308
|
});
|
2341
2309
|
|
2342
2310
|
setAttribute(option, tabIndex, '0');
|
@@ -2345,7 +2313,7 @@
|
|
2345
2313
|
setAttribute(option, ariaSelected, isActive ? 'true' : 'false');
|
2346
2314
|
|
2347
2315
|
if (isOptionsMenu) {
|
2348
|
-
setElementStyle(option, { backgroundColor:
|
2316
|
+
setElementStyle(option, { backgroundColor: value });
|
2349
2317
|
}
|
2350
2318
|
|
2351
2319
|
menu.append(option);
|
@@ -2354,55 +2322,10 @@
|
|
2354
2322
|
}
|
2355
2323
|
|
2356
2324
|
/**
|
2357
|
-
|
2358
|
-
|
2359
|
-
|
2360
|
-
|
2361
|
-
function isValidJSON(str) {
|
2362
|
-
try {
|
2363
|
-
JSON.parse(str);
|
2364
|
-
} catch (e) {
|
2365
|
-
return false;
|
2366
|
-
}
|
2367
|
-
return true;
|
2368
|
-
}
|
2369
|
-
|
2370
|
-
var version = "0.0.1alpha3";
|
2371
|
-
|
2372
|
-
// @ts-ignore
|
2373
|
-
|
2374
|
-
const Version = version;
|
2375
|
-
|
2376
|
-
// ColorPicker GC
|
2377
|
-
// ==============
|
2378
|
-
const colorPickerString = 'color-picker';
|
2379
|
-
const colorPickerSelector = `[data-function="${colorPickerString}"]`;
|
2380
|
-
const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
|
2381
|
-
const colorPickerDefaults = {
|
2382
|
-
componentLabels: colorPickerLabels,
|
2383
|
-
colorLabels: colorNames,
|
2384
|
-
format: 'rgb',
|
2385
|
-
colorPresets: false,
|
2386
|
-
colorKeywords: false,
|
2387
|
-
};
|
2388
|
-
|
2389
|
-
// ColorPicker Static Methods
|
2390
|
-
// ==========================
|
2391
|
-
|
2392
|
-
/** @type {CP.GetInstance<ColorPicker>} */
|
2393
|
-
const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
|
2394
|
-
|
2395
|
-
/** @type {CP.InitCallback<ColorPicker>} */
|
2396
|
-
const initColorPicker = (element) => new ColorPicker(element);
|
2397
|
-
|
2398
|
-
// ColorPicker Private Methods
|
2399
|
-
// ===========================
|
2400
|
-
|
2401
|
-
/**
|
2402
|
-
* Generate HTML markup and update instance properties.
|
2403
|
-
* @param {ColorPicker} self
|
2404
|
-
*/
|
2405
|
-
function initCallback(self) {
|
2325
|
+
* Generate HTML markup and update instance properties.
|
2326
|
+
* @param {CP.ColorPicker} self
|
2327
|
+
*/
|
2328
|
+
function setMarkup(self) {
|
2406
2329
|
const {
|
2407
2330
|
input, parent, format, id, componentLabels, colorKeywords, colorPresets,
|
2408
2331
|
} = self;
|
@@ -2417,9 +2340,7 @@
|
|
2417
2340
|
self.color = new Color(color, format);
|
2418
2341
|
|
2419
2342
|
// set initial controls dimensions
|
2420
|
-
|
2421
|
-
const dropClass = isMobile ? ' mobile' : '';
|
2422
|
-
const formatString = format === 'hex' ? hexLabel : format.toUpperCase();
|
2343
|
+
const formatString = format === 'hex' ? hexLabel : toUpperCase(format);
|
2423
2344
|
|
2424
2345
|
const pickerBtn = createElement({
|
2425
2346
|
id: `picker-btn-${id}`,
|
@@ -2436,7 +2357,7 @@
|
|
2436
2357
|
|
2437
2358
|
const pickerDropdown = createElement({
|
2438
2359
|
tagName: 'div',
|
2439
|
-
className:
|
2360
|
+
className: 'color-dropdown picker',
|
2440
2361
|
});
|
2441
2362
|
setAttribute(pickerDropdown, ariaLabelledBy, `picker-btn-${id}`);
|
2442
2363
|
setAttribute(pickerDropdown, 'role', 'group');
|
@@ -2452,7 +2373,7 @@
|
|
2452
2373
|
if (colorKeywords || colorPresets) {
|
2453
2374
|
const presetsDropdown = createElement({
|
2454
2375
|
tagName: 'div',
|
2455
|
-
className:
|
2376
|
+
className: 'color-dropdown scrollable menu',
|
2456
2377
|
});
|
2457
2378
|
|
2458
2379
|
// color presets
|
@@ -2502,6 +2423,37 @@
|
|
2502
2423
|
setAttribute(input, tabIndex, '-1');
|
2503
2424
|
}
|
2504
2425
|
|
2426
|
+
var version = "0.0.2alpha2";
|
2427
|
+
|
2428
|
+
// @ts-ignore
|
2429
|
+
|
2430
|
+
const Version = version;
|
2431
|
+
|
2432
|
+
// ColorPicker GC
|
2433
|
+
// ==============
|
2434
|
+
const colorPickerString = 'color-picker';
|
2435
|
+
const colorPickerSelector = `[data-function="${colorPickerString}"]`;
|
2436
|
+
const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
|
2437
|
+
const colorPickerDefaults = {
|
2438
|
+
componentLabels: colorPickerLabels,
|
2439
|
+
colorLabels: colorNames,
|
2440
|
+
format: 'rgb',
|
2441
|
+
colorPresets: false,
|
2442
|
+
colorKeywords: false,
|
2443
|
+
};
|
2444
|
+
|
2445
|
+
// ColorPicker Static Methods
|
2446
|
+
// ==========================
|
2447
|
+
|
2448
|
+
/** @type {CP.GetInstance<ColorPicker>} */
|
2449
|
+
const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
|
2450
|
+
|
2451
|
+
/** @type {CP.InitCallback<ColorPicker>} */
|
2452
|
+
const initColorPicker = (element) => new ColorPicker(element);
|
2453
|
+
|
2454
|
+
// ColorPicker Private Methods
|
2455
|
+
// ===========================
|
2456
|
+
|
2505
2457
|
/**
|
2506
2458
|
* Add / remove `ColorPicker` main event listeners.
|
2507
2459
|
* @param {ColorPicker} self
|
@@ -2514,8 +2466,6 @@
|
|
2514
2466
|
fn(input, focusinEvent, self.showPicker);
|
2515
2467
|
fn(pickerToggle, mouseclickEvent, self.togglePicker);
|
2516
2468
|
|
2517
|
-
fn(input, keydownEvent, self.keyToggle);
|
2518
|
-
|
2519
2469
|
if (menuToggle) {
|
2520
2470
|
fn(menuToggle, mouseclickEvent, self.toggleMenu);
|
2521
2471
|
}
|
@@ -2553,8 +2503,7 @@
|
|
2553
2503
|
fn(doc, pointerEvents.move, self.pointerMove);
|
2554
2504
|
fn(doc, pointerEvents.up, self.pointerUp);
|
2555
2505
|
fn(parent, focusoutEvent, self.handleFocusOut);
|
2556
|
-
|
2557
|
-
fn(win, keyupEvent, self.handleDismiss);
|
2506
|
+
fn(doc, keyupEvent, self.handleDismiss);
|
2558
2507
|
}
|
2559
2508
|
|
2560
2509
|
/**
|
@@ -2638,7 +2587,7 @@
|
|
2638
2587
|
const input = querySelector(target);
|
2639
2588
|
|
2640
2589
|
// invalidate
|
2641
|
-
if (!input) throw new TypeError(`ColorPicker target ${target} cannot be found.`);
|
2590
|
+
if (!input) throw new TypeError(`ColorPicker target "${target}" cannot be found.`);
|
2642
2591
|
self.input = input;
|
2643
2592
|
|
2644
2593
|
const parent = closest(input, colorPickerParentSelector);
|
@@ -2685,15 +2634,14 @@
|
|
2685
2634
|
});
|
2686
2635
|
|
2687
2636
|
// update and expose component labels
|
2688
|
-
const
|
2689
|
-
|
2690
|
-
? JSON.parse(componentLabels) : componentLabels || {};
|
2637
|
+
const tempComponentLabels = componentLabels && isValidJSON(componentLabels)
|
2638
|
+
? JSON.parse(componentLabels) : componentLabels;
|
2691
2639
|
|
2692
2640
|
/** @type {Record<string, string>} */
|
2693
|
-
self.componentLabels = ObjectAssign(
|
2641
|
+
self.componentLabels = ObjectAssign(colorPickerLabels, tempComponentLabels);
|
2694
2642
|
|
2695
2643
|
/** @type {Color} */
|
2696
|
-
self.color = new Color('
|
2644
|
+
self.color = new Color(input.value || '#fff', format);
|
2697
2645
|
|
2698
2646
|
/** @type {CP.ColorFormats} */
|
2699
2647
|
self.format = format;
|
@@ -2702,7 +2650,7 @@
|
|
2702
2650
|
if (colorKeywords instanceof Array) {
|
2703
2651
|
self.colorKeywords = colorKeywords;
|
2704
2652
|
} else if (typeof colorKeywords === 'string' && colorKeywords.length) {
|
2705
|
-
self.colorKeywords = colorKeywords.split(',');
|
2653
|
+
self.colorKeywords = colorKeywords.split(',').map((x) => x.trim());
|
2706
2654
|
}
|
2707
2655
|
|
2708
2656
|
// set colour presets
|
@@ -2731,11 +2679,10 @@
|
|
2731
2679
|
self.handleFocusOut = self.handleFocusOut.bind(self);
|
2732
2680
|
self.changeHandler = self.changeHandler.bind(self);
|
2733
2681
|
self.handleDismiss = self.handleDismiss.bind(self);
|
2734
|
-
self.keyToggle = self.keyToggle.bind(self);
|
2735
2682
|
self.handleKnobs = self.handleKnobs.bind(self);
|
2736
2683
|
|
2737
2684
|
// generate markup
|
2738
|
-
|
2685
|
+
setMarkup(self);
|
2739
2686
|
|
2740
2687
|
const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
|
2741
2688
|
// set main elements
|
@@ -2823,76 +2770,83 @@
|
|
2823
2770
|
return inputValue !== '' && new Color(inputValue).isValid;
|
2824
2771
|
}
|
2825
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
|
+
|
2826
2821
|
/** Updates `ColorPicker` visuals. */
|
2827
2822
|
updateVisuals() {
|
2828
2823
|
const self = this;
|
2829
2824
|
const {
|
2830
|
-
|
2825
|
+
controlPositions, visuals,
|
2831
2826
|
} = self;
|
2832
2827
|
const [v1, v2, v3] = visuals;
|
2833
|
-
const {
|
2834
|
-
const hue =
|
2835
|
-
|
2836
|
-
: controlPositions.c2y / offsetHeight;
|
2837
|
-
// @ts-ignore - `hslToRgb` is assigned to `Color` as static method
|
2838
|
-
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();
|
2839
2831
|
const whiteGrad = 'linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)';
|
2840
2832
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
2841
2833
|
const roundA = roundPart((alpha * 100)) / 100;
|
2842
2834
|
|
2843
|
-
|
2844
|
-
|
2845
|
-
|
2846
|
-
|
2847
|
-
|
2848
|
-
|
2849
|
-
|
2850
|
-
|
2851
|
-
|
2852
|
-
|
2853
|
-
|
2854
|
-
|
2855
|
-
|
2856
|
-
|
2857
|
-
setElementStyle(v2, { background: hueGradient });
|
2858
|
-
} else {
|
2859
|
-
const saturation = roundPart((controlPositions.c2y / offsetHeight) * 100);
|
2860
|
-
const fill0 = new Color({
|
2861
|
-
r: 255, g: 0, b: 0, a: alpha,
|
2862
|
-
}).saturate(-saturation).toRgbString();
|
2863
|
-
const fill1 = new Color({
|
2864
|
-
r: 255, g: 255, b: 0, a: alpha,
|
2865
|
-
}).saturate(-saturation).toRgbString();
|
2866
|
-
const fill2 = new Color({
|
2867
|
-
r: 0, g: 255, b: 0, a: alpha,
|
2868
|
-
}).saturate(-saturation).toRgbString();
|
2869
|
-
const fill3 = new Color({
|
2870
|
-
r: 0, g: 255, b: 255, a: alpha,
|
2871
|
-
}).saturate(-saturation).toRgbString();
|
2872
|
-
const fill4 = new Color({
|
2873
|
-
r: 0, g: 0, b: 255, a: alpha,
|
2874
|
-
}).saturate(-saturation).toRgbString();
|
2875
|
-
const fill5 = new Color({
|
2876
|
-
r: 255, g: 0, b: 255, a: alpha,
|
2877
|
-
}).saturate(-saturation).toRgbString();
|
2878
|
-
const fill6 = new Color({
|
2879
|
-
r: 255, g: 0, b: 0, a: alpha,
|
2880
|
-
}).saturate(-saturation).toRgbString();
|
2881
|
-
const fillGradient = `linear-gradient(to right,
|
2882
|
-
${fill0} 0%, ${fill1} 16.67%, ${fill2} 33.33%, ${fill3} 50%,
|
2883
|
-
${fill4} 66.67%, ${fill5} 83.33%, ${fill6} 100%)`;
|
2884
|
-
const lightGrad = `linear-gradient(rgba(255,255,255,${roundA}) 0%, rgba(255,255,255,0) 50%),
|
2885
|
-
linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,${roundA}) 100%)`;
|
2886
|
-
|
2887
|
-
setElementStyle(v1, { background: `${lightGrad},${fillGradient},${whiteGrad}` });
|
2888
|
-
const {
|
2889
|
-
r: gr, g: gg, b: gb,
|
2890
|
-
} = 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 });
|
2891
2849
|
|
2892
|
-
setElementStyle(v2, {
|
2893
|
-
background: `linear-gradient(rgb(${r},${g},${b}) 0%, rgb(${gr},${gg},${gb}) 100%)`,
|
2894
|
-
});
|
2895
|
-
}
|
2896
2850
|
setElementStyle(v3, {
|
2897
2851
|
background: `linear-gradient(rgba(${r},${g},${b},1) 0%,rgba(${r},${g},${b},0) 100%)`,
|
2898
2852
|
});
|
@@ -2931,7 +2885,7 @@
|
|
2931
2885
|
const self = this;
|
2932
2886
|
const { activeElement } = getDocument(self.input);
|
2933
2887
|
|
2934
|
-
if ((
|
2888
|
+
if ((e.type === touchmoveEvent && self.dragElement)
|
2935
2889
|
|| (activeElement && self.controlKnobs.includes(activeElement))) {
|
2936
2890
|
e.stopPropagation();
|
2937
2891
|
e.preventDefault();
|
@@ -3042,13 +2996,13 @@
|
|
3042
2996
|
const [v1, v2, v3] = visuals;
|
3043
2997
|
const [c1, c2, c3] = controlKnobs;
|
3044
2998
|
/** @type {HTMLElement} */
|
3045
|
-
const visual =
|
3046
|
-
? target : querySelector('.visual-control', target.parentElement);
|
2999
|
+
const visual = controlKnobs.includes(target) ? target.previousElementSibling : target;
|
3047
3000
|
const visualRect = getBoundingClientRect(visual);
|
3001
|
+
const html = getDocumentElement(v1);
|
3048
3002
|
const X = type === 'touchstart' ? touches[0].pageX : pageX;
|
3049
3003
|
const Y = type === 'touchstart' ? touches[0].pageY : pageY;
|
3050
|
-
const offsetX = X -
|
3051
|
-
const offsetY = Y -
|
3004
|
+
const offsetX = X - html.scrollLeft - visualRect.left;
|
3005
|
+
const offsetY = Y - html.scrollTop - visualRect.top;
|
3052
3006
|
|
3053
3007
|
if (target === v1 || target === c1) {
|
3054
3008
|
self.dragElement = visual;
|
@@ -3108,10 +3062,11 @@
|
|
3108
3062
|
if (!dragElement) return;
|
3109
3063
|
|
3110
3064
|
const controlRect = getBoundingClientRect(dragElement);
|
3111
|
-
const
|
3112
|
-
const
|
3113
|
-
const
|
3114
|
-
const
|
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;
|
3115
3070
|
|
3116
3071
|
if (dragElement === v1) {
|
3117
3072
|
self.changeControl1(offsetX, offsetY);
|
@@ -3138,30 +3093,41 @@
|
|
3138
3093
|
if (![keyArrowUp, keyArrowDown, keyArrowLeft, keyArrowRight].includes(code)) return;
|
3139
3094
|
e.preventDefault();
|
3140
3095
|
|
3141
|
-
const { controlKnobs } = self;
|
3096
|
+
const { controlKnobs, visuals } = self;
|
3097
|
+
const { offsetWidth, offsetHeight } = visuals[0];
|
3142
3098
|
const [c1, c2, c3] = controlKnobs;
|
3143
3099
|
const { activeElement } = getDocument(c1);
|
3144
3100
|
const currentKnob = controlKnobs.find((x) => x === activeElement);
|
3101
|
+
const yRatio = offsetHeight / 360;
|
3145
3102
|
|
3146
3103
|
if (currentKnob) {
|
3147
3104
|
let offsetX = 0;
|
3148
3105
|
let offsetY = 0;
|
3106
|
+
|
3149
3107
|
if (target === c1) {
|
3108
|
+
const xRatio = offsetWidth / 100;
|
3109
|
+
|
3150
3110
|
if ([keyArrowLeft, keyArrowRight].includes(code)) {
|
3151
|
-
self.controlPositions.c1x += code === keyArrowRight ?
|
3111
|
+
self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
|
3152
3112
|
} else if ([keyArrowUp, keyArrowDown].includes(code)) {
|
3153
|
-
self.controlPositions.c1y += code === keyArrowDown ?
|
3113
|
+
self.controlPositions.c1y += code === keyArrowDown ? yRatio : -yRatio;
|
3154
3114
|
}
|
3155
3115
|
|
3156
3116
|
offsetX = self.controlPositions.c1x;
|
3157
3117
|
offsetY = self.controlPositions.c1y;
|
3158
3118
|
self.changeControl1(offsetX, offsetY);
|
3159
3119
|
} else if (target === c2) {
|
3160
|
-
self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
|
3120
|
+
self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
|
3121
|
+
? yRatio
|
3122
|
+
: -yRatio;
|
3123
|
+
|
3161
3124
|
offsetY = self.controlPositions.c2y;
|
3162
3125
|
self.changeControl2(offsetY);
|
3163
3126
|
} else if (target === c3) {
|
3164
|
-
self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
|
3127
|
+
self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
|
3128
|
+
? yRatio
|
3129
|
+
: -yRatio;
|
3130
|
+
|
3165
3131
|
offsetY = self.controlPositions.c3y;
|
3166
3132
|
self.changeAlpha(offsetY);
|
3167
3133
|
}
|
@@ -3189,7 +3155,7 @@
|
|
3189
3155
|
if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
|
3190
3156
|
if (activeElement === input) {
|
3191
3157
|
if (isNonColorValue) {
|
3192
|
-
colorSource = '
|
3158
|
+
colorSource = currentValue === 'transparent' ? 'rgba(0,0,0,0)' : 'rgb(0,0,0)';
|
3193
3159
|
} else {
|
3194
3160
|
colorSource = currentValue;
|
3195
3161
|
}
|
@@ -3240,9 +3206,7 @@
|
|
3240
3206
|
changeControl1(X, Y) {
|
3241
3207
|
const self = this;
|
3242
3208
|
let [offsetX, offsetY] = [0, 0];
|
3243
|
-
const {
|
3244
|
-
format, controlPositions, visuals,
|
3245
|
-
} = self;
|
3209
|
+
const { controlPositions, visuals } = self;
|
3246
3210
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3247
3211
|
|
3248
3212
|
if (X > offsetWidth) offsetX = offsetWidth;
|
@@ -3251,29 +3215,19 @@
|
|
3251
3215
|
if (Y > offsetHeight) offsetY = offsetHeight;
|
3252
3216
|
else if (Y >= 0) offsetY = Y;
|
3253
3217
|
|
3254
|
-
const hue =
|
3255
|
-
? offsetX / offsetWidth
|
3256
|
-
: controlPositions.c2y / offsetHeight;
|
3218
|
+
const hue = controlPositions.c2y / offsetHeight;
|
3257
3219
|
|
3258
|
-
const saturation =
|
3259
|
-
? 1 - controlPositions.c2y / offsetHeight
|
3260
|
-
: offsetX / offsetWidth;
|
3220
|
+
const saturation = offsetX / offsetWidth;
|
3261
3221
|
|
3262
3222
|
const lightness = 1 - offsetY / offsetHeight;
|
3263
3223
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
3264
3224
|
|
3265
|
-
const colorObject = format === 'hsl'
|
3266
|
-
? {
|
3267
|
-
h: hue, s: saturation, l: lightness, a: alpha,
|
3268
|
-
}
|
3269
|
-
: {
|
3270
|
-
h: hue, s: saturation, v: lightness, a: alpha,
|
3271
|
-
};
|
3272
|
-
|
3273
3225
|
// new color
|
3274
3226
|
const {
|
3275
3227
|
r, g, b, a,
|
3276
|
-
} = new Color(
|
3228
|
+
} = new Color({
|
3229
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
3230
|
+
});
|
3277
3231
|
|
3278
3232
|
ObjectAssign(self.color, {
|
3279
3233
|
r, g, b, a,
|
@@ -3300,7 +3254,7 @@
|
|
3300
3254
|
changeControl2(Y) {
|
3301
3255
|
const self = this;
|
3302
3256
|
const {
|
3303
|
-
|
3257
|
+
controlPositions, visuals,
|
3304
3258
|
} = self;
|
3305
3259
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3306
3260
|
|
@@ -3309,26 +3263,17 @@
|
|
3309
3263
|
if (Y > offsetHeight) offsetY = offsetHeight;
|
3310
3264
|
else if (Y >= 0) offsetY = Y;
|
3311
3265
|
|
3312
|
-
const hue =
|
3313
|
-
|
3314
|
-
: offsetY / offsetHeight;
|
3315
|
-
const saturation = format === 'hsl'
|
3316
|
-
? 1 - offsetY / offsetHeight
|
3317
|
-
: controlPositions.c1x / offsetWidth;
|
3266
|
+
const hue = offsetY / offsetHeight;
|
3267
|
+
const saturation = controlPositions.c1x / offsetWidth;
|
3318
3268
|
const lightness = 1 - controlPositions.c1y / offsetHeight;
|
3319
3269
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
3320
|
-
const colorObject = format === 'hsl'
|
3321
|
-
? {
|
3322
|
-
h: hue, s: saturation, l: lightness, a: alpha,
|
3323
|
-
}
|
3324
|
-
: {
|
3325
|
-
h: hue, s: saturation, v: lightness, a: alpha,
|
3326
|
-
};
|
3327
3270
|
|
3328
3271
|
// new color
|
3329
3272
|
const {
|
3330
3273
|
r, g, b, a,
|
3331
|
-
} = new Color(
|
3274
|
+
} = new Color({
|
3275
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
3276
|
+
});
|
3332
3277
|
|
3333
3278
|
ObjectAssign(self.color, {
|
3334
3279
|
r, g, b, a,
|
@@ -3415,18 +3360,18 @@
|
|
3415
3360
|
setControlPositions() {
|
3416
3361
|
const self = this;
|
3417
3362
|
const {
|
3418
|
-
|
3363
|
+
visuals, color, hsv,
|
3419
3364
|
} = self;
|
3420
3365
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3421
3366
|
const alpha = color.a;
|
3422
|
-
const hue =
|
3367
|
+
const hue = hsv.h;
|
3423
3368
|
|
3424
|
-
const saturation =
|
3425
|
-
const lightness =
|
3369
|
+
const saturation = hsv.s;
|
3370
|
+
const lightness = hsv.v;
|
3426
3371
|
|
3427
|
-
self.controlPositions.c1x =
|
3372
|
+
self.controlPositions.c1x = saturation * offsetWidth;
|
3428
3373
|
self.controlPositions.c1y = (1 - lightness) * offsetHeight;
|
3429
|
-
self.controlPositions.c2y =
|
3374
|
+
self.controlPositions.c2y = hue * offsetHeight;
|
3430
3375
|
self.controlPositions.c3y = (1 - alpha) * offsetHeight;
|
3431
3376
|
}
|
3432
3377
|
|
@@ -3434,78 +3379,40 @@
|
|
3434
3379
|
updateAppearance() {
|
3435
3380
|
const self = this;
|
3436
3381
|
const {
|
3437
|
-
componentLabels,
|
3438
|
-
|
3382
|
+
componentLabels, color, parent,
|
3383
|
+
hsv, hex, format, controlKnobs,
|
3439
3384
|
} = self;
|
3440
3385
|
const {
|
3441
3386
|
appearanceLabel, hexLabel, valueLabel,
|
3442
3387
|
} = componentLabels;
|
3443
|
-
|
3388
|
+
let { r, g, b } = color.toRgb();
|
3444
3389
|
const [knob1, knob2, knob3] = controlKnobs;
|
3445
|
-
const hue = roundPart(
|
3390
|
+
const hue = roundPart(hsv.h * 360);
|
3446
3391
|
const alpha = color.a;
|
3447
|
-
const
|
3448
|
-
const
|
3449
|
-
const
|
3450
|
-
const hsvl = hsv.v * 100;
|
3451
|
-
let colorName;
|
3452
|
-
|
3453
|
-
// determine color appearance
|
3454
|
-
if (lightness === 100 && saturation === 0) {
|
3455
|
-
colorName = colorLabels.white;
|
3456
|
-
} else if (lightness === 0) {
|
3457
|
-
colorName = colorLabels.black;
|
3458
|
-
} else if (saturation === 0) {
|
3459
|
-
colorName = colorLabels.grey;
|
3460
|
-
} else if (hue < 15 || hue >= 345) {
|
3461
|
-
colorName = colorLabels.red;
|
3462
|
-
} else if (hue >= 15 && hue < 45) {
|
3463
|
-
colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
|
3464
|
-
} else if (hue >= 45 && hue < 75) {
|
3465
|
-
const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
|
3466
|
-
const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
|
3467
|
-
colorName = isGold ? colorLabels.gold : colorLabels.yellow;
|
3468
|
-
colorName = isOlive ? colorLabels.olive : colorName;
|
3469
|
-
} else if (hue >= 75 && hue < 155) {
|
3470
|
-
colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
|
3471
|
-
} else if (hue >= 155 && hue < 175) {
|
3472
|
-
colorName = colorLabels.teal;
|
3473
|
-
} else if (hue >= 175 && hue < 195) {
|
3474
|
-
colorName = colorLabels.cyan;
|
3475
|
-
} else if (hue >= 195 && hue < 255) {
|
3476
|
-
colorName = colorLabels.blue;
|
3477
|
-
} else if (hue >= 255 && hue < 270) {
|
3478
|
-
colorName = colorLabels.violet;
|
3479
|
-
} else if (hue >= 270 && hue < 295) {
|
3480
|
-
colorName = colorLabels.magenta;
|
3481
|
-
} else if (hue >= 295 && hue < 345) {
|
3482
|
-
colorName = colorLabels.pink;
|
3483
|
-
}
|
3392
|
+
const saturation = roundPart(hsv.s * 100);
|
3393
|
+
const lightness = roundPart(hsv.v * 100);
|
3394
|
+
const colorName = self.appearance;
|
3484
3395
|
|
3485
3396
|
let colorLabel = `${hexLabel} ${hex.split('').join(' ')}`;
|
3486
3397
|
|
3487
|
-
if (format === '
|
3488
|
-
colorLabel = `HSL: ${hue}°, ${saturation}%, ${lightness}%`;
|
3489
|
-
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3490
|
-
setAttribute(knob1, ariaValueText, `${hue}° & ${lightness}%`);
|
3491
|
-
setAttribute(knob1, ariaValueNow, `${hue}`);
|
3492
|
-
setAttribute(knob2, ariaValueText, `${saturation}%`);
|
3493
|
-
setAttribute(knob2, ariaValueNow, `${saturation}`);
|
3494
|
-
} else if (format === 'hwb') {
|
3398
|
+
if (format === 'hwb') {
|
3495
3399
|
const { hwb } = self;
|
3496
3400
|
const whiteness = roundPart(hwb.w * 100);
|
3497
3401
|
const blackness = roundPart(hwb.b * 100);
|
3498
3402
|
colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
|
3499
|
-
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3500
3403
|
setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
|
3501
3404
|
setAttribute(knob1, ariaValueNow, `${whiteness}`);
|
3405
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3502
3406
|
setAttribute(knob2, ariaValueText, `${hue}%`);
|
3503
3407
|
setAttribute(knob2, ariaValueNow, `${hue}`);
|
3504
3408
|
} else {
|
3409
|
+
[r, g, b] = [r, g, b].map(roundPart);
|
3410
|
+
colorLabel = format === 'hsl' ? `HSL: ${hue}°, ${saturation}%, ${lightness}%` : colorLabel;
|
3505
3411
|
colorLabel = format === 'rgb' ? `RGB: ${r}, ${g}, ${b}` : colorLabel;
|
3506
|
-
|
3412
|
+
|
3507
3413
|
setAttribute(knob1, ariaValueText, `${lightness}% & ${saturation}%`);
|
3508
3414
|
setAttribute(knob1, ariaValueNow, `${lightness}`);
|
3415
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3509
3416
|
setAttribute(knob2, ariaValueText, `${hue}°`);
|
3510
3417
|
setAttribute(knob2, ariaValueNow, `${hue}`);
|
3511
3418
|
}
|
@@ -3533,10 +3440,12 @@
|
|
3533
3440
|
/** Updates the control knobs actual positions. */
|
3534
3441
|
updateControls() {
|
3535
3442
|
const { controlKnobs, controlPositions } = this;
|
3536
|
-
|
3443
|
+
let {
|
3537
3444
|
c1x, c1y, c2y, c3y,
|
3538
3445
|
} = controlPositions;
|
3539
3446
|
const [control1, control2, control3] = controlKnobs;
|
3447
|
+
// round control positions
|
3448
|
+
[c1x, c1y, c2y, c3y] = [c1x, c1y, c2y, c3y].map(roundPart);
|
3540
3449
|
|
3541
3450
|
setElementStyle(control1, { transform: `translate3d(${c1x - 4}px,${c1y - 4}px,0)` });
|
3542
3451
|
setElementStyle(control2, { transform: `translate3d(0,${c2y - 4}px,0)` });
|
@@ -3579,7 +3488,8 @@
|
|
3579
3488
|
i3.value = `${blackness}`;
|
3580
3489
|
i4.value = `${alpha}`;
|
3581
3490
|
} else if (format === 'rgb') {
|
3582
|
-
|
3491
|
+
let { r, g, b } = self.rgb;
|
3492
|
+
[r, g, b] = [r, g, b].map(roundPart);
|
3583
3493
|
|
3584
3494
|
newColor = self.color.toRgbString();
|
3585
3495
|
i1.value = `${r}`;
|
@@ -3597,37 +3507,13 @@
|
|
3597
3507
|
}
|
3598
3508
|
}
|
3599
3509
|
|
3600
|
-
/**
|
3601
|
-
* The `Space` & `Enter` keys specific event listener.
|
3602
|
-
* Toggle visibility of the `ColorPicker` / the presets menu, showing one will hide the other.
|
3603
|
-
* @param {KeyboardEvent} e
|
3604
|
-
* @this {ColorPicker}
|
3605
|
-
*/
|
3606
|
-
keyToggle(e) {
|
3607
|
-
const self = this;
|
3608
|
-
const { menuToggle } = self;
|
3609
|
-
const { activeElement } = getDocument(menuToggle);
|
3610
|
-
const { code } = e;
|
3611
|
-
|
3612
|
-
if ([keyEnter, keySpace].includes(code)) {
|
3613
|
-
if ((menuToggle && activeElement === menuToggle) || !activeElement) {
|
3614
|
-
e.preventDefault();
|
3615
|
-
if (!activeElement) {
|
3616
|
-
self.togglePicker(e);
|
3617
|
-
} else {
|
3618
|
-
self.toggleMenu();
|
3619
|
-
}
|
3620
|
-
}
|
3621
|
-
}
|
3622
|
-
}
|
3623
|
-
|
3624
3510
|
/**
|
3625
3511
|
* Toggle the `ColorPicker` dropdown visibility.
|
3626
|
-
* @param {Event} e
|
3512
|
+
* @param {Event=} e
|
3627
3513
|
* @this {ColorPicker}
|
3628
3514
|
*/
|
3629
3515
|
togglePicker(e) {
|
3630
|
-
e.preventDefault();
|
3516
|
+
if (e) e.preventDefault();
|
3631
3517
|
const self = this;
|
3632
3518
|
const { colorPicker } = self;
|
3633
3519
|
|
@@ -3648,8 +3534,13 @@
|
|
3648
3534
|
}
|
3649
3535
|
}
|
3650
3536
|
|
3651
|
-
/**
|
3652
|
-
|
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();
|
3653
3544
|
const self = this;
|
3654
3545
|
const { colorMenu } = self;
|
3655
3546
|
|
@@ -3675,6 +3566,10 @@
|
|
3675
3566
|
const relatedBtn = openPicker ? pickerToggle : menuToggle;
|
3676
3567
|
const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
|
3677
3568
|
|
3569
|
+
// if (!self.isValid) {
|
3570
|
+
self.value = self.color.toString(true);
|
3571
|
+
// }
|
3572
|
+
|
3678
3573
|
if (openDropdown) {
|
3679
3574
|
removeClass(openDropdown, 'show');
|
3680
3575
|
setAttribute(relatedBtn, ariaExpanded, 'false');
|
@@ -3688,9 +3583,6 @@
|
|
3688
3583
|
}, animationDuration);
|
3689
3584
|
}
|
3690
3585
|
|
3691
|
-
if (!self.isValid) {
|
3692
|
-
self.value = self.color.toString();
|
3693
|
-
}
|
3694
3586
|
if (!focusPrevented) {
|
3695
3587
|
focus(pickerToggle);
|
3696
3588
|
}
|
@@ -3735,4 +3627,4 @@
|
|
3735
3627
|
|
3736
3628
|
return ColorPicker;
|
3737
3629
|
|
3738
|
-
}))
|
3630
|
+
}));
|