@thednp/color-picker 0.0.1 → 0.0.2-alpha3
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 +1 -1
- package/dist/css/color-picker.min.css +1 -1
- package/dist/css/color-picker.rtl.css +1 -1
- package/dist/css/color-picker.rtl.min.css +1 -1
- 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 +543 -671
- package/dist/js/color-picker-element-esm.min.js +2 -2
- package/dist/js/color-picker-element.js +545 -673
- package/dist/js/color-picker-element.min.js +2 -2
- package/dist/js/color-picker-esm.js +758 -878
- package/dist/js/color-picker-esm.min.js +2 -2
- package/dist/js/color-picker.js +760 -880
- 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 +137 -325
- package/src/js/color.js +169 -185
- 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/types/cp.d.ts +64 -32
- package/types/source/types.d.ts +1 -1
- package/src/js/util/templates.js +0 -10
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
* ColorPicker v0.0.
|
2
|
+
* ColorPicker v0.0.2alpha3 (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
|
*/
|
@@ -129,24 +129,6 @@ const ariaValueText = 'aria-valuetext';
|
|
129
129
|
*/
|
130
130
|
const ariaValueNow = 'aria-valuenow';
|
131
131
|
|
132
|
-
/**
|
133
|
-
* A global namespace for aria-haspopup.
|
134
|
-
* @type {string}
|
135
|
-
*/
|
136
|
-
const ariaHasPopup = 'aria-haspopup';
|
137
|
-
|
138
|
-
/**
|
139
|
-
* A global namespace for aria-hidden.
|
140
|
-
* @type {string}
|
141
|
-
*/
|
142
|
-
const ariaHidden = 'aria-hidden';
|
143
|
-
|
144
|
-
/**
|
145
|
-
* A global namespace for aria-labelledby.
|
146
|
-
* @type {string}
|
147
|
-
*/
|
148
|
-
const ariaLabelledBy = 'aria-labelledby';
|
149
|
-
|
150
132
|
/**
|
151
133
|
* A global namespace for `ArrowDown` key.
|
152
134
|
* @type {string} e.which = 40 equivalent
|
@@ -273,37 +255,6 @@ const resizeEvent = 'resize';
|
|
273
255
|
*/
|
274
256
|
const focusoutEvent = 'focusout';
|
275
257
|
|
276
|
-
// @ts-ignore
|
277
|
-
const { userAgentData: uaDATA } = navigator;
|
278
|
-
|
279
|
-
/**
|
280
|
-
* A global namespace for `userAgentData` object.
|
281
|
-
*/
|
282
|
-
const userAgentData = uaDATA;
|
283
|
-
|
284
|
-
const { userAgent: userAgentString } = navigator;
|
285
|
-
|
286
|
-
/**
|
287
|
-
* A global namespace for `navigator.userAgent` string.
|
288
|
-
*/
|
289
|
-
const userAgent = userAgentString;
|
290
|
-
|
291
|
-
const mobileBrands = /iPhone|iPad|iPod|Android/i;
|
292
|
-
let isMobileCheck = false;
|
293
|
-
|
294
|
-
if (userAgentData) {
|
295
|
-
isMobileCheck = userAgentData.brands
|
296
|
-
.some((/** @type {Record<String, any>} */x) => mobileBrands.test(x.brand));
|
297
|
-
} else {
|
298
|
-
isMobileCheck = mobileBrands.test(userAgent);
|
299
|
-
}
|
300
|
-
|
301
|
-
/**
|
302
|
-
* A global `boolean` for mobile detection.
|
303
|
-
* @type {boolean}
|
304
|
-
*/
|
305
|
-
const isMobile = isMobileCheck;
|
306
|
-
|
307
258
|
/**
|
308
259
|
* Returns the `document` or the `#document` element.
|
309
260
|
* @see https://github.com/floating-ui/floating-ui
|
@@ -524,60 +475,6 @@ function getElementsByClassName(selector, parent) {
|
|
524
475
|
return lookUp.getElementsByClassName(selector);
|
525
476
|
}
|
526
477
|
|
527
|
-
/**
|
528
|
-
* Shortcut for `Object.assign()` static method.
|
529
|
-
* @param {Record<string, any>} obj a target object
|
530
|
-
* @param {Record<string, any>} source a source object
|
531
|
-
*/
|
532
|
-
const ObjectAssign = (obj, source) => Object.assign(obj, source);
|
533
|
-
|
534
|
-
/**
|
535
|
-
* This is a shortie for `document.createElement` method
|
536
|
-
* which allows you to create a new `HTMLElement` for a given `tagName`
|
537
|
-
* or based on an object with specific non-readonly attributes:
|
538
|
-
* `id`, `className`, `textContent`, `style`, etc.
|
539
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
|
540
|
-
*
|
541
|
-
* @param {Record<string, string> | string} param `tagName` or object
|
542
|
-
* @return {HTMLElement | Element} a new `HTMLElement` or `Element`
|
543
|
-
*/
|
544
|
-
function createElement(param) {
|
545
|
-
if (typeof param === 'string') {
|
546
|
-
return getDocument().createElement(param);
|
547
|
-
}
|
548
|
-
|
549
|
-
const { tagName } = param;
|
550
|
-
const attr = { ...param };
|
551
|
-
const newElement = createElement(tagName);
|
552
|
-
delete attr.tagName;
|
553
|
-
ObjectAssign(newElement, attr);
|
554
|
-
return newElement;
|
555
|
-
}
|
556
|
-
|
557
|
-
/**
|
558
|
-
* This is a shortie for `document.createElementNS` method
|
559
|
-
* which allows you to create a new `HTMLElement` for a given `tagName`
|
560
|
-
* or based on an object with specific non-readonly attributes:
|
561
|
-
* `id`, `className`, `textContent`, `style`, etc.
|
562
|
-
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
|
563
|
-
*
|
564
|
-
* @param {string} namespace `namespaceURI` to associate with the new `HTMLElement`
|
565
|
-
* @param {Record<string, string> | string} param `tagName` or object
|
566
|
-
* @return {HTMLElement | Element} a new `HTMLElement` or `Element`
|
567
|
-
*/
|
568
|
-
function createElementNS(namespace, param) {
|
569
|
-
if (typeof param === 'string') {
|
570
|
-
return getDocument().createElementNS(namespace, param);
|
571
|
-
}
|
572
|
-
|
573
|
-
const { tagName } = param;
|
574
|
-
const attr = { ...param };
|
575
|
-
const newElement = createElementNS(namespace, tagName);
|
576
|
-
delete attr.tagName;
|
577
|
-
ObjectAssign(newElement, attr);
|
578
|
-
return newElement;
|
579
|
-
}
|
580
|
-
|
581
478
|
/**
|
582
479
|
* Shortcut for the `Element.dispatchEvent(Event)` method.
|
583
480
|
*
|
@@ -586,6 +483,13 @@ function createElementNS(namespace, param) {
|
|
586
483
|
*/
|
587
484
|
const dispatchEvent = (element, event) => element.dispatchEvent(event);
|
588
485
|
|
486
|
+
/**
|
487
|
+
* Shortcut for `Object.assign()` static method.
|
488
|
+
* @param {Record<string, any>} obj a target object
|
489
|
+
* @param {Record<string, any>} source a source object
|
490
|
+
*/
|
491
|
+
const ObjectAssign = (obj, source) => Object.assign(obj, source);
|
492
|
+
|
589
493
|
/** @type {Map<string, Map<HTMLElement | Element, Record<string, any>>>} */
|
590
494
|
const componentData = new Map();
|
591
495
|
/**
|
@@ -661,20 +565,13 @@ const Data = {
|
|
661
565
|
*/
|
662
566
|
const getInstance = (target, component) => Data.get(target, component);
|
663
567
|
|
664
|
-
/**
|
665
|
-
* Shortcut for `Object.keys()` static method.
|
666
|
-
* @param {Record<string, any>} obj a target object
|
667
|
-
* @returns {string[]}
|
668
|
-
*/
|
669
|
-
const ObjectKeys = (obj) => Object.keys(obj);
|
670
|
-
|
671
568
|
/**
|
672
569
|
* Shortcut for multiple uses of `HTMLElement.style.propertyName` method.
|
673
570
|
* @param {HTMLElement | Element} element target element
|
674
571
|
* @param {Partial<CSSStyleDeclaration>} styles attribute value
|
675
572
|
*/
|
676
573
|
// @ts-ignore
|
677
|
-
const setElementStyle = (element, styles) => ObjectAssign(element.style, styles);
|
574
|
+
const setElementStyle = (element, styles) => { ObjectAssign(element.style, styles); };
|
678
575
|
|
679
576
|
/**
|
680
577
|
* Shortcut for `HTMLElement.getAttribute()` method.
|
@@ -717,6 +614,13 @@ function normalizeValue(value) {
|
|
717
614
|
return value;
|
718
615
|
}
|
719
616
|
|
617
|
+
/**
|
618
|
+
* Shortcut for `Object.keys()` static method.
|
619
|
+
* @param {Record<string, any>} obj a target object
|
620
|
+
* @returns {string[]}
|
621
|
+
*/
|
622
|
+
const ObjectKeys = (obj) => Object.keys(obj);
|
623
|
+
|
720
624
|
/**
|
721
625
|
* Shortcut for `String.toLowerCase()`.
|
722
626
|
*
|
@@ -837,32 +741,10 @@ const setAttribute = (element, attribute, value) => element.setAttribute(attribu
|
|
837
741
|
*/
|
838
742
|
const removeAttribute = (element, attribute) => element.removeAttribute(attribute);
|
839
743
|
|
840
|
-
/** @type {Record<string, string>} */
|
841
|
-
const colorPickerLabels = {
|
842
|
-
pickerLabel: 'Colour Picker',
|
843
|
-
appearanceLabel: 'Colour Appearance',
|
844
|
-
valueLabel: 'Colour Value',
|
845
|
-
toggleLabel: 'Select Colour',
|
846
|
-
presetsLabel: 'Colour Presets',
|
847
|
-
defaultsLabel: 'Colour Defaults',
|
848
|
-
formatLabel: 'Format',
|
849
|
-
alphaLabel: 'Alpha',
|
850
|
-
hexLabel: 'Hexadecimal',
|
851
|
-
hueLabel: 'Hue',
|
852
|
-
whitenessLabel: 'Whiteness',
|
853
|
-
blacknessLabel: 'Blackness',
|
854
|
-
saturationLabel: 'Saturation',
|
855
|
-
lightnessLabel: 'Lightness',
|
856
|
-
redLabel: 'Red',
|
857
|
-
greenLabel: 'Green',
|
858
|
-
blueLabel: 'Blue',
|
859
|
-
};
|
860
|
-
|
861
744
|
/**
|
862
|
-
* A
|
863
|
-
* @type {string[]}
|
745
|
+
* A global namespace for `document.head`.
|
864
746
|
*/
|
865
|
-
const
|
747
|
+
const { head: documentHead } = document;
|
866
748
|
|
867
749
|
/**
|
868
750
|
* A list of explicit default non-color values.
|
@@ -870,297 +752,99 @@ const colorNames = ['white', 'black', 'grey', 'red', 'orange', 'brown', 'gold',
|
|
870
752
|
const nonColors = ['transparent', 'currentColor', 'inherit', 'revert', 'initial'];
|
871
753
|
|
872
754
|
/**
|
873
|
-
*
|
874
|
-
*
|
875
|
-
* @
|
876
|
-
* @returns {string} uppercase output string
|
755
|
+
* Round colour components, for all formats except HEX.
|
756
|
+
* @param {number} v one of the colour components
|
757
|
+
* @returns {number} the rounded number
|
877
758
|
*/
|
878
|
-
|
759
|
+
function roundPart(v) {
|
760
|
+
const floor = Math.floor(v);
|
761
|
+
return v - floor < 0.5 ? floor : Math.round(v);
|
762
|
+
}
|
879
763
|
|
880
|
-
|
764
|
+
// Color supported formats
|
765
|
+
const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsv', 'hwb'];
|
881
766
|
|
882
|
-
|
883
|
-
|
884
|
-
*
|
885
|
-
* @param {CP.ColorPicker} self the `ColorPicker` instance
|
886
|
-
* @returns {HTMLElement | Element} a new `<div>` element with color component `<input>`
|
887
|
-
*/
|
888
|
-
function getColorForm(self) {
|
889
|
-
const { format, id, componentLabels } = self;
|
890
|
-
const colorForm = createElement({
|
891
|
-
tagName: 'div',
|
892
|
-
className: `color-form ${format}`,
|
893
|
-
});
|
767
|
+
// Hue angles
|
768
|
+
const ANGLES = 'deg|rad|grad|turn';
|
894
769
|
|
895
|
-
|
896
|
-
|
897
|
-
else if (format === 'hsl') components = ['hue', 'saturation', 'lightness', 'alpha'];
|
898
|
-
else if (format === 'hwb') components = ['hue', 'whiteness', 'blackness', 'alpha'];
|
770
|
+
// <http://www.w3.org/TR/css3-values/#integers>
|
771
|
+
const CSS_INTEGER = '[-\\+]?\\d+%?';
|
899
772
|
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
const formatLabel = componentLabels[`${c}Label`];
|
904
|
-
const cInputLabel = createElement({ tagName: 'label' });
|
905
|
-
setAttribute(cInputLabel, 'for', cID);
|
906
|
-
cInputLabel.append(
|
907
|
-
createElement({ tagName: 'span', ariaHidden: 'true', innerText: `${C}:` }),
|
908
|
-
createElement({ tagName: 'span', className: vHidden, innerText: formatLabel }),
|
909
|
-
);
|
910
|
-
const cInput = createElement({
|
911
|
-
tagName: 'input',
|
912
|
-
id: cID,
|
913
|
-
// name: cID, - prevent saving the value to a form
|
914
|
-
type: format === 'hex' ? 'text' : 'number',
|
915
|
-
value: c === 'alpha' ? '100' : '0',
|
916
|
-
className: `color-input ${c}`,
|
917
|
-
});
|
918
|
-
setAttribute(cInput, 'autocomplete', 'off');
|
919
|
-
setAttribute(cInput, 'spellcheck', 'false');
|
773
|
+
// Include CSS3 Module
|
774
|
+
// <http://www.w3.org/TR/css3-values/#number-value>
|
775
|
+
const CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?';
|
920
776
|
|
921
|
-
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
777
|
+
// Include CSS4 Module Hue degrees unit
|
778
|
+
// <https://www.w3.org/TR/css3-values/#angle-value>
|
779
|
+
const CSS_ANGLE = `[-\\+]?\\d*\\.?\\d+(?:${ANGLES})?`;
|
780
|
+
|
781
|
+
// Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
|
782
|
+
const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;
|
783
|
+
|
784
|
+
// Add angles to the mix
|
785
|
+
const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
|
786
|
+
|
787
|
+
// Start & end
|
788
|
+
const START_MATCH = '(?:[\\s|\\(\\s|\\s\\(\\s]+)?';
|
789
|
+
const END_MATCH = '(?:[\\s|\\)\\s]+)?';
|
790
|
+
// Components separation
|
791
|
+
const SEP = '(?:[,|\\s]+)';
|
792
|
+
const SEP2 = '(?:[,|\\/\\s]*)?';
|
793
|
+
|
794
|
+
// Actual matching.
|
795
|
+
// Parentheses and commas are optional, but not required.
|
796
|
+
// Whitespace can take the place of commas or opening paren
|
797
|
+
const PERMISSIVE_MATCH = `${START_MATCH}(${CSS_UNIT2})${SEP}(${CSS_UNIT})${SEP}(${CSS_UNIT})${SEP2}(${CSS_UNIT})?${END_MATCH}`;
|
798
|
+
|
799
|
+
const matchers = {
|
800
|
+
CSS_UNIT: new RegExp(CSS_UNIT2),
|
801
|
+
hwb: new RegExp(`hwb${PERMISSIVE_MATCH}`),
|
802
|
+
rgb: new RegExp(`rgb(?:a)?${PERMISSIVE_MATCH}`),
|
803
|
+
hsl: new RegExp(`hsl(?:a)?${PERMISSIVE_MATCH}`),
|
804
|
+
hsv: new RegExp(`hsv(?:a)?${PERMISSIVE_MATCH}`),
|
805
|
+
hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
806
|
+
hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
807
|
+
hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
808
|
+
hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
809
|
+
};
|
940
810
|
|
941
811
|
/**
|
942
|
-
*
|
943
|
-
*
|
812
|
+
* Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
|
813
|
+
* <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
|
814
|
+
* @param {string} n testing number
|
815
|
+
* @returns {boolean} the query result
|
944
816
|
*/
|
945
|
-
|
817
|
+
function isOnePointZero(n) {
|
818
|
+
return `${n}`.includes('.') && parseFloat(n) === 1;
|
819
|
+
}
|
946
820
|
|
947
821
|
/**
|
948
|
-
*
|
949
|
-
* @
|
822
|
+
* Check to see if string passed in is a percentage
|
823
|
+
* @param {string} n testing number
|
824
|
+
* @returns {boolean} the query result
|
950
825
|
*/
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
* A global namespace for aria-valuemax.
|
955
|
-
* @type {string}
|
956
|
-
*/
|
957
|
-
const ariaValueMax = 'aria-valuemax';
|
958
|
-
|
959
|
-
const tabIndex = 'tabindex';
|
960
|
-
|
961
|
-
/**
|
962
|
-
* Returns all color controls for `ColorPicker`.
|
963
|
-
*
|
964
|
-
* @param {CP.ColorPicker} self the `ColorPicker` instance
|
965
|
-
* @returns {HTMLElement | Element} color controls
|
966
|
-
*/
|
967
|
-
function getColorControls(self) {
|
968
|
-
const { format, componentLabels } = self;
|
969
|
-
const {
|
970
|
-
hueLabel, alphaLabel, lightnessLabel, saturationLabel,
|
971
|
-
whitenessLabel, blacknessLabel,
|
972
|
-
} = componentLabels;
|
973
|
-
|
974
|
-
const max1 = format === 'hsl' ? 360 : 100;
|
975
|
-
const max2 = format === 'hsl' ? 100 : 360;
|
976
|
-
const max3 = 100;
|
977
|
-
|
978
|
-
let ctrl1Label = format === 'hsl'
|
979
|
-
? `${hueLabel} & ${lightnessLabel}`
|
980
|
-
: `${lightnessLabel} & ${saturationLabel}`;
|
981
|
-
|
982
|
-
ctrl1Label = format === 'hwb'
|
983
|
-
? `${whitenessLabel} & ${blacknessLabel}`
|
984
|
-
: ctrl1Label;
|
985
|
-
|
986
|
-
const ctrl2Label = format === 'hsl'
|
987
|
-
? `${saturationLabel}`
|
988
|
-
: `${hueLabel}`;
|
989
|
-
|
990
|
-
const colorControls = createElement({
|
991
|
-
tagName: 'div',
|
992
|
-
className: `color-controls ${format}`,
|
993
|
-
});
|
994
|
-
|
995
|
-
const colorPointer = 'color-pointer';
|
996
|
-
const colorSlider = 'color-slider';
|
997
|
-
|
998
|
-
const controls = [
|
999
|
-
{
|
1000
|
-
i: 1,
|
1001
|
-
c: colorPointer,
|
1002
|
-
l: ctrl1Label,
|
1003
|
-
min: 0,
|
1004
|
-
max: max1,
|
1005
|
-
},
|
1006
|
-
{
|
1007
|
-
i: 2,
|
1008
|
-
c: colorSlider,
|
1009
|
-
l: ctrl2Label,
|
1010
|
-
min: 0,
|
1011
|
-
max: max2,
|
1012
|
-
},
|
1013
|
-
{
|
1014
|
-
i: 3,
|
1015
|
-
c: colorSlider,
|
1016
|
-
l: alphaLabel,
|
1017
|
-
min: 0,
|
1018
|
-
max: max3,
|
1019
|
-
},
|
1020
|
-
];
|
1021
|
-
|
1022
|
-
controls.forEach((template) => {
|
1023
|
-
const {
|
1024
|
-
i, c, l, min, max,
|
1025
|
-
} = template;
|
1026
|
-
const control = createElement({
|
1027
|
-
tagName: 'div',
|
1028
|
-
className: 'color-control',
|
1029
|
-
});
|
1030
|
-
setAttribute(control, 'role', 'presentation');
|
1031
|
-
|
1032
|
-
control.append(
|
1033
|
-
createElement({
|
1034
|
-
tagName: 'div',
|
1035
|
-
className: `visual-control visual-control${i}`,
|
1036
|
-
}),
|
1037
|
-
);
|
1038
|
-
|
1039
|
-
const knob = createElement({
|
1040
|
-
tagName: 'div',
|
1041
|
-
className: `${c} knob`,
|
1042
|
-
ariaLive: 'polite',
|
1043
|
-
});
|
1044
|
-
|
1045
|
-
setAttribute(knob, ariaLabel, l);
|
1046
|
-
setAttribute(knob, 'role', 'slider');
|
1047
|
-
setAttribute(knob, tabIndex, '0');
|
1048
|
-
setAttribute(knob, ariaValueMin, `${min}`);
|
1049
|
-
setAttribute(knob, ariaValueMax, `${max}`);
|
1050
|
-
control.append(knob);
|
1051
|
-
colorControls.append(control);
|
1052
|
-
});
|
1053
|
-
|
1054
|
-
return colorControls;
|
1055
|
-
}
|
1056
|
-
|
1057
|
-
/**
|
1058
|
-
* Helps setting CSS variables to the color-menu.
|
1059
|
-
* @param {HTMLElement} element
|
1060
|
-
* @param {Record<string,any>} props
|
1061
|
-
*/
|
1062
|
-
function setCSSProperties(element, props) {
|
1063
|
-
ObjectKeys(props).forEach((prop) => {
|
1064
|
-
element.style.setProperty(prop, props[prop]);
|
1065
|
-
});
|
1066
|
-
}
|
1067
|
-
|
1068
|
-
/**
|
1069
|
-
* Returns the `document.head` or the `<head>` element.
|
1070
|
-
*
|
1071
|
-
* @param {(Node | HTMLElement | Element | globalThis)=} node
|
1072
|
-
* @returns {HTMLElement | HTMLHeadElement}
|
1073
|
-
*/
|
1074
|
-
function getDocumentHead(node) {
|
1075
|
-
return getDocument(node).head;
|
1076
|
-
}
|
1077
|
-
|
1078
|
-
/**
|
1079
|
-
* Round colour components, for all formats except HEX.
|
1080
|
-
* @param {number} v one of the colour components
|
1081
|
-
* @returns {number} the rounded number
|
1082
|
-
*/
|
1083
|
-
function roundPart(v) {
|
1084
|
-
const floor = Math.floor(v);
|
1085
|
-
return v - floor < 0.5 ? floor : Math.round(v);
|
1086
|
-
}
|
1087
|
-
|
1088
|
-
// Color supported formats
|
1089
|
-
const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsb', 'hwb'];
|
1090
|
-
|
1091
|
-
// Hue angles
|
1092
|
-
const ANGLES = 'deg|rad|grad|turn';
|
1093
|
-
|
1094
|
-
// <http://www.w3.org/TR/css3-values/#integers>
|
1095
|
-
const CSS_INTEGER = '[-\\+]?\\d+%?';
|
1096
|
-
|
1097
|
-
// Include CSS3 Module
|
1098
|
-
// <http://www.w3.org/TR/css3-values/#number-value>
|
1099
|
-
const CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?';
|
1100
|
-
|
1101
|
-
// Include CSS4 Module Hue degrees unit
|
1102
|
-
// <https://www.w3.org/TR/css3-values/#angle-value>
|
1103
|
-
const CSS_ANGLE = `[-\\+]?\\d*\\.?\\d+(?:${ANGLES})?`;
|
1104
|
-
|
1105
|
-
// Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
|
1106
|
-
const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;
|
1107
|
-
|
1108
|
-
// Add angles to the mix
|
1109
|
-
const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
|
1110
|
-
|
1111
|
-
// Actual matching.
|
1112
|
-
// Parentheses and commas are optional, but not required.
|
1113
|
-
// Whitespace can take the place of commas or opening paren
|
1114
|
-
const PERMISSIVE_MATCH = `[\\s|\\(]+(${CSS_UNIT2})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s|\\/\\s]*(${CSS_UNIT})?\\s*\\)?`;
|
1115
|
-
|
1116
|
-
const matchers = {
|
1117
|
-
CSS_UNIT: new RegExp(CSS_UNIT2),
|
1118
|
-
hwb: new RegExp(`hwb${PERMISSIVE_MATCH}`),
|
1119
|
-
rgb: new RegExp(`rgb(?:a)?${PERMISSIVE_MATCH}`),
|
1120
|
-
hsl: new RegExp(`hsl(?:a)?${PERMISSIVE_MATCH}`),
|
1121
|
-
hsv: new RegExp(`hsv(?:a)?${PERMISSIVE_MATCH}`),
|
1122
|
-
hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
1123
|
-
hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
1124
|
-
hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
1125
|
-
hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
1126
|
-
};
|
1127
|
-
|
1128
|
-
/**
|
1129
|
-
* Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
|
1130
|
-
* <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
|
1131
|
-
* @param {string} n testing number
|
1132
|
-
* @returns {boolean} the query result
|
1133
|
-
*/
|
1134
|
-
function isOnePointZero(n) {
|
1135
|
-
return `${n}`.includes('.') && parseFloat(n) === 1;
|
1136
|
-
}
|
1137
|
-
|
1138
|
-
/**
|
1139
|
-
* Check to see if string passed in is a percentage
|
1140
|
-
* @param {string} n testing number
|
1141
|
-
* @returns {boolean} the query result
|
1142
|
-
*/
|
1143
|
-
function isPercentage(n) {
|
1144
|
-
return `${n}`.includes('%');
|
1145
|
-
}
|
1146
|
-
|
1147
|
-
/**
|
1148
|
-
* Check to see if string passed in is an angle
|
1149
|
-
* @param {string} n testing string
|
1150
|
-
* @returns {boolean} the query result
|
1151
|
-
*/
|
1152
|
-
function isAngle(n) {
|
1153
|
-
return ANGLES.split('|').some((a) => `${n}`.includes(a));
|
1154
|
-
}
|
826
|
+
function isPercentage(n) {
|
827
|
+
return `${n}`.includes('%');
|
828
|
+
}
|
1155
829
|
|
1156
830
|
/**
|
1157
831
|
* Check to see if string passed is a web safe colour.
|
832
|
+
* @see https://stackoverflow.com/a/16994164
|
1158
833
|
* @param {string} color a colour name, EG: *red*
|
1159
834
|
* @returns {boolean} the query result
|
1160
835
|
*/
|
1161
836
|
function isColorName(color) {
|
1162
|
-
|
1163
|
-
|
837
|
+
if (nonColors.includes(color)
|
838
|
+
|| ['#', ...COLOR_FORMAT].some((f) => color.includes(f))) return false;
|
839
|
+
|
840
|
+
if (['black', 'white'].includes(color)) return true;
|
841
|
+
|
842
|
+
return ['rgb(255, 255, 255)', 'rgb(0, 0, 0)'].every((c) => {
|
843
|
+
setElementStyle(documentHead, { color });
|
844
|
+
const computedColor = getElementStyle(documentHead, 'color');
|
845
|
+
setElementStyle(documentHead, { color: '' });
|
846
|
+
return computedColor !== c;
|
847
|
+
});
|
1164
848
|
}
|
1165
849
|
|
1166
850
|
/**
|
@@ -1181,15 +865,20 @@ function isValidCSSUnit(color) {
|
|
1181
865
|
*/
|
1182
866
|
function bound01(N, max) {
|
1183
867
|
let n = N;
|
1184
|
-
if (isOnePointZero(n)) n = '100%';
|
1185
868
|
|
1186
|
-
|
869
|
+
if (typeof N === 'number'
|
870
|
+
&& Math.min(N, 0) === 0 // round values to 6 decimals Math.round(N * (10 ** 6)) / 10 ** 6
|
871
|
+
&& Math.max(N, 1) === 1) return N;
|
872
|
+
|
873
|
+
if (isOnePointZero(N)) n = '100%';
|
1187
874
|
|
1188
|
-
|
1189
|
-
|
875
|
+
const processPercent = isPercentage(n);
|
876
|
+
n = max === 360
|
877
|
+
? parseFloat(n)
|
878
|
+
: Math.min(max, Math.max(0, parseFloat(n)));
|
1190
879
|
|
1191
880
|
// Automatically convert percentage into number
|
1192
|
-
if (
|
881
|
+
if (processPercent) n = (n * max) / 100;
|
1193
882
|
|
1194
883
|
// Handle floating point rounding errors
|
1195
884
|
if (Math.abs(n - max) < 0.000001) {
|
@@ -1200,11 +889,11 @@ function bound01(N, max) {
|
|
1200
889
|
// If n is a hue given in degrees,
|
1201
890
|
// wrap around out-of-range values into [0, 360] range
|
1202
891
|
// then convert into [0, 1].
|
1203
|
-
n = (n < 0 ? (n % max) + max : n % max) /
|
892
|
+
n = (n < 0 ? (n % max) + max : n % max) / max;
|
1204
893
|
} else {
|
1205
894
|
// If n not a hue given in degrees
|
1206
895
|
// Convert into [0, 1] range if it isn't already.
|
1207
|
-
n = (n % max) /
|
896
|
+
n = (n % max) / max;
|
1208
897
|
}
|
1209
898
|
return n;
|
1210
899
|
}
|
@@ -1239,7 +928,6 @@ function clamp01(v) {
|
|
1239
928
|
* @returns {string}
|
1240
929
|
*/
|
1241
930
|
function getRGBFromName(name) {
|
1242
|
-
const documentHead = getDocumentHead();
|
1243
931
|
setElementStyle(documentHead, { color: name });
|
1244
932
|
const colorName = getElementStyle(documentHead, 'color');
|
1245
933
|
setElementStyle(documentHead, { color: '' });
|
@@ -1285,15 +973,12 @@ function pad2(c) {
|
|
1285
973
|
/**
|
1286
974
|
* Converts an RGB colour value to HSL.
|
1287
975
|
*
|
1288
|
-
* @param {number}
|
1289
|
-
* @param {number}
|
1290
|
-
* @param {number}
|
976
|
+
* @param {number} r Red component [0, 1]
|
977
|
+
* @param {number} g Green component [0, 1]
|
978
|
+
* @param {number} b Blue component [0, 1]
|
1291
979
|
* @returns {CP.HSL} {h,s,l} object with [0, 1] ranged values
|
1292
980
|
*/
|
1293
|
-
function rgbToHsl(
|
1294
|
-
const r = R / 255;
|
1295
|
-
const g = G / 255;
|
1296
|
-
const b = B / 255;
|
981
|
+
function rgbToHsl(r, g, b) {
|
1297
982
|
const max = Math.max(r, g, b);
|
1298
983
|
const min = Math.min(r, g, b);
|
1299
984
|
let h = 0;
|
@@ -1305,17 +990,10 @@ function rgbToHsl(R, G, B) {
|
|
1305
990
|
} else {
|
1306
991
|
const d = max - min;
|
1307
992
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
1308
|
-
|
1309
|
-
|
1310
|
-
|
1311
|
-
|
1312
|
-
case g:
|
1313
|
-
h = (b - r) / d + 2;
|
1314
|
-
break;
|
1315
|
-
case b:
|
1316
|
-
h = (r - g) / d + 4;
|
1317
|
-
break;
|
1318
|
-
}
|
993
|
+
if (max === r) h = (g - b) / d + (g < b ? 6 : 0);
|
994
|
+
if (max === g) h = (b - r) / d + 2;
|
995
|
+
if (max === b) h = (r - g) / d + 4;
|
996
|
+
|
1319
997
|
h /= 6;
|
1320
998
|
}
|
1321
999
|
return { h, s, l };
|
@@ -1338,21 +1016,46 @@ function hueToRgb(p, q, t) {
|
|
1338
1016
|
return p;
|
1339
1017
|
}
|
1340
1018
|
|
1019
|
+
/**
|
1020
|
+
* Converts an HSL colour value to RGB.
|
1021
|
+
*
|
1022
|
+
* @param {number} h Hue Angle [0, 1]
|
1023
|
+
* @param {number} s Saturation [0, 1]
|
1024
|
+
* @param {number} l Lightness Angle [0, 1]
|
1025
|
+
* @returns {CP.RGB} {r,g,b} object with [0, 1] ranged values
|
1026
|
+
*/
|
1027
|
+
function hslToRgb(h, s, l) {
|
1028
|
+
let r = 0;
|
1029
|
+
let g = 0;
|
1030
|
+
let b = 0;
|
1031
|
+
|
1032
|
+
if (s === 0) {
|
1033
|
+
// achromatic
|
1034
|
+
g = l;
|
1035
|
+
b = l;
|
1036
|
+
r = l;
|
1037
|
+
} else {
|
1038
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
1039
|
+
const p = 2 * l - q;
|
1040
|
+
r = hueToRgb(p, q, h + 1 / 3);
|
1041
|
+
g = hueToRgb(p, q, h);
|
1042
|
+
b = hueToRgb(p, q, h - 1 / 3);
|
1043
|
+
}
|
1044
|
+
|
1045
|
+
return { r, g, b };
|
1046
|
+
}
|
1047
|
+
|
1341
1048
|
/**
|
1342
1049
|
* Returns an HWB colour object from an RGB colour object.
|
1343
1050
|
* @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
|
1344
1051
|
* @link http://alvyray.com/Papers/CG/hwb2rgb.htm
|
1345
1052
|
*
|
1346
|
-
* @param {number}
|
1347
|
-
* @param {number}
|
1348
|
-
* @param {number}
|
1053
|
+
* @param {number} r Red component [0, 1]
|
1054
|
+
* @param {number} g Green [0, 1]
|
1055
|
+
* @param {number} b Blue [0, 1]
|
1349
1056
|
* @return {CP.HWB} {h,w,b} object with [0, 1] ranged values
|
1350
1057
|
*/
|
1351
|
-
function rgbToHwb(
|
1352
|
-
const r = R / 255;
|
1353
|
-
const g = G / 255;
|
1354
|
-
const b = B / 255;
|
1355
|
-
|
1058
|
+
function rgbToHwb(r, g, b) {
|
1356
1059
|
let f = 0;
|
1357
1060
|
let i = 0;
|
1358
1061
|
const whiteness = Math.min(r, g, b);
|
@@ -1382,50 +1085,18 @@ function rgbToHwb(R, G, B) {
|
|
1382
1085
|
* @param {number} H Hue Angle [0, 1]
|
1383
1086
|
* @param {number} W Whiteness [0, 1]
|
1384
1087
|
* @param {number} B Blackness [0, 1]
|
1385
|
-
* @return {CP.RGB} {r,g,b} object with [0,
|
1088
|
+
* @return {CP.RGB} {r,g,b} object with [0, 1] ranged values
|
1386
1089
|
*
|
1387
1090
|
* @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
|
1388
1091
|
* @link http://alvyray.com/Papers/CG/hwb2rgb.htm
|
1389
1092
|
*/
|
1390
1093
|
function hwbToRgb(H, W, B) {
|
1391
1094
|
if (W + B >= 1) {
|
1392
|
-
const gray =
|
1095
|
+
const gray = W / (W + B);
|
1393
1096
|
return { r: gray, g: gray, b: gray };
|
1394
1097
|
}
|
1395
1098
|
let { r, g, b } = hslToRgb(H, 1, 0.5);
|
1396
|
-
[r, g, b] = [r, g, b]
|
1397
|
-
.map((v) => (v / 255) * (1 - W - B) + W)
|
1398
|
-
.map((v) => v * 255);
|
1399
|
-
|
1400
|
-
return { r, g, b };
|
1401
|
-
}
|
1402
|
-
|
1403
|
-
/**
|
1404
|
-
* Converts an HSL colour value to RGB.
|
1405
|
-
*
|
1406
|
-
* @param {number} h Hue Angle [0, 1]
|
1407
|
-
* @param {number} s Saturation [0, 1]
|
1408
|
-
* @param {number} l Lightness Angle [0, 1]
|
1409
|
-
* @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
|
1410
|
-
*/
|
1411
|
-
function hslToRgb(h, s, l) {
|
1412
|
-
let r = 0;
|
1413
|
-
let g = 0;
|
1414
|
-
let b = 0;
|
1415
|
-
|
1416
|
-
if (s === 0) {
|
1417
|
-
// achromatic
|
1418
|
-
g = l;
|
1419
|
-
b = l;
|
1420
|
-
r = l;
|
1421
|
-
} else {
|
1422
|
-
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
1423
|
-
const p = 2 * l - q;
|
1424
|
-
r = hueToRgb(p, q, h + 1 / 3);
|
1425
|
-
g = hueToRgb(p, q, h);
|
1426
|
-
b = hueToRgb(p, q, h - 1 / 3);
|
1427
|
-
}
|
1428
|
-
[r, g, b] = [r, g, b].map((x) => x * 255);
|
1099
|
+
[r, g, b] = [r, g, b].map((v) => v * (1 - W - B) + W);
|
1429
1100
|
|
1430
1101
|
return { r, g, b };
|
1431
1102
|
}
|
@@ -1433,15 +1104,12 @@ function hslToRgb(h, s, l) {
|
|
1433
1104
|
/**
|
1434
1105
|
* Converts an RGB colour value to HSV.
|
1435
1106
|
*
|
1436
|
-
* @param {number}
|
1437
|
-
* @param {number}
|
1438
|
-
* @param {number}
|
1107
|
+
* @param {number} r Red component [0, 1]
|
1108
|
+
* @param {number} g Green [0, 1]
|
1109
|
+
* @param {number} b Blue [0, 1]
|
1439
1110
|
* @returns {CP.HSV} {h,s,v} object with [0, 1] ranged values
|
1440
1111
|
*/
|
1441
|
-
function rgbToHsv(
|
1442
|
-
const r = R / 255;
|
1443
|
-
const g = G / 255;
|
1444
|
-
const b = B / 255;
|
1112
|
+
function rgbToHsv(r, g, b) {
|
1445
1113
|
const max = Math.max(r, g, b);
|
1446
1114
|
const min = Math.min(r, g, b);
|
1447
1115
|
let h = 0;
|
@@ -1451,17 +1119,10 @@ function rgbToHsv(R, G, B) {
|
|
1451
1119
|
if (max === min) {
|
1452
1120
|
h = 0; // achromatic
|
1453
1121
|
} else {
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
case g:
|
1459
|
-
h = (b - r) / d + 2;
|
1460
|
-
break;
|
1461
|
-
case b:
|
1462
|
-
h = (r - g) / d + 4;
|
1463
|
-
break;
|
1464
|
-
}
|
1122
|
+
if (r === max) h = (g - b) / d + (g < b ? 6 : 0);
|
1123
|
+
if (g === max) h = (b - r) / d + 2;
|
1124
|
+
if (b === max) h = (r - g) / d + 4;
|
1125
|
+
|
1465
1126
|
h /= 6;
|
1466
1127
|
}
|
1467
1128
|
return { h, s, v };
|
@@ -1488,7 +1149,7 @@ function hsvToRgb(H, S, V) {
|
|
1488
1149
|
const r = [v, q, p, p, t, v][mod];
|
1489
1150
|
const g = [t, v, v, q, p, p][mod];
|
1490
1151
|
const b = [p, p, t, v, v, q][mod];
|
1491
|
-
return { r
|
1152
|
+
return { r, g, b };
|
1492
1153
|
}
|
1493
1154
|
|
1494
1155
|
/**
|
@@ -1512,7 +1173,7 @@ function rgbToHex(r, g, b, allow3Char) {
|
|
1512
1173
|
// Return a 3 character hex if possible
|
1513
1174
|
if (allow3Char && hex[0].charAt(0) === hex[0].charAt(1)
|
1514
1175
|
&& hex[1].charAt(0) === hex[1].charAt(1)
|
1515
|
-
|
1176
|
+
&& hex[2].charAt(0) === hex[2].charAt(1)) {
|
1516
1177
|
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
|
1517
1178
|
}
|
1518
1179
|
|
@@ -1540,51 +1201,34 @@ function rgbaToHex(r, g, b, a, allow4Char) {
|
|
1540
1201
|
// Return a 4 character hex if possible
|
1541
1202
|
if (allow4Char && hex[0].charAt(0) === hex[0].charAt(1)
|
1542
1203
|
&& hex[1].charAt(0) === hex[1].charAt(1)
|
1543
|
-
|
1544
|
-
|
1204
|
+
&& hex[2].charAt(0) === hex[2].charAt(1)
|
1205
|
+
&& hex[3].charAt(0) === hex[3].charAt(1)) {
|
1545
1206
|
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
|
1546
1207
|
}
|
1547
1208
|
return hex.join('');
|
1548
1209
|
}
|
1549
1210
|
|
1550
|
-
/**
|
1551
|
-
* Returns a colour object corresponding to a given number.
|
1552
|
-
* @param {number} color input number
|
1553
|
-
* @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
|
1554
|
-
*/
|
1555
|
-
function numberInputToObject(color) {
|
1556
|
-
/* eslint-disable no-bitwise */
|
1557
|
-
return {
|
1558
|
-
r: color >> 16,
|
1559
|
-
g: (color & 0xff00) >> 8,
|
1560
|
-
b: color & 0xff,
|
1561
|
-
};
|
1562
|
-
/* eslint-enable no-bitwise */
|
1563
|
-
}
|
1564
|
-
|
1565
1211
|
/**
|
1566
1212
|
* Permissive string parsing. Take in a number of formats, and output an object
|
1567
1213
|
* based on detected format. Returns {r,g,b} or {h,s,l} or {h,s,v}
|
1568
1214
|
* @param {string} input colour value in any format
|
1569
|
-
* @returns {Record<string, (number | string)> | false} an object matching the RegExp
|
1215
|
+
* @returns {Record<string, (number | string | boolean)> | false} an object matching the RegExp
|
1570
1216
|
*/
|
1571
1217
|
function stringInputToObject(input) {
|
1572
|
-
let color = input.trim()
|
1218
|
+
let color = toLowerCase(input.trim());
|
1219
|
+
|
1573
1220
|
if (color.length === 0) {
|
1574
1221
|
return {
|
1575
|
-
r: 0, g: 0, b: 0, a:
|
1222
|
+
r: 0, g: 0, b: 0, a: 1,
|
1576
1223
|
};
|
1577
1224
|
}
|
1578
|
-
|
1225
|
+
|
1579
1226
|
if (isColorName(color)) {
|
1580
1227
|
color = getRGBFromName(color);
|
1581
|
-
named = true;
|
1582
1228
|
} else if (nonColors.includes(color)) {
|
1583
|
-
const
|
1584
|
-
const rgb = isTransparent ? 0 : 255;
|
1585
|
-
const a = isTransparent ? 0 : 1;
|
1229
|
+
const a = color === 'transparent' ? 0 : 1;
|
1586
1230
|
return {
|
1587
|
-
r:
|
1231
|
+
r: 0, g: 0, b: 0, a, format: 'rgb', ok: true,
|
1588
1232
|
};
|
1589
1233
|
}
|
1590
1234
|
|
@@ -1599,24 +1243,28 @@ function stringInputToObject(input) {
|
|
1599
1243
|
r: m1, g: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'rgb',
|
1600
1244
|
};
|
1601
1245
|
}
|
1246
|
+
|
1602
1247
|
[, m1, m2, m3, m4] = matchers.hsl.exec(color) || [];
|
1603
1248
|
if (m1 && m2 && m3/* && m4 */) {
|
1604
1249
|
return {
|
1605
1250
|
h: m1, s: m2, l: m3, a: m4 !== undefined ? m4 : 1, format: 'hsl',
|
1606
1251
|
};
|
1607
1252
|
}
|
1253
|
+
|
1608
1254
|
[, m1, m2, m3, m4] = matchers.hsv.exec(color) || [];
|
1609
1255
|
if (m1 && m2 && m3/* && m4 */) {
|
1610
1256
|
return {
|
1611
1257
|
h: m1, s: m2, v: m3, a: m4 !== undefined ? m4 : 1, format: 'hsv',
|
1612
1258
|
};
|
1613
1259
|
}
|
1260
|
+
|
1614
1261
|
[, m1, m2, m3, m4] = matchers.hwb.exec(color) || [];
|
1615
1262
|
if (m1 && m2 && m3) {
|
1616
1263
|
return {
|
1617
1264
|
h: m1, w: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'hwb',
|
1618
1265
|
};
|
1619
1266
|
}
|
1267
|
+
|
1620
1268
|
[, m1, m2, m3, m4] = matchers.hex8.exec(color) || [];
|
1621
1269
|
if (m1 && m2 && m3 && m4) {
|
1622
1270
|
return {
|
@@ -1624,19 +1272,20 @@ function stringInputToObject(input) {
|
|
1624
1272
|
g: parseIntFromHex(m2),
|
1625
1273
|
b: parseIntFromHex(m3),
|
1626
1274
|
a: convertHexToDecimal(m4),
|
1627
|
-
|
1628
|
-
format: named ? 'rgb' : 'hex',
|
1275
|
+
format: 'hex',
|
1629
1276
|
};
|
1630
1277
|
}
|
1278
|
+
|
1631
1279
|
[, m1, m2, m3] = matchers.hex6.exec(color) || [];
|
1632
1280
|
if (m1 && m2 && m3) {
|
1633
1281
|
return {
|
1634
1282
|
r: parseIntFromHex(m1),
|
1635
1283
|
g: parseIntFromHex(m2),
|
1636
1284
|
b: parseIntFromHex(m3),
|
1637
|
-
format:
|
1285
|
+
format: 'hex',
|
1638
1286
|
};
|
1639
1287
|
}
|
1288
|
+
|
1640
1289
|
[, m1, m2, m3, m4] = matchers.hex4.exec(color) || [];
|
1641
1290
|
if (m1 && m2 && m3 && m4) {
|
1642
1291
|
return {
|
@@ -1644,19 +1293,20 @@ function stringInputToObject(input) {
|
|
1644
1293
|
g: parseIntFromHex(m2 + m2),
|
1645
1294
|
b: parseIntFromHex(m3 + m3),
|
1646
1295
|
a: convertHexToDecimal(m4 + m4),
|
1647
|
-
|
1648
|
-
format: named ? 'rgb' : 'hex',
|
1296
|
+
format: 'hex',
|
1649
1297
|
};
|
1650
1298
|
}
|
1299
|
+
|
1651
1300
|
[, m1, m2, m3] = matchers.hex3.exec(color) || [];
|
1652
1301
|
if (m1 && m2 && m3) {
|
1653
1302
|
return {
|
1654
1303
|
r: parseIntFromHex(m1 + m1),
|
1655
1304
|
g: parseIntFromHex(m2 + m2),
|
1656
1305
|
b: parseIntFromHex(m3 + m3),
|
1657
|
-
format:
|
1306
|
+
format: 'hex',
|
1658
1307
|
};
|
1659
1308
|
}
|
1309
|
+
|
1660
1310
|
return false;
|
1661
1311
|
}
|
1662
1312
|
|
@@ -1687,7 +1337,9 @@ function stringInputToObject(input) {
|
|
1687
1337
|
*/
|
1688
1338
|
function inputToRGB(input) {
|
1689
1339
|
let rgb = { r: 0, g: 0, b: 0 };
|
1340
|
+
/** @type {*} */
|
1690
1341
|
let color = input;
|
1342
|
+
/** @type {string | number} */
|
1691
1343
|
let a = 1;
|
1692
1344
|
let s = null;
|
1693
1345
|
let v = null;
|
@@ -1698,58 +1350,67 @@ function inputToRGB(input) {
|
|
1698
1350
|
let r = null;
|
1699
1351
|
let g = null;
|
1700
1352
|
let ok = false;
|
1701
|
-
|
1353
|
+
const inputFormat = typeof color === 'object' && color.format;
|
1354
|
+
let format = inputFormat && COLOR_FORMAT.includes(inputFormat) ? inputFormat : 'rgb';
|
1702
1355
|
|
1703
1356
|
if (typeof input === 'string') {
|
1704
|
-
// @ts-ignore -- this now is converted to object
|
1705
1357
|
color = stringInputToObject(input);
|
1706
1358
|
if (color) ok = true;
|
1707
1359
|
}
|
1708
1360
|
if (typeof color === 'object') {
|
1709
1361
|
if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
|
1710
1362
|
({ r, g, b } = color);
|
1711
|
-
// RGB values now are all in [0,
|
1712
|
-
[r, g, b] = [r, g, b].map((n) => bound01(n, isPercentage(n) ? 100 : 255)
|
1363
|
+
// RGB values now are all in [0, 1] range
|
1364
|
+
[r, g, b] = [r, g, b].map((n) => bound01(n, isPercentage(n) ? 100 : 255));
|
1713
1365
|
rgb = { r, g, b };
|
1714
1366
|
ok = true;
|
1715
|
-
format = 'rgb';
|
1716
|
-
}
|
1367
|
+
format = color.format || 'rgb';
|
1368
|
+
}
|
1369
|
+
if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
|
1717
1370
|
({ h, s, v } = color);
|
1718
|
-
h =
|
1719
|
-
s =
|
1720
|
-
v =
|
1371
|
+
h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
|
1372
|
+
s = bound01(s, 100); // saturation can be `5%` or a [0, 1] value
|
1373
|
+
v = bound01(v, 100); // brightness can be `5%` or a [0, 1] value
|
1721
1374
|
rgb = hsvToRgb(h, s, v);
|
1722
1375
|
ok = true;
|
1723
1376
|
format = 'hsv';
|
1724
|
-
}
|
1377
|
+
}
|
1378
|
+
if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
|
1725
1379
|
({ h, s, l } = color);
|
1726
|
-
h =
|
1727
|
-
s =
|
1728
|
-
l =
|
1380
|
+
h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
|
1381
|
+
s = bound01(s, 100); // saturation can be `5%` or a [0, 1] value
|
1382
|
+
l = bound01(l, 100); // lightness can be `5%` or a [0, 1] value
|
1729
1383
|
rgb = hslToRgb(h, s, l);
|
1730
1384
|
ok = true;
|
1731
1385
|
format = 'hsl';
|
1732
|
-
}
|
1386
|
+
}
|
1387
|
+
if (isValidCSSUnit(color.h) && isValidCSSUnit(color.w) && isValidCSSUnit(color.b)) {
|
1733
1388
|
({ h, w, b } = color);
|
1734
|
-
h =
|
1735
|
-
w =
|
1736
|
-
b =
|
1389
|
+
h = bound01(h, 360); // hue can be `5deg` or a [0, 1] value
|
1390
|
+
w = bound01(w, 100); // whiteness can be `5%` or a [0, 1] value
|
1391
|
+
b = bound01(b, 100); // blackness can be `5%` or a [0, 1] value
|
1737
1392
|
rgb = hwbToRgb(h, w, b);
|
1738
1393
|
ok = true;
|
1739
1394
|
format = 'hwb';
|
1740
1395
|
}
|
1741
1396
|
if (isValidCSSUnit(color.a)) {
|
1742
|
-
a = color.a;
|
1743
|
-
a = isPercentage(`${a}`) ? bound01(a, 100) : a;
|
1397
|
+
a = color.a; // @ts-ignore -- `parseFloat` works with numbers too
|
1398
|
+
a = isPercentage(`${a}`) || parseFloat(a) > 1 ? bound01(a, 100) : a;
|
1744
1399
|
}
|
1745
1400
|
}
|
1401
|
+
if (typeof color === 'undefined') {
|
1402
|
+
ok = true;
|
1403
|
+
}
|
1746
1404
|
|
1747
1405
|
return {
|
1748
|
-
ok,
|
1749
|
-
format
|
1750
|
-
r: Math.min(255, Math.max(rgb.r, 0)),
|
1751
|
-
g: Math.min(255, Math.max(rgb.g, 0)),
|
1752
|
-
b: Math.min(255, Math.max(rgb.b, 0)),
|
1406
|
+
ok,
|
1407
|
+
format,
|
1408
|
+
// r: Math.min(255, Math.max(rgb.r, 0)),
|
1409
|
+
// g: Math.min(255, Math.max(rgb.g, 0)),
|
1410
|
+
// b: Math.min(255, Math.max(rgb.b, 0)),
|
1411
|
+
r: rgb.r,
|
1412
|
+
g: rgb.g,
|
1413
|
+
b: rgb.b,
|
1753
1414
|
a: boundAlpha(a),
|
1754
1415
|
};
|
1755
1416
|
}
|
@@ -1768,15 +1429,13 @@ class Color {
|
|
1768
1429
|
constructor(input, config) {
|
1769
1430
|
let color = input;
|
1770
1431
|
const configFormat = config && COLOR_FORMAT.includes(config)
|
1771
|
-
? config : '
|
1432
|
+
? config : '';
|
1772
1433
|
|
1773
|
-
// If input is already a `Color`,
|
1434
|
+
// If input is already a `Color`, clone its values
|
1774
1435
|
if (color instanceof Color) {
|
1775
1436
|
color = inputToRGB(color);
|
1776
1437
|
}
|
1777
|
-
|
1778
|
-
color = numberInputToObject(color);
|
1779
|
-
}
|
1438
|
+
|
1780
1439
|
const {
|
1781
1440
|
r, g, b, a, ok, format,
|
1782
1441
|
} = inputToRGB(color);
|
@@ -1785,7 +1444,7 @@ class Color {
|
|
1785
1444
|
const self = this;
|
1786
1445
|
|
1787
1446
|
/** @type {CP.ColorInput} */
|
1788
|
-
self.originalInput =
|
1447
|
+
self.originalInput = input;
|
1789
1448
|
/** @type {number} */
|
1790
1449
|
self.r = r;
|
1791
1450
|
/** @type {number} */
|
@@ -1826,24 +1485,21 @@ class Color {
|
|
1826
1485
|
let R = 0;
|
1827
1486
|
let G = 0;
|
1828
1487
|
let B = 0;
|
1829
|
-
const rp = r / 255;
|
1830
|
-
const rg = g / 255;
|
1831
|
-
const rb = b / 255;
|
1832
1488
|
|
1833
|
-
if (
|
1834
|
-
R =
|
1489
|
+
if (r <= 0.03928) {
|
1490
|
+
R = r / 12.92;
|
1835
1491
|
} else {
|
1836
|
-
R = ((
|
1492
|
+
R = ((r + 0.055) / 1.055) ** 2.4;
|
1837
1493
|
}
|
1838
|
-
if (
|
1839
|
-
G =
|
1494
|
+
if (g <= 0.03928) {
|
1495
|
+
G = g / 12.92;
|
1840
1496
|
} else {
|
1841
|
-
G = ((
|
1497
|
+
G = ((g + 0.055) / 1.055) ** 2.4;
|
1842
1498
|
}
|
1843
|
-
if (
|
1844
|
-
B =
|
1499
|
+
if (b <= 0.03928) {
|
1500
|
+
B = b / 12.92;
|
1845
1501
|
} else {
|
1846
|
-
B = ((
|
1502
|
+
B = ((b + 0.055) / 1.055) ** 2.4;
|
1847
1503
|
}
|
1848
1504
|
return 0.2126 * R + 0.7152 * G + 0.0722 * B;
|
1849
1505
|
}
|
@@ -1853,7 +1509,7 @@ class Color {
|
|
1853
1509
|
* @returns {number} a number in the [0, 255] range
|
1854
1510
|
*/
|
1855
1511
|
get brightness() {
|
1856
|
-
const { r, g, b } = this;
|
1512
|
+
const { r, g, b } = this.toRgb();
|
1857
1513
|
return (r * 299 + g * 587 + b * 114) / 1000;
|
1858
1514
|
}
|
1859
1515
|
|
@@ -1862,12 +1518,14 @@ class Color {
|
|
1862
1518
|
* @returns {CP.RGBA} an {r,g,b,a} object with [0, 255] ranged values
|
1863
1519
|
*/
|
1864
1520
|
toRgb() {
|
1865
|
-
|
1521
|
+
let {
|
1866
1522
|
r, g, b, a,
|
1867
1523
|
} = this;
|
1868
1524
|
|
1525
|
+
[r, g, b] = [r, g, b].map((n) => roundPart(n * 255 * 100) / 100);
|
1526
|
+
a = roundPart(a * 100) / 100;
|
1869
1527
|
return {
|
1870
|
-
r, g, b, a
|
1528
|
+
r, g, b, a,
|
1871
1529
|
};
|
1872
1530
|
}
|
1873
1531
|
|
@@ -1961,7 +1619,7 @@ class Color {
|
|
1961
1619
|
toHsv() {
|
1962
1620
|
const {
|
1963
1621
|
r, g, b, a,
|
1964
|
-
} = this
|
1622
|
+
} = this;
|
1965
1623
|
const { h, s, v } = rgbToHsv(r, g, b);
|
1966
1624
|
|
1967
1625
|
return {
|
@@ -1976,7 +1634,7 @@ class Color {
|
|
1976
1634
|
toHsl() {
|
1977
1635
|
const {
|
1978
1636
|
r, g, b, a,
|
1979
|
-
} = this
|
1637
|
+
} = this;
|
1980
1638
|
const { h, s, l } = rgbToHsl(r, g, b);
|
1981
1639
|
|
1982
1640
|
return {
|
@@ -2061,6 +1719,7 @@ class Color {
|
|
2061
1719
|
*/
|
2062
1720
|
setAlpha(alpha) {
|
2063
1721
|
const self = this;
|
1722
|
+
if (typeof alpha !== 'number') return self;
|
2064
1723
|
self.a = boundAlpha(alpha);
|
2065
1724
|
return self;
|
2066
1725
|
}
|
@@ -2175,6 +1834,7 @@ ObjectAssign(Color, {
|
|
2175
1834
|
isOnePointZero,
|
2176
1835
|
isPercentage,
|
2177
1836
|
isValidCSSUnit,
|
1837
|
+
isColorName,
|
2178
1838
|
pad2,
|
2179
1839
|
clamp01,
|
2180
1840
|
bound01,
|
@@ -2192,10 +1852,11 @@ ObjectAssign(Color, {
|
|
2192
1852
|
hueToRgb,
|
2193
1853
|
hwbToRgb,
|
2194
1854
|
parseIntFromHex,
|
2195
|
-
numberInputToObject,
|
2196
1855
|
stringInputToObject,
|
2197
1856
|
inputToRGB,
|
2198
1857
|
roundPart,
|
1858
|
+
getElementStyle,
|
1859
|
+
setElementStyle,
|
2199
1860
|
ObjectAssign,
|
2200
1861
|
});
|
2201
1862
|
|
@@ -2204,7 +1865,7 @@ ObjectAssign(Color, {
|
|
2204
1865
|
* Returns a color palette with a given set of parameters.
|
2205
1866
|
* @example
|
2206
1867
|
* new ColorPalette(0, 12, 10);
|
2207
|
-
* // => { hue: 0, hueSteps: 12, lightSteps: 10, colors:
|
1868
|
+
* // => { hue: 0, hueSteps: 12, lightSteps: 10, colors: Array<Color> }
|
2208
1869
|
*/
|
2209
1870
|
class ColorPalette {
|
2210
1871
|
/**
|
@@ -2224,48 +1885,352 @@ class ColorPalette {
|
|
2224
1885
|
[hue, hueSteps, lightSteps] = args;
|
2225
1886
|
} else if (args.length === 2) {
|
2226
1887
|
[hueSteps, lightSteps] = args;
|
2227
|
-
|
2228
|
-
|
1888
|
+
if ([hueSteps, lightSteps].some((n) => n < 1)) {
|
1889
|
+
throw TypeError('ColorPalette: both arguments must be higher than 0.');
|
1890
|
+
}
|
2229
1891
|
}
|
2230
1892
|
|
2231
|
-
/** @type {
|
1893
|
+
/** @type {*} */
|
2232
1894
|
const colors = [];
|
2233
|
-
|
2234
1895
|
const hueStep = 360 / hueSteps;
|
2235
1896
|
const half = roundPart((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
|
2236
|
-
const
|
1897
|
+
const steps1To13 = [0.25, 0.2, 0.15, 0.11, 0.09, 0.075];
|
1898
|
+
const lightSets = [[1, 2, 3], [4, 5], [6, 7], [8, 9], [10, 11], [12, 13]];
|
1899
|
+
const closestSet = lightSets.find((set) => set.includes(lightSteps));
|
1900
|
+
|
1901
|
+
// find a lightStep that won't go beyond black and white
|
1902
|
+
// something within the [10-90] range of lightness
|
1903
|
+
const lightStep = closestSet
|
1904
|
+
? steps1To13[lightSets.indexOf(closestSet)]
|
1905
|
+
: (100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100);
|
1906
|
+
|
1907
|
+
// light tints
|
1908
|
+
for (let i = 1; i < half + 1; i += 1) {
|
1909
|
+
lightnessArray = [...lightnessArray, (0.5 + lightStep * (i))];
|
1910
|
+
}
|
1911
|
+
|
1912
|
+
// dark tints
|
1913
|
+
for (let i = 1; i < lightSteps - half; i += 1) {
|
1914
|
+
lightnessArray = [(0.5 - lightStep * (i)), ...lightnessArray];
|
1915
|
+
}
|
1916
|
+
|
1917
|
+
// feed `colors` Array
|
1918
|
+
for (let i = 0; i < hueSteps; i += 1) {
|
1919
|
+
const currentHue = ((hue + i * hueStep) % 360) / 360;
|
1920
|
+
lightnessArray.forEach((l) => {
|
1921
|
+
colors.push(new Color({ h: currentHue, s: 1, l }));
|
1922
|
+
});
|
1923
|
+
}
|
1924
|
+
|
1925
|
+
this.hue = hue;
|
1926
|
+
this.hueSteps = hueSteps;
|
1927
|
+
this.lightSteps = lightSteps;
|
1928
|
+
this.colors = colors;
|
1929
|
+
}
|
1930
|
+
}
|
1931
|
+
|
1932
|
+
ObjectAssign(ColorPalette, { Color });
|
1933
|
+
|
1934
|
+
/** @type {Record<string, string>} */
|
1935
|
+
const colorPickerLabels = {
|
1936
|
+
pickerLabel: 'Colour Picker',
|
1937
|
+
appearanceLabel: 'Colour Appearance',
|
1938
|
+
valueLabel: 'Colour Value',
|
1939
|
+
toggleLabel: 'Select Colour',
|
1940
|
+
presetsLabel: 'Colour Presets',
|
1941
|
+
defaultsLabel: 'Colour Defaults',
|
1942
|
+
formatLabel: 'Format',
|
1943
|
+
alphaLabel: 'Alpha',
|
1944
|
+
hexLabel: 'Hexadecimal',
|
1945
|
+
hueLabel: 'Hue',
|
1946
|
+
whitenessLabel: 'Whiteness',
|
1947
|
+
blacknessLabel: 'Blackness',
|
1948
|
+
saturationLabel: 'Saturation',
|
1949
|
+
lightnessLabel: 'Lightness',
|
1950
|
+
redLabel: 'Red',
|
1951
|
+
greenLabel: 'Green',
|
1952
|
+
blueLabel: 'Blue',
|
1953
|
+
};
|
1954
|
+
|
1955
|
+
/**
|
1956
|
+
* A list of 17 color names used for WAI-ARIA compliance.
|
1957
|
+
* @type {string[]}
|
1958
|
+
*/
|
1959
|
+
const colorNames = ['white', 'black', 'grey', 'red', 'orange', 'brown', 'gold', 'olive', 'yellow', 'lime', 'green', 'teal', 'cyan', 'blue', 'violet', 'magenta', 'pink'];
|
1960
|
+
|
1961
|
+
const tabIndex = 'tabindex';
|
1962
|
+
|
1963
|
+
/**
|
1964
|
+
* Check if a string is valid JSON string.
|
1965
|
+
* @param {string} str the string input
|
1966
|
+
* @returns {boolean} the query result
|
1967
|
+
*/
|
1968
|
+
function isValidJSON(str) {
|
1969
|
+
try {
|
1970
|
+
JSON.parse(str);
|
1971
|
+
} catch (e) {
|
1972
|
+
return false;
|
1973
|
+
}
|
1974
|
+
return true;
|
1975
|
+
}
|
1976
|
+
|
1977
|
+
/**
|
1978
|
+
* Shortcut for `String.toUpperCase()`.
|
1979
|
+
*
|
1980
|
+
* @param {string} source input string
|
1981
|
+
* @returns {string} uppercase output string
|
1982
|
+
*/
|
1983
|
+
const toUpperCase = (source) => source.toUpperCase();
|
1984
|
+
|
1985
|
+
/**
|
1986
|
+
* A global namespace for aria-haspopup.
|
1987
|
+
* @type {string}
|
1988
|
+
*/
|
1989
|
+
const ariaHasPopup = 'aria-haspopup';
|
1990
|
+
|
1991
|
+
/**
|
1992
|
+
* A global namespace for aria-hidden.
|
1993
|
+
* @type {string}
|
1994
|
+
*/
|
1995
|
+
const ariaHidden = 'aria-hidden';
|
1996
|
+
|
1997
|
+
/**
|
1998
|
+
* A global namespace for aria-labelledby.
|
1999
|
+
* @type {string}
|
2000
|
+
*/
|
2001
|
+
const ariaLabelledBy = 'aria-labelledby';
|
2002
|
+
|
2003
|
+
/**
|
2004
|
+
* This is a shortie for `document.createElement` method
|
2005
|
+
* which allows you to create a new `HTMLElement` for a given `tagName`
|
2006
|
+
* or based on an object with specific non-readonly attributes:
|
2007
|
+
* `id`, `className`, `textContent`, `style`, etc.
|
2008
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
|
2009
|
+
*
|
2010
|
+
* @param {Record<string, string> | string} param `tagName` or object
|
2011
|
+
* @return {HTMLElement | Element} a new `HTMLElement` or `Element`
|
2012
|
+
*/
|
2013
|
+
function createElement(param) {
|
2014
|
+
if (typeof param === 'string') {
|
2015
|
+
return getDocument().createElement(param);
|
2016
|
+
}
|
2017
|
+
|
2018
|
+
const { tagName } = param;
|
2019
|
+
const attr = { ...param };
|
2020
|
+
const newElement = createElement(tagName);
|
2021
|
+
delete attr.tagName;
|
2022
|
+
ObjectAssign(newElement, attr);
|
2023
|
+
return newElement;
|
2024
|
+
}
|
2025
|
+
|
2026
|
+
/**
|
2027
|
+
* This is a shortie for `document.createElementNS` method
|
2028
|
+
* which allows you to create a new `HTMLElement` for a given `tagName`
|
2029
|
+
* or based on an object with specific non-readonly attributes:
|
2030
|
+
* `id`, `className`, `textContent`, `style`, etc.
|
2031
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
|
2032
|
+
*
|
2033
|
+
* @param {string} namespace `namespaceURI` to associate with the new `HTMLElement`
|
2034
|
+
* @param {Record<string, string> | string} param `tagName` or object
|
2035
|
+
* @return {HTMLElement | Element} a new `HTMLElement` or `Element`
|
2036
|
+
*/
|
2037
|
+
function createElementNS(namespace, param) {
|
2038
|
+
if (typeof param === 'string') {
|
2039
|
+
return getDocument().createElementNS(namespace, param);
|
2040
|
+
}
|
2041
|
+
|
2042
|
+
const { tagName } = param;
|
2043
|
+
const attr = { ...param };
|
2044
|
+
const newElement = createElementNS(namespace, tagName);
|
2045
|
+
delete attr.tagName;
|
2046
|
+
ObjectAssign(newElement, attr);
|
2047
|
+
return newElement;
|
2048
|
+
}
|
2049
|
+
|
2050
|
+
const vHidden = 'v-hidden';
|
2051
|
+
|
2052
|
+
/**
|
2053
|
+
* Returns the color form for `ColorPicker`.
|
2054
|
+
*
|
2055
|
+
* @param {CP.ColorPicker} self the `ColorPicker` instance
|
2056
|
+
* @returns {HTMLElement | Element} a new `<div>` element with color component `<input>`
|
2057
|
+
*/
|
2058
|
+
function getColorForm(self) {
|
2059
|
+
const { format, id, componentLabels } = self;
|
2060
|
+
const colorForm = createElement({
|
2061
|
+
tagName: 'div',
|
2062
|
+
className: `color-form ${format}`,
|
2063
|
+
});
|
2064
|
+
|
2065
|
+
let components = ['hex'];
|
2066
|
+
if (format === 'rgb') components = ['red', 'green', 'blue', 'alpha'];
|
2067
|
+
else if (format === 'hsl') components = ['hue', 'saturation', 'lightness', 'alpha'];
|
2068
|
+
else if (format === 'hwb') components = ['hue', 'whiteness', 'blackness', 'alpha'];
|
2069
|
+
|
2070
|
+
components.forEach((c) => {
|
2071
|
+
const [C] = format === 'hex' ? ['#'] : toUpperCase(c).split('');
|
2072
|
+
const cID = `color_${format}_${c}_${id}`;
|
2073
|
+
const formatLabel = componentLabels[`${c}Label`];
|
2074
|
+
const cInputLabel = createElement({ tagName: 'label' });
|
2075
|
+
setAttribute(cInputLabel, 'for', cID);
|
2076
|
+
cInputLabel.append(
|
2077
|
+
createElement({ tagName: 'span', ariaHidden: 'true', innerText: `${C}:` }),
|
2078
|
+
createElement({ tagName: 'span', className: vHidden, innerText: formatLabel }),
|
2079
|
+
);
|
2080
|
+
const cInput = createElement({
|
2081
|
+
tagName: 'input',
|
2082
|
+
id: cID,
|
2083
|
+
// name: cID, - prevent saving the value to a form
|
2084
|
+
type: format === 'hex' ? 'text' : 'number',
|
2085
|
+
value: c === 'alpha' ? '100' : '0',
|
2086
|
+
className: `color-input ${c}`,
|
2087
|
+
});
|
2088
|
+
setAttribute(cInput, 'autocomplete', 'off');
|
2089
|
+
setAttribute(cInput, 'spellcheck', 'false');
|
2090
|
+
|
2091
|
+
// alpha
|
2092
|
+
let max = '100';
|
2093
|
+
let step = '1';
|
2094
|
+
if (c !== 'alpha') {
|
2095
|
+
if (format === 'rgb') {
|
2096
|
+
max = '255'; step = '1';
|
2097
|
+
} else if (c === 'hue') {
|
2098
|
+
max = '360'; step = '1';
|
2099
|
+
}
|
2100
|
+
}
|
2101
|
+
ObjectAssign(cInput, {
|
2102
|
+
min: '0',
|
2103
|
+
max,
|
2104
|
+
step,
|
2105
|
+
});
|
2106
|
+
colorForm.append(cInputLabel, cInput);
|
2107
|
+
});
|
2108
|
+
return colorForm;
|
2109
|
+
}
|
2110
|
+
|
2111
|
+
/**
|
2112
|
+
* A global namespace for aria-label.
|
2113
|
+
* @type {string}
|
2114
|
+
*/
|
2115
|
+
const ariaLabel = 'aria-label';
|
2116
|
+
|
2117
|
+
/**
|
2118
|
+
* A global namespace for aria-valuemin.
|
2119
|
+
* @type {string}
|
2120
|
+
*/
|
2121
|
+
const ariaValueMin = 'aria-valuemin';
|
2122
|
+
|
2123
|
+
/**
|
2124
|
+
* A global namespace for aria-valuemax.
|
2125
|
+
* @type {string}
|
2126
|
+
*/
|
2127
|
+
const ariaValueMax = 'aria-valuemax';
|
2128
|
+
|
2129
|
+
/**
|
2130
|
+
* Returns all color controls for `ColorPicker`.
|
2131
|
+
*
|
2132
|
+
* @param {CP.ColorPicker} self the `ColorPicker` instance
|
2133
|
+
* @returns {HTMLElement | Element} color controls
|
2134
|
+
*/
|
2135
|
+
function getColorControls(self) {
|
2136
|
+
const { format, componentLabels } = self;
|
2137
|
+
const {
|
2138
|
+
hueLabel, alphaLabel, lightnessLabel, saturationLabel,
|
2139
|
+
whitenessLabel, blacknessLabel,
|
2140
|
+
} = componentLabels;
|
2141
|
+
|
2142
|
+
const max1 = format === 'hsl' ? 360 : 100;
|
2143
|
+
const max2 = format === 'hsl' ? 100 : 360;
|
2144
|
+
const max3 = 100;
|
2145
|
+
|
2146
|
+
let ctrl1Label = format === 'hsl'
|
2147
|
+
? `${hueLabel} & ${lightnessLabel}`
|
2148
|
+
: `${lightnessLabel} & ${saturationLabel}`;
|
2149
|
+
|
2150
|
+
ctrl1Label = format === 'hwb'
|
2151
|
+
? `${whitenessLabel} & ${blacknessLabel}`
|
2152
|
+
: ctrl1Label;
|
2153
|
+
|
2154
|
+
const ctrl2Label = format === 'hsl'
|
2155
|
+
? `${saturationLabel}`
|
2156
|
+
: `${hueLabel}`;
|
2157
|
+
|
2158
|
+
const colorControls = createElement({
|
2159
|
+
tagName: 'div',
|
2160
|
+
className: `color-controls ${format}`,
|
2161
|
+
});
|
2162
|
+
|
2163
|
+
const colorPointer = 'color-pointer';
|
2164
|
+
const colorSlider = 'color-slider';
|
2165
|
+
|
2166
|
+
const controls = [
|
2167
|
+
{
|
2168
|
+
i: 1,
|
2169
|
+
c: colorPointer,
|
2170
|
+
l: ctrl1Label,
|
2171
|
+
min: 0,
|
2172
|
+
max: max1,
|
2173
|
+
},
|
2174
|
+
{
|
2175
|
+
i: 2,
|
2176
|
+
c: colorSlider,
|
2177
|
+
l: ctrl2Label,
|
2178
|
+
min: 0,
|
2179
|
+
max: max2,
|
2180
|
+
},
|
2181
|
+
{
|
2182
|
+
i: 3,
|
2183
|
+
c: colorSlider,
|
2184
|
+
l: alphaLabel,
|
2185
|
+
min: 0,
|
2186
|
+
max: max3,
|
2187
|
+
},
|
2188
|
+
];
|
2189
|
+
|
2190
|
+
controls.forEach((template) => {
|
2191
|
+
const {
|
2192
|
+
i, c, l, min, max,
|
2193
|
+
} = template;
|
2194
|
+
const control = createElement({
|
2195
|
+
tagName: 'div',
|
2196
|
+
className: 'color-control',
|
2197
|
+
});
|
2198
|
+
setAttribute(control, 'role', 'presentation');
|
2237
2199
|
|
2238
|
-
|
2239
|
-
|
2240
|
-
|
2241
|
-
|
2242
|
-
|
2243
|
-
|
2244
|
-
lightStep = lightSteps > 13 ? estimatedStep : lightStep;
|
2200
|
+
control.append(
|
2201
|
+
createElement({
|
2202
|
+
tagName: 'div',
|
2203
|
+
className: `visual-control visual-control${i}`,
|
2204
|
+
}),
|
2205
|
+
);
|
2245
2206
|
|
2246
|
-
|
2247
|
-
|
2248
|
-
|
2249
|
-
|
2207
|
+
const knob = createElement({
|
2208
|
+
tagName: 'div',
|
2209
|
+
className: `${c} knob`,
|
2210
|
+
ariaLive: 'polite',
|
2211
|
+
});
|
2250
2212
|
|
2251
|
-
|
2252
|
-
|
2253
|
-
|
2254
|
-
}
|
2213
|
+
setAttribute(knob, ariaLabel, l);
|
2214
|
+
setAttribute(knob, 'role', 'slider');
|
2215
|
+
setAttribute(knob, tabIndex, '0');
|
2216
|
+
setAttribute(knob, ariaValueMin, `${min}`);
|
2217
|
+
setAttribute(knob, ariaValueMax, `${max}`);
|
2218
|
+
control.append(knob);
|
2219
|
+
colorControls.append(control);
|
2220
|
+
});
|
2255
2221
|
|
2256
|
-
|
2257
|
-
|
2258
|
-
const currentHue = ((hue + i * hueStep) % 360) / 360;
|
2259
|
-
lightnessArray.forEach((l) => {
|
2260
|
-
colors.push(new Color({ h: currentHue, s: 1, l }).toHexString());
|
2261
|
-
});
|
2262
|
-
}
|
2222
|
+
return colorControls;
|
2223
|
+
}
|
2263
2224
|
|
2264
|
-
|
2265
|
-
|
2266
|
-
|
2267
|
-
|
2268
|
-
|
2225
|
+
/**
|
2226
|
+
* Helps setting CSS variables to the color-menu.
|
2227
|
+
* @param {HTMLElement} element
|
2228
|
+
* @param {Record<string,any>} props
|
2229
|
+
*/
|
2230
|
+
function setCSSProperties(element, props) {
|
2231
|
+
ObjectKeys(props).forEach((prop) => {
|
2232
|
+
element.style.setProperty(prop, props[prop]);
|
2233
|
+
});
|
2269
2234
|
}
|
2270
2235
|
|
2271
2236
|
/**
|
@@ -2301,7 +2266,8 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2301
2266
|
optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
|
2302
2267
|
const menuHeight = `${(rowCount || 1) * optionSize}rem`;
|
2303
2268
|
const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
|
2304
|
-
|
2269
|
+
/** @type {HTMLUListElement} */
|
2270
|
+
// @ts-ignore -- <UL> is an `HTMLElement`
|
2305
2271
|
const menu = createElement({
|
2306
2272
|
tagName: 'ul',
|
2307
2273
|
className: finalClass,
|
@@ -2309,7 +2275,7 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2309
2275
|
setAttribute(menu, 'role', 'listbox');
|
2310
2276
|
setAttribute(menu, ariaLabel, menuLabel);
|
2311
2277
|
|
2312
|
-
if (isScrollable) {
|
2278
|
+
if (isScrollable) {
|
2313
2279
|
setCSSProperties(menu, {
|
2314
2280
|
'--grid-item-size': `${optionSize}rem`,
|
2315
2281
|
'--grid-fit': fit,
|
@@ -2320,15 +2286,19 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2320
2286
|
}
|
2321
2287
|
|
2322
2288
|
colorsArray.forEach((x) => {
|
2323
|
-
|
2324
|
-
|
2325
|
-
|
2289
|
+
let [value, label] = typeof x === 'string' ? x.trim().split(':') : [];
|
2290
|
+
if (x instanceof Color) {
|
2291
|
+
value = x.toHexString();
|
2292
|
+
label = value;
|
2293
|
+
}
|
2294
|
+
const color = new Color(x instanceof Color ? x : value, format);
|
2295
|
+
const isActive = color.toString() === getAttribute(input, 'value');
|
2326
2296
|
const active = isActive ? ' active' : '';
|
2327
2297
|
|
2328
2298
|
const option = createElement({
|
2329
2299
|
tagName: 'li',
|
2330
2300
|
className: `color-option${active}`,
|
2331
|
-
innerText: `${label ||
|
2301
|
+
innerText: `${label || value}`,
|
2332
2302
|
});
|
2333
2303
|
|
2334
2304
|
setAttribute(option, tabIndex, '0');
|
@@ -2337,7 +2307,7 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2337
2307
|
setAttribute(option, ariaSelected, isActive ? 'true' : 'false');
|
2338
2308
|
|
2339
2309
|
if (isOptionsMenu) {
|
2340
|
-
setElementStyle(option, { backgroundColor:
|
2310
|
+
setElementStyle(option, { backgroundColor: value });
|
2341
2311
|
}
|
2342
2312
|
|
2343
2313
|
menu.append(option);
|
@@ -2346,55 +2316,10 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2346
2316
|
}
|
2347
2317
|
|
2348
2318
|
/**
|
2349
|
-
|
2350
|
-
|
2351
|
-
|
2352
|
-
|
2353
|
-
function isValidJSON(str) {
|
2354
|
-
try {
|
2355
|
-
JSON.parse(str);
|
2356
|
-
} catch (e) {
|
2357
|
-
return false;
|
2358
|
-
}
|
2359
|
-
return true;
|
2360
|
-
}
|
2361
|
-
|
2362
|
-
var version = "0.0.1";
|
2363
|
-
|
2364
|
-
// @ts-ignore
|
2365
|
-
|
2366
|
-
const Version = version;
|
2367
|
-
|
2368
|
-
// ColorPicker GC
|
2369
|
-
// ==============
|
2370
|
-
const colorPickerString = 'color-picker';
|
2371
|
-
const colorPickerSelector = `[data-function="${colorPickerString}"]`;
|
2372
|
-
const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
|
2373
|
-
const colorPickerDefaults = {
|
2374
|
-
componentLabels: colorPickerLabels,
|
2375
|
-
colorLabels: colorNames,
|
2376
|
-
format: 'rgb',
|
2377
|
-
colorPresets: false,
|
2378
|
-
colorKeywords: false,
|
2379
|
-
};
|
2380
|
-
|
2381
|
-
// ColorPicker Static Methods
|
2382
|
-
// ==========================
|
2383
|
-
|
2384
|
-
/** @type {CP.GetInstance<ColorPicker>} */
|
2385
|
-
const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
|
2386
|
-
|
2387
|
-
/** @type {CP.InitCallback<ColorPicker>} */
|
2388
|
-
const initColorPicker = (element) => new ColorPicker(element);
|
2389
|
-
|
2390
|
-
// ColorPicker Private Methods
|
2391
|
-
// ===========================
|
2392
|
-
|
2393
|
-
/**
|
2394
|
-
* Generate HTML markup and update instance properties.
|
2395
|
-
* @param {ColorPicker} self
|
2396
|
-
*/
|
2397
|
-
function initCallback(self) {
|
2319
|
+
* Generate HTML markup and update instance properties.
|
2320
|
+
* @param {CP.ColorPicker} self
|
2321
|
+
*/
|
2322
|
+
function setMarkup(self) {
|
2398
2323
|
const {
|
2399
2324
|
input, parent, format, id, componentLabels, colorKeywords, colorPresets,
|
2400
2325
|
} = self;
|
@@ -2409,9 +2334,7 @@ function initCallback(self) {
|
|
2409
2334
|
self.color = new Color(color, format);
|
2410
2335
|
|
2411
2336
|
// set initial controls dimensions
|
2412
|
-
|
2413
|
-
const dropClass = isMobile ? ' mobile' : '';
|
2414
|
-
const formatString = format === 'hex' ? hexLabel : format.toUpperCase();
|
2337
|
+
const formatString = format === 'hex' ? hexLabel : toUpperCase(format);
|
2415
2338
|
|
2416
2339
|
const pickerBtn = createElement({
|
2417
2340
|
id: `picker-btn-${id}`,
|
@@ -2428,7 +2351,7 @@ function initCallback(self) {
|
|
2428
2351
|
|
2429
2352
|
const pickerDropdown = createElement({
|
2430
2353
|
tagName: 'div',
|
2431
|
-
className:
|
2354
|
+
className: 'color-dropdown picker',
|
2432
2355
|
});
|
2433
2356
|
setAttribute(pickerDropdown, ariaLabelledBy, `picker-btn-${id}`);
|
2434
2357
|
setAttribute(pickerDropdown, 'role', 'group');
|
@@ -2444,7 +2367,7 @@ function initCallback(self) {
|
|
2444
2367
|
if (colorKeywords || colorPresets) {
|
2445
2368
|
const presetsDropdown = createElement({
|
2446
2369
|
tagName: 'div',
|
2447
|
-
className:
|
2370
|
+
className: 'color-dropdown scrollable menu',
|
2448
2371
|
});
|
2449
2372
|
|
2450
2373
|
// color presets
|
@@ -2494,6 +2417,37 @@ function initCallback(self) {
|
|
2494
2417
|
setAttribute(input, tabIndex, '-1');
|
2495
2418
|
}
|
2496
2419
|
|
2420
|
+
var version = "0.0.2alpha3";
|
2421
|
+
|
2422
|
+
// @ts-ignore
|
2423
|
+
|
2424
|
+
const Version = version;
|
2425
|
+
|
2426
|
+
// ColorPicker GC
|
2427
|
+
// ==============
|
2428
|
+
const colorPickerString = 'color-picker';
|
2429
|
+
const colorPickerSelector = `[data-function="${colorPickerString}"]`;
|
2430
|
+
const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
|
2431
|
+
const colorPickerDefaults = {
|
2432
|
+
componentLabels: colorPickerLabels,
|
2433
|
+
colorLabels: colorNames,
|
2434
|
+
format: 'rgb',
|
2435
|
+
colorPresets: false,
|
2436
|
+
colorKeywords: false,
|
2437
|
+
};
|
2438
|
+
|
2439
|
+
// ColorPicker Static Methods
|
2440
|
+
// ==========================
|
2441
|
+
|
2442
|
+
/** @type {CP.GetInstance<ColorPicker>} */
|
2443
|
+
const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
|
2444
|
+
|
2445
|
+
/** @type {CP.InitCallback<ColorPicker>} */
|
2446
|
+
const initColorPicker = (element) => new ColorPicker(element);
|
2447
|
+
|
2448
|
+
// ColorPicker Private Methods
|
2449
|
+
// ===========================
|
2450
|
+
|
2497
2451
|
/**
|
2498
2452
|
* Add / remove `ColorPicker` main event listeners.
|
2499
2453
|
* @param {ColorPicker} self
|
@@ -2506,8 +2460,6 @@ function toggleEvents(self, action) {
|
|
2506
2460
|
fn(input, focusinEvent, self.showPicker);
|
2507
2461
|
fn(pickerToggle, mouseclickEvent, self.togglePicker);
|
2508
2462
|
|
2509
|
-
fn(input, keydownEvent, self.keyToggle);
|
2510
|
-
|
2511
2463
|
if (menuToggle) {
|
2512
2464
|
fn(menuToggle, mouseclickEvent, self.toggleMenu);
|
2513
2465
|
}
|
@@ -2545,8 +2497,7 @@ function toggleEventsOnShown(self, action) {
|
|
2545
2497
|
fn(doc, pointerEvents.move, self.pointerMove);
|
2546
2498
|
fn(doc, pointerEvents.up, self.pointerUp);
|
2547
2499
|
fn(parent, focusoutEvent, self.handleFocusOut);
|
2548
|
-
|
2549
|
-
fn(win, keyupEvent, self.handleDismiss);
|
2500
|
+
fn(doc, keyupEvent, self.handleDismiss);
|
2550
2501
|
}
|
2551
2502
|
|
2552
2503
|
/**
|
@@ -2630,7 +2581,7 @@ class ColorPicker {
|
|
2630
2581
|
const input = querySelector(target);
|
2631
2582
|
|
2632
2583
|
// invalidate
|
2633
|
-
if (!input) throw new TypeError(`ColorPicker target ${target} cannot be found.`);
|
2584
|
+
if (!input) throw new TypeError(`ColorPicker target "${target}" cannot be found.`);
|
2634
2585
|
self.input = input;
|
2635
2586
|
|
2636
2587
|
const parent = closest(input, colorPickerParentSelector);
|
@@ -2677,15 +2628,14 @@ class ColorPicker {
|
|
2677
2628
|
});
|
2678
2629
|
|
2679
2630
|
// update and expose component labels
|
2680
|
-
const
|
2681
|
-
|
2682
|
-
? JSON.parse(componentLabels) : componentLabels || {};
|
2631
|
+
const tempComponentLabels = componentLabels && isValidJSON(componentLabels)
|
2632
|
+
? JSON.parse(componentLabels) : componentLabels;
|
2683
2633
|
|
2684
2634
|
/** @type {Record<string, string>} */
|
2685
|
-
self.componentLabels = ObjectAssign(
|
2635
|
+
self.componentLabels = ObjectAssign(colorPickerLabels, tempComponentLabels);
|
2686
2636
|
|
2687
2637
|
/** @type {Color} */
|
2688
|
-
self.color = new Color('
|
2638
|
+
self.color = new Color(input.value || '#fff', format);
|
2689
2639
|
|
2690
2640
|
/** @type {CP.ColorFormats} */
|
2691
2641
|
self.format = format;
|
@@ -2694,7 +2644,7 @@ class ColorPicker {
|
|
2694
2644
|
if (colorKeywords instanceof Array) {
|
2695
2645
|
self.colorKeywords = colorKeywords;
|
2696
2646
|
} else if (typeof colorKeywords === 'string' && colorKeywords.length) {
|
2697
|
-
self.colorKeywords = colorKeywords.split(',');
|
2647
|
+
self.colorKeywords = colorKeywords.split(',').map((x) => x.trim());
|
2698
2648
|
}
|
2699
2649
|
|
2700
2650
|
// set colour presets
|
@@ -2723,11 +2673,10 @@ class ColorPicker {
|
|
2723
2673
|
self.handleFocusOut = self.handleFocusOut.bind(self);
|
2724
2674
|
self.changeHandler = self.changeHandler.bind(self);
|
2725
2675
|
self.handleDismiss = self.handleDismiss.bind(self);
|
2726
|
-
self.keyToggle = self.keyToggle.bind(self);
|
2727
2676
|
self.handleKnobs = self.handleKnobs.bind(self);
|
2728
2677
|
|
2729
2678
|
// generate markup
|
2730
|
-
|
2679
|
+
setMarkup(self);
|
2731
2680
|
|
2732
2681
|
const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
|
2733
2682
|
// set main elements
|
@@ -2815,76 +2764,83 @@ class ColorPicker {
|
|
2815
2764
|
return inputValue !== '' && new Color(inputValue).isValid;
|
2816
2765
|
}
|
2817
2766
|
|
2767
|
+
/** Returns the colour appearance, usually the closest colour name for the current value. */
|
2768
|
+
get appearance() {
|
2769
|
+
const {
|
2770
|
+
colorLabels, hsl, hsv, format,
|
2771
|
+
} = this;
|
2772
|
+
|
2773
|
+
const hue = roundPart(hsl.h * 360);
|
2774
|
+
const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
|
2775
|
+
const saturation = roundPart(saturationSource * 100);
|
2776
|
+
const lightness = roundPart(hsl.l * 100);
|
2777
|
+
const hsvl = hsv.v * 100;
|
2778
|
+
|
2779
|
+
let colorName;
|
2780
|
+
|
2781
|
+
// determine color appearance
|
2782
|
+
if (lightness === 100 && saturation === 0) {
|
2783
|
+
colorName = colorLabels.white;
|
2784
|
+
} else if (lightness === 0) {
|
2785
|
+
colorName = colorLabels.black;
|
2786
|
+
} else if (saturation === 0) {
|
2787
|
+
colorName = colorLabels.grey;
|
2788
|
+
} else if (hue < 15 || hue >= 345) {
|
2789
|
+
colorName = colorLabels.red;
|
2790
|
+
} else if (hue >= 15 && hue < 45) {
|
2791
|
+
colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
|
2792
|
+
} else if (hue >= 45 && hue < 75) {
|
2793
|
+
const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
|
2794
|
+
const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
|
2795
|
+
colorName = isGold ? colorLabels.gold : colorLabels.yellow;
|
2796
|
+
colorName = isOlive ? colorLabels.olive : colorName;
|
2797
|
+
} else if (hue >= 75 && hue < 155) {
|
2798
|
+
colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
|
2799
|
+
} else if (hue >= 155 && hue < 175) {
|
2800
|
+
colorName = colorLabels.teal;
|
2801
|
+
} else if (hue >= 175 && hue < 195) {
|
2802
|
+
colorName = colorLabels.cyan;
|
2803
|
+
} else if (hue >= 195 && hue < 255) {
|
2804
|
+
colorName = colorLabels.blue;
|
2805
|
+
} else if (hue >= 255 && hue < 270) {
|
2806
|
+
colorName = colorLabels.violet;
|
2807
|
+
} else if (hue >= 270 && hue < 295) {
|
2808
|
+
colorName = colorLabels.magenta;
|
2809
|
+
} else if (hue >= 295 && hue < 345) {
|
2810
|
+
colorName = colorLabels.pink;
|
2811
|
+
}
|
2812
|
+
return colorName;
|
2813
|
+
}
|
2814
|
+
|
2818
2815
|
/** Updates `ColorPicker` visuals. */
|
2819
2816
|
updateVisuals() {
|
2820
2817
|
const self = this;
|
2821
2818
|
const {
|
2822
|
-
|
2819
|
+
controlPositions, visuals,
|
2823
2820
|
} = self;
|
2824
2821
|
const [v1, v2, v3] = visuals;
|
2825
|
-
const {
|
2826
|
-
const hue =
|
2827
|
-
|
2828
|
-
: controlPositions.c2y / offsetHeight;
|
2829
|
-
// @ts-ignore - `hslToRgb` is assigned to `Color` as static method
|
2830
|
-
const { r, g, b } = Color.hslToRgb(hue, 1, 0.5);
|
2822
|
+
const { offsetHeight } = v1;
|
2823
|
+
const hue = controlPositions.c2y / offsetHeight;
|
2824
|
+
const { r, g, b } = new Color({ h: hue, s: 1, l: 0.5 }).toRgb();
|
2831
2825
|
const whiteGrad = 'linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)';
|
2832
2826
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
2833
2827
|
const roundA = roundPart((alpha * 100)) / 100;
|
2834
2828
|
|
2835
|
-
|
2836
|
-
|
2837
|
-
|
2838
|
-
|
2839
|
-
|
2840
|
-
|
2841
|
-
|
2842
|
-
|
2843
|
-
|
2844
|
-
|
2845
|
-
|
2846
|
-
|
2847
|
-
|
2848
|
-
|
2849
|
-
setElementStyle(v2, { background: hueGradient });
|
2850
|
-
} else {
|
2851
|
-
const saturation = roundPart((controlPositions.c2y / offsetHeight) * 100);
|
2852
|
-
const fill0 = new Color({
|
2853
|
-
r: 255, g: 0, b: 0, a: alpha,
|
2854
|
-
}).saturate(-saturation).toRgbString();
|
2855
|
-
const fill1 = new Color({
|
2856
|
-
r: 255, g: 255, b: 0, a: alpha,
|
2857
|
-
}).saturate(-saturation).toRgbString();
|
2858
|
-
const fill2 = new Color({
|
2859
|
-
r: 0, g: 255, b: 0, a: alpha,
|
2860
|
-
}).saturate(-saturation).toRgbString();
|
2861
|
-
const fill3 = new Color({
|
2862
|
-
r: 0, g: 255, b: 255, a: alpha,
|
2863
|
-
}).saturate(-saturation).toRgbString();
|
2864
|
-
const fill4 = new Color({
|
2865
|
-
r: 0, g: 0, b: 255, a: alpha,
|
2866
|
-
}).saturate(-saturation).toRgbString();
|
2867
|
-
const fill5 = new Color({
|
2868
|
-
r: 255, g: 0, b: 255, a: alpha,
|
2869
|
-
}).saturate(-saturation).toRgbString();
|
2870
|
-
const fill6 = new Color({
|
2871
|
-
r: 255, g: 0, b: 0, a: alpha,
|
2872
|
-
}).saturate(-saturation).toRgbString();
|
2873
|
-
const fillGradient = `linear-gradient(to right,
|
2874
|
-
${fill0} 0%, ${fill1} 16.67%, ${fill2} 33.33%, ${fill3} 50%,
|
2875
|
-
${fill4} 66.67%, ${fill5} 83.33%, ${fill6} 100%)`;
|
2876
|
-
const lightGrad = `linear-gradient(rgba(255,255,255,${roundA}) 0%, rgba(255,255,255,0) 50%),
|
2877
|
-
linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,${roundA}) 100%)`;
|
2878
|
-
|
2879
|
-
setElementStyle(v1, { background: `${lightGrad},${fillGradient},${whiteGrad}` });
|
2880
|
-
const {
|
2881
|
-
r: gr, g: gg, b: gb,
|
2882
|
-
} = new Color({ r, g, b }).greyscale().toRgb();
|
2829
|
+
const fill = new Color({
|
2830
|
+
h: hue, s: 1, l: 0.5, a: alpha,
|
2831
|
+
}).toRgbString();
|
2832
|
+
const hueGradient = `linear-gradient(
|
2833
|
+
rgb(255,0,0) 0%, rgb(255,255,0) 16.67%,
|
2834
|
+
rgb(0,255,0) 33.33%, rgb(0,255,255) 50%,
|
2835
|
+
rgb(0,0,255) 66.67%, rgb(255,0,255) 83.33%,
|
2836
|
+
rgb(255,0,0) 100%)`;
|
2837
|
+
setElementStyle(v1, {
|
2838
|
+
background: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,${roundA}) 100%),
|
2839
|
+
linear-gradient(to right, rgba(255,255,255,${roundA}) 0%, ${fill} 100%),
|
2840
|
+
${whiteGrad}`,
|
2841
|
+
});
|
2842
|
+
setElementStyle(v2, { background: hueGradient });
|
2883
2843
|
|
2884
|
-
setElementStyle(v2, {
|
2885
|
-
background: `linear-gradient(rgb(${r},${g},${b}) 0%, rgb(${gr},${gg},${gb}) 100%)`,
|
2886
|
-
});
|
2887
|
-
}
|
2888
2844
|
setElementStyle(v3, {
|
2889
2845
|
background: `linear-gradient(rgba(${r},${g},${b},1) 0%,rgba(${r},${g},${b},0) 100%)`,
|
2890
2846
|
});
|
@@ -2923,7 +2879,7 @@ class ColorPicker {
|
|
2923
2879
|
const self = this;
|
2924
2880
|
const { activeElement } = getDocument(self.input);
|
2925
2881
|
|
2926
|
-
if ((
|
2882
|
+
if ((e.type === touchmoveEvent && self.dragElement)
|
2927
2883
|
|| (activeElement && self.controlKnobs.includes(activeElement))) {
|
2928
2884
|
e.stopPropagation();
|
2929
2885
|
e.preventDefault();
|
@@ -3034,13 +2990,13 @@ class ColorPicker {
|
|
3034
2990
|
const [v1, v2, v3] = visuals;
|
3035
2991
|
const [c1, c2, c3] = controlKnobs;
|
3036
2992
|
/** @type {HTMLElement} */
|
3037
|
-
const visual =
|
3038
|
-
? target : querySelector('.visual-control', target.parentElement);
|
2993
|
+
const visual = controlKnobs.includes(target) ? target.previousElementSibling : target;
|
3039
2994
|
const visualRect = getBoundingClientRect(visual);
|
2995
|
+
const html = getDocumentElement(v1);
|
3040
2996
|
const X = type === 'touchstart' ? touches[0].pageX : pageX;
|
3041
2997
|
const Y = type === 'touchstart' ? touches[0].pageY : pageY;
|
3042
|
-
const offsetX = X -
|
3043
|
-
const offsetY = Y -
|
2998
|
+
const offsetX = X - html.scrollLeft - visualRect.left;
|
2999
|
+
const offsetY = Y - html.scrollTop - visualRect.top;
|
3044
3000
|
|
3045
3001
|
if (target === v1 || target === c1) {
|
3046
3002
|
self.dragElement = visual;
|
@@ -3100,10 +3056,11 @@ class ColorPicker {
|
|
3100
3056
|
if (!dragElement) return;
|
3101
3057
|
|
3102
3058
|
const controlRect = getBoundingClientRect(dragElement);
|
3103
|
-
const
|
3104
|
-
const
|
3105
|
-
const
|
3106
|
-
const
|
3059
|
+
const win = getDocumentElement(v1);
|
3060
|
+
const X = type === touchmoveEvent ? touches[0].pageX : pageX;
|
3061
|
+
const Y = type === touchmoveEvent ? touches[0].pageY : pageY;
|
3062
|
+
const offsetX = X - win.scrollLeft - controlRect.left;
|
3063
|
+
const offsetY = Y - win.scrollTop - controlRect.top;
|
3107
3064
|
|
3108
3065
|
if (dragElement === v1) {
|
3109
3066
|
self.changeControl1(offsetX, offsetY);
|
@@ -3130,19 +3087,19 @@ class ColorPicker {
|
|
3130
3087
|
if (![keyArrowUp, keyArrowDown, keyArrowLeft, keyArrowRight].includes(code)) return;
|
3131
3088
|
e.preventDefault();
|
3132
3089
|
|
3133
|
-
const {
|
3090
|
+
const { controlKnobs, visuals } = self;
|
3134
3091
|
const { offsetWidth, offsetHeight } = visuals[0];
|
3135
3092
|
const [c1, c2, c3] = controlKnobs;
|
3136
3093
|
const { activeElement } = getDocument(c1);
|
3137
3094
|
const currentKnob = controlKnobs.find((x) => x === activeElement);
|
3138
|
-
const yRatio = offsetHeight /
|
3095
|
+
const yRatio = offsetHeight / 360;
|
3139
3096
|
|
3140
3097
|
if (currentKnob) {
|
3141
3098
|
let offsetX = 0;
|
3142
3099
|
let offsetY = 0;
|
3143
3100
|
|
3144
3101
|
if (target === c1) {
|
3145
|
-
const xRatio = offsetWidth /
|
3102
|
+
const xRatio = offsetWidth / 100;
|
3146
3103
|
|
3147
3104
|
if ([keyArrowLeft, keyArrowRight].includes(code)) {
|
3148
3105
|
self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
|
@@ -3192,7 +3149,7 @@ class ColorPicker {
|
|
3192
3149
|
if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
|
3193
3150
|
if (activeElement === input) {
|
3194
3151
|
if (isNonColorValue) {
|
3195
|
-
colorSource = '
|
3152
|
+
colorSource = currentValue === 'transparent' ? 'rgba(0,0,0,0)' : 'rgb(0,0,0)';
|
3196
3153
|
} else {
|
3197
3154
|
colorSource = currentValue;
|
3198
3155
|
}
|
@@ -3243,9 +3200,7 @@ class ColorPicker {
|
|
3243
3200
|
changeControl1(X, Y) {
|
3244
3201
|
const self = this;
|
3245
3202
|
let [offsetX, offsetY] = [0, 0];
|
3246
|
-
const {
|
3247
|
-
format, controlPositions, visuals,
|
3248
|
-
} = self;
|
3203
|
+
const { controlPositions, visuals } = self;
|
3249
3204
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3250
3205
|
|
3251
3206
|
if (X > offsetWidth) offsetX = offsetWidth;
|
@@ -3254,29 +3209,19 @@ class ColorPicker {
|
|
3254
3209
|
if (Y > offsetHeight) offsetY = offsetHeight;
|
3255
3210
|
else if (Y >= 0) offsetY = Y;
|
3256
3211
|
|
3257
|
-
const hue =
|
3258
|
-
? offsetX / offsetWidth
|
3259
|
-
: controlPositions.c2y / offsetHeight;
|
3212
|
+
const hue = controlPositions.c2y / offsetHeight;
|
3260
3213
|
|
3261
|
-
const saturation =
|
3262
|
-
? 1 - controlPositions.c2y / offsetHeight
|
3263
|
-
: offsetX / offsetWidth;
|
3214
|
+
const saturation = offsetX / offsetWidth;
|
3264
3215
|
|
3265
3216
|
const lightness = 1 - offsetY / offsetHeight;
|
3266
3217
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
3267
3218
|
|
3268
|
-
const colorObject = format === 'hsl'
|
3269
|
-
? {
|
3270
|
-
h: hue, s: saturation, l: lightness, a: alpha,
|
3271
|
-
}
|
3272
|
-
: {
|
3273
|
-
h: hue, s: saturation, v: lightness, a: alpha,
|
3274
|
-
};
|
3275
|
-
|
3276
3219
|
// new color
|
3277
3220
|
const {
|
3278
3221
|
r, g, b, a,
|
3279
|
-
} = new Color(
|
3222
|
+
} = new Color({
|
3223
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
3224
|
+
});
|
3280
3225
|
|
3281
3226
|
ObjectAssign(self.color, {
|
3282
3227
|
r, g, b, a,
|
@@ -3303,7 +3248,7 @@ class ColorPicker {
|
|
3303
3248
|
changeControl2(Y) {
|
3304
3249
|
const self = this;
|
3305
3250
|
const {
|
3306
|
-
|
3251
|
+
controlPositions, visuals,
|
3307
3252
|
} = self;
|
3308
3253
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3309
3254
|
|
@@ -3312,26 +3257,17 @@ class ColorPicker {
|
|
3312
3257
|
if (Y > offsetHeight) offsetY = offsetHeight;
|
3313
3258
|
else if (Y >= 0) offsetY = Y;
|
3314
3259
|
|
3315
|
-
const hue =
|
3316
|
-
|
3317
|
-
: offsetY / offsetHeight;
|
3318
|
-
const saturation = format === 'hsl'
|
3319
|
-
? 1 - offsetY / offsetHeight
|
3320
|
-
: controlPositions.c1x / offsetWidth;
|
3260
|
+
const hue = offsetY / offsetHeight;
|
3261
|
+
const saturation = controlPositions.c1x / offsetWidth;
|
3321
3262
|
const lightness = 1 - controlPositions.c1y / offsetHeight;
|
3322
3263
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
3323
|
-
const colorObject = format === 'hsl'
|
3324
|
-
? {
|
3325
|
-
h: hue, s: saturation, l: lightness, a: alpha,
|
3326
|
-
}
|
3327
|
-
: {
|
3328
|
-
h: hue, s: saturation, v: lightness, a: alpha,
|
3329
|
-
};
|
3330
3264
|
|
3331
3265
|
// new color
|
3332
3266
|
const {
|
3333
3267
|
r, g, b, a,
|
3334
|
-
} = new Color(
|
3268
|
+
} = new Color({
|
3269
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
3270
|
+
});
|
3335
3271
|
|
3336
3272
|
ObjectAssign(self.color, {
|
3337
3273
|
r, g, b, a,
|
@@ -3418,18 +3354,18 @@ class ColorPicker {
|
|
3418
3354
|
setControlPositions() {
|
3419
3355
|
const self = this;
|
3420
3356
|
const {
|
3421
|
-
|
3357
|
+
visuals, color, hsv,
|
3422
3358
|
} = self;
|
3423
3359
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3424
3360
|
const alpha = color.a;
|
3425
|
-
const hue =
|
3361
|
+
const hue = hsv.h;
|
3426
3362
|
|
3427
|
-
const saturation =
|
3428
|
-
const lightness =
|
3363
|
+
const saturation = hsv.s;
|
3364
|
+
const lightness = hsv.v;
|
3429
3365
|
|
3430
|
-
self.controlPositions.c1x =
|
3366
|
+
self.controlPositions.c1x = saturation * offsetWidth;
|
3431
3367
|
self.controlPositions.c1y = (1 - lightness) * offsetHeight;
|
3432
|
-
self.controlPositions.c2y =
|
3368
|
+
self.controlPositions.c2y = hue * offsetHeight;
|
3433
3369
|
self.controlPositions.c3y = (1 - alpha) * offsetHeight;
|
3434
3370
|
}
|
3435
3371
|
|
@@ -3437,78 +3373,40 @@ class ColorPicker {
|
|
3437
3373
|
updateAppearance() {
|
3438
3374
|
const self = this;
|
3439
3375
|
const {
|
3440
|
-
componentLabels,
|
3441
|
-
|
3376
|
+
componentLabels, color, parent,
|
3377
|
+
hsv, hex, format, controlKnobs,
|
3442
3378
|
} = self;
|
3443
3379
|
const {
|
3444
3380
|
appearanceLabel, hexLabel, valueLabel,
|
3445
3381
|
} = componentLabels;
|
3446
|
-
|
3382
|
+
let { r, g, b } = color.toRgb();
|
3447
3383
|
const [knob1, knob2, knob3] = controlKnobs;
|
3448
|
-
const hue = roundPart(
|
3384
|
+
const hue = roundPart(hsv.h * 360);
|
3449
3385
|
const alpha = color.a;
|
3450
|
-
const
|
3451
|
-
const
|
3452
|
-
const
|
3453
|
-
const hsvl = hsv.v * 100;
|
3454
|
-
let colorName;
|
3455
|
-
|
3456
|
-
// determine color appearance
|
3457
|
-
if (lightness === 100 && saturation === 0) {
|
3458
|
-
colorName = colorLabels.white;
|
3459
|
-
} else if (lightness === 0) {
|
3460
|
-
colorName = colorLabels.black;
|
3461
|
-
} else if (saturation === 0) {
|
3462
|
-
colorName = colorLabels.grey;
|
3463
|
-
} else if (hue < 15 || hue >= 345) {
|
3464
|
-
colorName = colorLabels.red;
|
3465
|
-
} else if (hue >= 15 && hue < 45) {
|
3466
|
-
colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
|
3467
|
-
} else if (hue >= 45 && hue < 75) {
|
3468
|
-
const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
|
3469
|
-
const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
|
3470
|
-
colorName = isGold ? colorLabels.gold : colorLabels.yellow;
|
3471
|
-
colorName = isOlive ? colorLabels.olive : colorName;
|
3472
|
-
} else if (hue >= 75 && hue < 155) {
|
3473
|
-
colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
|
3474
|
-
} else if (hue >= 155 && hue < 175) {
|
3475
|
-
colorName = colorLabels.teal;
|
3476
|
-
} else if (hue >= 175 && hue < 195) {
|
3477
|
-
colorName = colorLabels.cyan;
|
3478
|
-
} else if (hue >= 195 && hue < 255) {
|
3479
|
-
colorName = colorLabels.blue;
|
3480
|
-
} else if (hue >= 255 && hue < 270) {
|
3481
|
-
colorName = colorLabels.violet;
|
3482
|
-
} else if (hue >= 270 && hue < 295) {
|
3483
|
-
colorName = colorLabels.magenta;
|
3484
|
-
} else if (hue >= 295 && hue < 345) {
|
3485
|
-
colorName = colorLabels.pink;
|
3486
|
-
}
|
3386
|
+
const saturation = roundPart(hsv.s * 100);
|
3387
|
+
const lightness = roundPart(hsv.v * 100);
|
3388
|
+
const colorName = self.appearance;
|
3487
3389
|
|
3488
3390
|
let colorLabel = `${hexLabel} ${hex.split('').join(' ')}`;
|
3489
3391
|
|
3490
|
-
if (format === '
|
3491
|
-
colorLabel = `HSL: ${hue}°, ${saturation}%, ${lightness}%`;
|
3492
|
-
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3493
|
-
setAttribute(knob1, ariaValueText, `${hue}° & ${lightness}%`);
|
3494
|
-
setAttribute(knob1, ariaValueNow, `${hue}`);
|
3495
|
-
setAttribute(knob2, ariaValueText, `${saturation}%`);
|
3496
|
-
setAttribute(knob2, ariaValueNow, `${saturation}`);
|
3497
|
-
} else if (format === 'hwb') {
|
3392
|
+
if (format === 'hwb') {
|
3498
3393
|
const { hwb } = self;
|
3499
3394
|
const whiteness = roundPart(hwb.w * 100);
|
3500
3395
|
const blackness = roundPart(hwb.b * 100);
|
3501
3396
|
colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
|
3502
|
-
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3503
3397
|
setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
|
3504
3398
|
setAttribute(knob1, ariaValueNow, `${whiteness}`);
|
3399
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3505
3400
|
setAttribute(knob2, ariaValueText, `${hue}%`);
|
3506
3401
|
setAttribute(knob2, ariaValueNow, `${hue}`);
|
3507
3402
|
} else {
|
3403
|
+
[r, g, b] = [r, g, b].map(roundPart);
|
3404
|
+
colorLabel = format === 'hsl' ? `HSL: ${hue}°, ${saturation}%, ${lightness}%` : colorLabel;
|
3508
3405
|
colorLabel = format === 'rgb' ? `RGB: ${r}, ${g}, ${b}` : colorLabel;
|
3509
|
-
|
3406
|
+
|
3510
3407
|
setAttribute(knob1, ariaValueText, `${lightness}% & ${saturation}%`);
|
3511
3408
|
setAttribute(knob1, ariaValueNow, `${lightness}`);
|
3409
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3512
3410
|
setAttribute(knob2, ariaValueText, `${hue}°`);
|
3513
3411
|
setAttribute(knob2, ariaValueNow, `${hue}`);
|
3514
3412
|
}
|
@@ -3603,37 +3501,13 @@ class ColorPicker {
|
|
3603
3501
|
}
|
3604
3502
|
}
|
3605
3503
|
|
3606
|
-
/**
|
3607
|
-
* The `Space` & `Enter` keys specific event listener.
|
3608
|
-
* Toggle visibility of the `ColorPicker` / the presets menu, showing one will hide the other.
|
3609
|
-
* @param {KeyboardEvent} e
|
3610
|
-
* @this {ColorPicker}
|
3611
|
-
*/
|
3612
|
-
keyToggle(e) {
|
3613
|
-
const self = this;
|
3614
|
-
const { menuToggle } = self;
|
3615
|
-
const { activeElement } = getDocument(menuToggle);
|
3616
|
-
const { code } = e;
|
3617
|
-
|
3618
|
-
if ([keyEnter, keySpace].includes(code)) {
|
3619
|
-
if ((menuToggle && activeElement === menuToggle) || !activeElement) {
|
3620
|
-
e.preventDefault();
|
3621
|
-
if (!activeElement) {
|
3622
|
-
self.togglePicker(e);
|
3623
|
-
} else {
|
3624
|
-
self.toggleMenu();
|
3625
|
-
}
|
3626
|
-
}
|
3627
|
-
}
|
3628
|
-
}
|
3629
|
-
|
3630
3504
|
/**
|
3631
3505
|
* Toggle the `ColorPicker` dropdown visibility.
|
3632
|
-
* @param {Event} e
|
3506
|
+
* @param {Event=} e
|
3633
3507
|
* @this {ColorPicker}
|
3634
3508
|
*/
|
3635
3509
|
togglePicker(e) {
|
3636
|
-
e.preventDefault();
|
3510
|
+
if (e) e.preventDefault();
|
3637
3511
|
const self = this;
|
3638
3512
|
const { colorPicker } = self;
|
3639
3513
|
|
@@ -3654,8 +3528,13 @@ class ColorPicker {
|
|
3654
3528
|
}
|
3655
3529
|
}
|
3656
3530
|
|
3657
|
-
/**
|
3658
|
-
|
3531
|
+
/**
|
3532
|
+
* Toggles the visibility of the `ColorPicker` presets menu.
|
3533
|
+
* @param {Event=} e
|
3534
|
+
* @this {ColorPicker}
|
3535
|
+
*/
|
3536
|
+
toggleMenu(e) {
|
3537
|
+
if (e) e.preventDefault();
|
3659
3538
|
const self = this;
|
3660
3539
|
const { colorMenu } = self;
|
3661
3540
|
|
@@ -3681,6 +3560,10 @@ class ColorPicker {
|
|
3681
3560
|
const relatedBtn = openPicker ? pickerToggle : menuToggle;
|
3682
3561
|
const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
|
3683
3562
|
|
3563
|
+
// if (!self.isValid) {
|
3564
|
+
self.value = self.color.toString(true);
|
3565
|
+
// }
|
3566
|
+
|
3684
3567
|
if (openDropdown) {
|
3685
3568
|
removeClass(openDropdown, 'show');
|
3686
3569
|
setAttribute(relatedBtn, ariaExpanded, 'false');
|
@@ -3694,9 +3577,6 @@ class ColorPicker {
|
|
3694
3577
|
}, animationDuration);
|
3695
3578
|
}
|
3696
3579
|
|
3697
|
-
if (!self.isValid) {
|
3698
|
-
self.value = self.color.toString();
|
3699
|
-
}
|
3700
3580
|
if (!focusPrevented) {
|
3701
3581
|
focus(pickerToggle);
|
3702
3582
|
}
|
@@ -3739,4 +3619,4 @@ ObjectAssign(ColorPicker, {
|
|
3739
3619
|
getBoundingClientRect,
|
3740
3620
|
});
|
3741
3621
|
|
3742
|
-
export default
|
3622
|
+
export { ColorPicker as default };
|