@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
@@ -1,5 +1,5 @@
|
|
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
|
*/
|
@@ -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
|
-
|
1712
|
-
|
1713
|
-
rgb = { r, g, b };
|
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));
|
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,16 +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
|
-
const [R, G, B] = [r, g, b].map((x) => roundPart(x));
|
1869
1524
|
|
1525
|
+
[r, g, b] = [r, g, b].map((n) => roundPart(n * 255 * 100) / 100);
|
1526
|
+
a = roundPart(a * 100) / 100;
|
1870
1527
|
return {
|
1871
|
-
r
|
1872
|
-
g: G,
|
1873
|
-
b: B,
|
1874
|
-
a: roundPart(a * 100) / 100,
|
1528
|
+
r, g, b, a,
|
1875
1529
|
};
|
1876
1530
|
}
|
1877
1531
|
|
@@ -1885,10 +1539,11 @@ class Color {
|
|
1885
1539
|
const {
|
1886
1540
|
r, g, b, a,
|
1887
1541
|
} = this.toRgb();
|
1542
|
+
const [R, G, B] = [r, g, b].map(roundPart);
|
1888
1543
|
|
1889
1544
|
return a === 1
|
1890
|
-
? `rgb(${
|
1891
|
-
: `rgba(${
|
1545
|
+
? `rgb(${R}, ${G}, ${B})`
|
1546
|
+
: `rgba(${R}, ${G}, ${B}, ${a})`;
|
1892
1547
|
}
|
1893
1548
|
|
1894
1549
|
/**
|
@@ -1901,9 +1556,10 @@ class Color {
|
|
1901
1556
|
const {
|
1902
1557
|
r, g, b, a,
|
1903
1558
|
} = this.toRgb();
|
1559
|
+
const [R, G, B] = [r, g, b].map(roundPart);
|
1904
1560
|
const A = a === 1 ? '' : ` / ${roundPart(a * 100)}%`;
|
1905
1561
|
|
1906
|
-
return `rgb(${
|
1562
|
+
return `rgb(${R} ${G} ${B}${A})`;
|
1907
1563
|
}
|
1908
1564
|
|
1909
1565
|
/**
|
@@ -1963,7 +1619,7 @@ class Color {
|
|
1963
1619
|
toHsv() {
|
1964
1620
|
const {
|
1965
1621
|
r, g, b, a,
|
1966
|
-
} = this
|
1622
|
+
} = this;
|
1967
1623
|
const { h, s, v } = rgbToHsv(r, g, b);
|
1968
1624
|
|
1969
1625
|
return {
|
@@ -1978,7 +1634,7 @@ class Color {
|
|
1978
1634
|
toHsl() {
|
1979
1635
|
const {
|
1980
1636
|
r, g, b, a,
|
1981
|
-
} = this
|
1637
|
+
} = this;
|
1982
1638
|
const { h, s, l } = rgbToHsl(r, g, b);
|
1983
1639
|
|
1984
1640
|
return {
|
@@ -2063,6 +1719,7 @@ class Color {
|
|
2063
1719
|
*/
|
2064
1720
|
setAlpha(alpha) {
|
2065
1721
|
const self = this;
|
1722
|
+
if (typeof alpha !== 'number') return self;
|
2066
1723
|
self.a = boundAlpha(alpha);
|
2067
1724
|
return self;
|
2068
1725
|
}
|
@@ -2177,6 +1834,7 @@ ObjectAssign(Color, {
|
|
2177
1834
|
isOnePointZero,
|
2178
1835
|
isPercentage,
|
2179
1836
|
isValidCSSUnit,
|
1837
|
+
isColorName,
|
2180
1838
|
pad2,
|
2181
1839
|
clamp01,
|
2182
1840
|
bound01,
|
@@ -2194,10 +1852,11 @@ ObjectAssign(Color, {
|
|
2194
1852
|
hueToRgb,
|
2195
1853
|
hwbToRgb,
|
2196
1854
|
parseIntFromHex,
|
2197
|
-
numberInputToObject,
|
2198
1855
|
stringInputToObject,
|
2199
1856
|
inputToRGB,
|
2200
1857
|
roundPart,
|
1858
|
+
getElementStyle,
|
1859
|
+
setElementStyle,
|
2201
1860
|
ObjectAssign,
|
2202
1861
|
});
|
2203
1862
|
|
@@ -2206,7 +1865,7 @@ ObjectAssign(Color, {
|
|
2206
1865
|
* Returns a color palette with a given set of parameters.
|
2207
1866
|
* @example
|
2208
1867
|
* new ColorPalette(0, 12, 10);
|
2209
|
-
* // => { hue: 0, hueSteps: 12, lightSteps: 10, colors:
|
1868
|
+
* // => { hue: 0, hueSteps: 12, lightSteps: 10, colors: Array<Color> }
|
2210
1869
|
*/
|
2211
1870
|
class ColorPalette {
|
2212
1871
|
/**
|
@@ -2226,48 +1885,352 @@ class ColorPalette {
|
|
2226
1885
|
[hue, hueSteps, lightSteps] = args;
|
2227
1886
|
} else if (args.length === 2) {
|
2228
1887
|
[hueSteps, lightSteps] = args;
|
2229
|
-
|
2230
|
-
|
1888
|
+
if ([hueSteps, lightSteps].some((n) => n < 1)) {
|
1889
|
+
throw TypeError('ColorPalette: both arguments must be higher than 0.');
|
1890
|
+
}
|
2231
1891
|
}
|
2232
1892
|
|
2233
|
-
/** @type {
|
1893
|
+
/** @type {*} */
|
2234
1894
|
const colors = [];
|
2235
|
-
|
2236
1895
|
const hueStep = 360 / hueSteps;
|
2237
1896
|
const half = roundPart((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
|
2238
|
-
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');
|
2239
2199
|
|
2240
|
-
|
2241
|
-
|
2242
|
-
|
2243
|
-
|
2244
|
-
|
2245
|
-
|
2246
|
-
lightStep = lightSteps > 13 ? estimatedStep : lightStep;
|
2200
|
+
control.append(
|
2201
|
+
createElement({
|
2202
|
+
tagName: 'div',
|
2203
|
+
className: `visual-control visual-control${i}`,
|
2204
|
+
}),
|
2205
|
+
);
|
2247
2206
|
|
2248
|
-
|
2249
|
-
|
2250
|
-
|
2251
|
-
|
2207
|
+
const knob = createElement({
|
2208
|
+
tagName: 'div',
|
2209
|
+
className: `${c} knob`,
|
2210
|
+
ariaLive: 'polite',
|
2211
|
+
});
|
2252
2212
|
|
2253
|
-
|
2254
|
-
|
2255
|
-
|
2256
|
-
}
|
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
|
+
});
|
2257
2221
|
|
2258
|
-
|
2259
|
-
|
2260
|
-
const currentHue = ((hue + i * hueStep) % 360) / 360;
|
2261
|
-
lightnessArray.forEach((l) => {
|
2262
|
-
colors.push(new Color({ h: currentHue, s: 1, l }).toHexString());
|
2263
|
-
});
|
2264
|
-
}
|
2222
|
+
return colorControls;
|
2223
|
+
}
|
2265
2224
|
|
2266
|
-
|
2267
|
-
|
2268
|
-
|
2269
|
-
|
2270
|
-
|
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
|
+
});
|
2271
2234
|
}
|
2272
2235
|
|
2273
2236
|
/**
|
@@ -2303,7 +2266,8 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2303
2266
|
optionSize = fit > 5 && isMultiLine ? 1.5 : optionSize;
|
2304
2267
|
const menuHeight = `${(rowCount || 1) * optionSize}rem`;
|
2305
2268
|
const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
|
2306
|
-
|
2269
|
+
/** @type {HTMLUListElement} */
|
2270
|
+
// @ts-ignore -- <UL> is an `HTMLElement`
|
2307
2271
|
const menu = createElement({
|
2308
2272
|
tagName: 'ul',
|
2309
2273
|
className: finalClass,
|
@@ -2311,7 +2275,7 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2311
2275
|
setAttribute(menu, 'role', 'listbox');
|
2312
2276
|
setAttribute(menu, ariaLabel, menuLabel);
|
2313
2277
|
|
2314
|
-
if (isScrollable) {
|
2278
|
+
if (isScrollable) {
|
2315
2279
|
setCSSProperties(menu, {
|
2316
2280
|
'--grid-item-size': `${optionSize}rem`,
|
2317
2281
|
'--grid-fit': fit,
|
@@ -2322,15 +2286,19 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2322
2286
|
}
|
2323
2287
|
|
2324
2288
|
colorsArray.forEach((x) => {
|
2325
|
-
|
2326
|
-
|
2327
|
-
|
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');
|
2328
2296
|
const active = isActive ? ' active' : '';
|
2329
2297
|
|
2330
2298
|
const option = createElement({
|
2331
2299
|
tagName: 'li',
|
2332
2300
|
className: `color-option${active}`,
|
2333
|
-
innerText: `${label ||
|
2301
|
+
innerText: `${label || value}`,
|
2334
2302
|
});
|
2335
2303
|
|
2336
2304
|
setAttribute(option, tabIndex, '0');
|
@@ -2339,7 +2307,7 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2339
2307
|
setAttribute(option, ariaSelected, isActive ? 'true' : 'false');
|
2340
2308
|
|
2341
2309
|
if (isOptionsMenu) {
|
2342
|
-
setElementStyle(option, { backgroundColor:
|
2310
|
+
setElementStyle(option, { backgroundColor: value });
|
2343
2311
|
}
|
2344
2312
|
|
2345
2313
|
menu.append(option);
|
@@ -2348,55 +2316,10 @@ function getColorMenu(self, colorsSource, menuClass) {
|
|
2348
2316
|
}
|
2349
2317
|
|
2350
2318
|
/**
|
2351
|
-
|
2352
|
-
|
2353
|
-
|
2354
|
-
|
2355
|
-
function isValidJSON(str) {
|
2356
|
-
try {
|
2357
|
-
JSON.parse(str);
|
2358
|
-
} catch (e) {
|
2359
|
-
return false;
|
2360
|
-
}
|
2361
|
-
return true;
|
2362
|
-
}
|
2363
|
-
|
2364
|
-
var version = "0.0.1alpha3";
|
2365
|
-
|
2366
|
-
// @ts-ignore
|
2367
|
-
|
2368
|
-
const Version = version;
|
2369
|
-
|
2370
|
-
// ColorPicker GC
|
2371
|
-
// ==============
|
2372
|
-
const colorPickerString = 'color-picker';
|
2373
|
-
const colorPickerSelector = `[data-function="${colorPickerString}"]`;
|
2374
|
-
const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
|
2375
|
-
const colorPickerDefaults = {
|
2376
|
-
componentLabels: colorPickerLabels,
|
2377
|
-
colorLabels: colorNames,
|
2378
|
-
format: 'rgb',
|
2379
|
-
colorPresets: false,
|
2380
|
-
colorKeywords: false,
|
2381
|
-
};
|
2382
|
-
|
2383
|
-
// ColorPicker Static Methods
|
2384
|
-
// ==========================
|
2385
|
-
|
2386
|
-
/** @type {CP.GetInstance<ColorPicker>} */
|
2387
|
-
const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
|
2388
|
-
|
2389
|
-
/** @type {CP.InitCallback<ColorPicker>} */
|
2390
|
-
const initColorPicker = (element) => new ColorPicker(element);
|
2391
|
-
|
2392
|
-
// ColorPicker Private Methods
|
2393
|
-
// ===========================
|
2394
|
-
|
2395
|
-
/**
|
2396
|
-
* Generate HTML markup and update instance properties.
|
2397
|
-
* @param {ColorPicker} self
|
2398
|
-
*/
|
2399
|
-
function initCallback(self) {
|
2319
|
+
* Generate HTML markup and update instance properties.
|
2320
|
+
* @param {CP.ColorPicker} self
|
2321
|
+
*/
|
2322
|
+
function setMarkup(self) {
|
2400
2323
|
const {
|
2401
2324
|
input, parent, format, id, componentLabels, colorKeywords, colorPresets,
|
2402
2325
|
} = self;
|
@@ -2411,9 +2334,7 @@ function initCallback(self) {
|
|
2411
2334
|
self.color = new Color(color, format);
|
2412
2335
|
|
2413
2336
|
// set initial controls dimensions
|
2414
|
-
|
2415
|
-
const dropClass = isMobile ? ' mobile' : '';
|
2416
|
-
const formatString = format === 'hex' ? hexLabel : format.toUpperCase();
|
2337
|
+
const formatString = format === 'hex' ? hexLabel : toUpperCase(format);
|
2417
2338
|
|
2418
2339
|
const pickerBtn = createElement({
|
2419
2340
|
id: `picker-btn-${id}`,
|
@@ -2430,7 +2351,7 @@ function initCallback(self) {
|
|
2430
2351
|
|
2431
2352
|
const pickerDropdown = createElement({
|
2432
2353
|
tagName: 'div',
|
2433
|
-
className:
|
2354
|
+
className: 'color-dropdown picker',
|
2434
2355
|
});
|
2435
2356
|
setAttribute(pickerDropdown, ariaLabelledBy, `picker-btn-${id}`);
|
2436
2357
|
setAttribute(pickerDropdown, 'role', 'group');
|
@@ -2446,7 +2367,7 @@ function initCallback(self) {
|
|
2446
2367
|
if (colorKeywords || colorPresets) {
|
2447
2368
|
const presetsDropdown = createElement({
|
2448
2369
|
tagName: 'div',
|
2449
|
-
className:
|
2370
|
+
className: 'color-dropdown scrollable menu',
|
2450
2371
|
});
|
2451
2372
|
|
2452
2373
|
// color presets
|
@@ -2496,6 +2417,37 @@ function initCallback(self) {
|
|
2496
2417
|
setAttribute(input, tabIndex, '-1');
|
2497
2418
|
}
|
2498
2419
|
|
2420
|
+
var version = "0.0.2alpha2";
|
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
|
+
|
2499
2451
|
/**
|
2500
2452
|
* Add / remove `ColorPicker` main event listeners.
|
2501
2453
|
* @param {ColorPicker} self
|
@@ -2508,8 +2460,6 @@ function toggleEvents(self, action) {
|
|
2508
2460
|
fn(input, focusinEvent, self.showPicker);
|
2509
2461
|
fn(pickerToggle, mouseclickEvent, self.togglePicker);
|
2510
2462
|
|
2511
|
-
fn(input, keydownEvent, self.keyToggle);
|
2512
|
-
|
2513
2463
|
if (menuToggle) {
|
2514
2464
|
fn(menuToggle, mouseclickEvent, self.toggleMenu);
|
2515
2465
|
}
|
@@ -2547,8 +2497,7 @@ function toggleEventsOnShown(self, action) {
|
|
2547
2497
|
fn(doc, pointerEvents.move, self.pointerMove);
|
2548
2498
|
fn(doc, pointerEvents.up, self.pointerUp);
|
2549
2499
|
fn(parent, focusoutEvent, self.handleFocusOut);
|
2550
|
-
|
2551
|
-
fn(win, keyupEvent, self.handleDismiss);
|
2500
|
+
fn(doc, keyupEvent, self.handleDismiss);
|
2552
2501
|
}
|
2553
2502
|
|
2554
2503
|
/**
|
@@ -2632,7 +2581,7 @@ class ColorPicker {
|
|
2632
2581
|
const input = querySelector(target);
|
2633
2582
|
|
2634
2583
|
// invalidate
|
2635
|
-
if (!input) throw new TypeError(`ColorPicker target ${target} cannot be found.`);
|
2584
|
+
if (!input) throw new TypeError(`ColorPicker target "${target}" cannot be found.`);
|
2636
2585
|
self.input = input;
|
2637
2586
|
|
2638
2587
|
const parent = closest(input, colorPickerParentSelector);
|
@@ -2679,15 +2628,14 @@ class ColorPicker {
|
|
2679
2628
|
});
|
2680
2629
|
|
2681
2630
|
// update and expose component labels
|
2682
|
-
const
|
2683
|
-
|
2684
|
-
? JSON.parse(componentLabels) : componentLabels || {};
|
2631
|
+
const tempComponentLabels = componentLabels && isValidJSON(componentLabels)
|
2632
|
+
? JSON.parse(componentLabels) : componentLabels;
|
2685
2633
|
|
2686
2634
|
/** @type {Record<string, string>} */
|
2687
|
-
self.componentLabels = ObjectAssign(
|
2635
|
+
self.componentLabels = ObjectAssign(colorPickerLabels, tempComponentLabels);
|
2688
2636
|
|
2689
2637
|
/** @type {Color} */
|
2690
|
-
self.color = new Color('
|
2638
|
+
self.color = new Color(input.value || '#fff', format);
|
2691
2639
|
|
2692
2640
|
/** @type {CP.ColorFormats} */
|
2693
2641
|
self.format = format;
|
@@ -2696,7 +2644,7 @@ class ColorPicker {
|
|
2696
2644
|
if (colorKeywords instanceof Array) {
|
2697
2645
|
self.colorKeywords = colorKeywords;
|
2698
2646
|
} else if (typeof colorKeywords === 'string' && colorKeywords.length) {
|
2699
|
-
self.colorKeywords = colorKeywords.split(',');
|
2647
|
+
self.colorKeywords = colorKeywords.split(',').map((x) => x.trim());
|
2700
2648
|
}
|
2701
2649
|
|
2702
2650
|
// set colour presets
|
@@ -2725,11 +2673,10 @@ class ColorPicker {
|
|
2725
2673
|
self.handleFocusOut = self.handleFocusOut.bind(self);
|
2726
2674
|
self.changeHandler = self.changeHandler.bind(self);
|
2727
2675
|
self.handleDismiss = self.handleDismiss.bind(self);
|
2728
|
-
self.keyToggle = self.keyToggle.bind(self);
|
2729
2676
|
self.handleKnobs = self.handleKnobs.bind(self);
|
2730
2677
|
|
2731
2678
|
// generate markup
|
2732
|
-
|
2679
|
+
setMarkup(self);
|
2733
2680
|
|
2734
2681
|
const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
|
2735
2682
|
// set main elements
|
@@ -2817,76 +2764,83 @@ class ColorPicker {
|
|
2817
2764
|
return inputValue !== '' && new Color(inputValue).isValid;
|
2818
2765
|
}
|
2819
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
|
+
|
2820
2815
|
/** Updates `ColorPicker` visuals. */
|
2821
2816
|
updateVisuals() {
|
2822
2817
|
const self = this;
|
2823
2818
|
const {
|
2824
|
-
|
2819
|
+
controlPositions, visuals,
|
2825
2820
|
} = self;
|
2826
2821
|
const [v1, v2, v3] = visuals;
|
2827
|
-
const {
|
2828
|
-
const hue =
|
2829
|
-
|
2830
|
-
: controlPositions.c2y / offsetHeight;
|
2831
|
-
// @ts-ignore - `hslToRgb` is assigned to `Color` as static method
|
2832
|
-
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();
|
2833
2825
|
const whiteGrad = 'linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)';
|
2834
2826
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
2835
2827
|
const roundA = roundPart((alpha * 100)) / 100;
|
2836
2828
|
|
2837
|
-
|
2838
|
-
|
2839
|
-
|
2840
|
-
|
2841
|
-
|
2842
|
-
|
2843
|
-
|
2844
|
-
|
2845
|
-
|
2846
|
-
|
2847
|
-
|
2848
|
-
|
2849
|
-
|
2850
|
-
|
2851
|
-
setElementStyle(v2, { background: hueGradient });
|
2852
|
-
} else {
|
2853
|
-
const saturation = roundPart((controlPositions.c2y / offsetHeight) * 100);
|
2854
|
-
const fill0 = new Color({
|
2855
|
-
r: 255, g: 0, b: 0, a: alpha,
|
2856
|
-
}).saturate(-saturation).toRgbString();
|
2857
|
-
const fill1 = new Color({
|
2858
|
-
r: 255, g: 255, b: 0, a: alpha,
|
2859
|
-
}).saturate(-saturation).toRgbString();
|
2860
|
-
const fill2 = new Color({
|
2861
|
-
r: 0, g: 255, b: 0, a: alpha,
|
2862
|
-
}).saturate(-saturation).toRgbString();
|
2863
|
-
const fill3 = new Color({
|
2864
|
-
r: 0, g: 255, b: 255, a: alpha,
|
2865
|
-
}).saturate(-saturation).toRgbString();
|
2866
|
-
const fill4 = new Color({
|
2867
|
-
r: 0, g: 0, b: 255, a: alpha,
|
2868
|
-
}).saturate(-saturation).toRgbString();
|
2869
|
-
const fill5 = new Color({
|
2870
|
-
r: 255, g: 0, b: 255, a: alpha,
|
2871
|
-
}).saturate(-saturation).toRgbString();
|
2872
|
-
const fill6 = new Color({
|
2873
|
-
r: 255, g: 0, b: 0, a: alpha,
|
2874
|
-
}).saturate(-saturation).toRgbString();
|
2875
|
-
const fillGradient = `linear-gradient(to right,
|
2876
|
-
${fill0} 0%, ${fill1} 16.67%, ${fill2} 33.33%, ${fill3} 50%,
|
2877
|
-
${fill4} 66.67%, ${fill5} 83.33%, ${fill6} 100%)`;
|
2878
|
-
const lightGrad = `linear-gradient(rgba(255,255,255,${roundA}) 0%, rgba(255,255,255,0) 50%),
|
2879
|
-
linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,${roundA}) 100%)`;
|
2880
|
-
|
2881
|
-
setElementStyle(v1, { background: `${lightGrad},${fillGradient},${whiteGrad}` });
|
2882
|
-
const {
|
2883
|
-
r: gr, g: gg, b: gb,
|
2884
|
-
} = 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 });
|
2885
2843
|
|
2886
|
-
setElementStyle(v2, {
|
2887
|
-
background: `linear-gradient(rgb(${r},${g},${b}) 0%, rgb(${gr},${gg},${gb}) 100%)`,
|
2888
|
-
});
|
2889
|
-
}
|
2890
2844
|
setElementStyle(v3, {
|
2891
2845
|
background: `linear-gradient(rgba(${r},${g},${b},1) 0%,rgba(${r},${g},${b},0) 100%)`,
|
2892
2846
|
});
|
@@ -2925,7 +2879,7 @@ class ColorPicker {
|
|
2925
2879
|
const self = this;
|
2926
2880
|
const { activeElement } = getDocument(self.input);
|
2927
2881
|
|
2928
|
-
if ((
|
2882
|
+
if ((e.type === touchmoveEvent && self.dragElement)
|
2929
2883
|
|| (activeElement && self.controlKnobs.includes(activeElement))) {
|
2930
2884
|
e.stopPropagation();
|
2931
2885
|
e.preventDefault();
|
@@ -3036,13 +2990,13 @@ class ColorPicker {
|
|
3036
2990
|
const [v1, v2, v3] = visuals;
|
3037
2991
|
const [c1, c2, c3] = controlKnobs;
|
3038
2992
|
/** @type {HTMLElement} */
|
3039
|
-
const visual =
|
3040
|
-
? target : querySelector('.visual-control', target.parentElement);
|
2993
|
+
const visual = controlKnobs.includes(target) ? target.previousElementSibling : target;
|
3041
2994
|
const visualRect = getBoundingClientRect(visual);
|
2995
|
+
const html = getDocumentElement(v1);
|
3042
2996
|
const X = type === 'touchstart' ? touches[0].pageX : pageX;
|
3043
2997
|
const Y = type === 'touchstart' ? touches[0].pageY : pageY;
|
3044
|
-
const offsetX = X -
|
3045
|
-
const offsetY = Y -
|
2998
|
+
const offsetX = X - html.scrollLeft - visualRect.left;
|
2999
|
+
const offsetY = Y - html.scrollTop - visualRect.top;
|
3046
3000
|
|
3047
3001
|
if (target === v1 || target === c1) {
|
3048
3002
|
self.dragElement = visual;
|
@@ -3102,10 +3056,11 @@ class ColorPicker {
|
|
3102
3056
|
if (!dragElement) return;
|
3103
3057
|
|
3104
3058
|
const controlRect = getBoundingClientRect(dragElement);
|
3105
|
-
const
|
3106
|
-
const
|
3107
|
-
const
|
3108
|
-
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;
|
3109
3064
|
|
3110
3065
|
if (dragElement === v1) {
|
3111
3066
|
self.changeControl1(offsetX, offsetY);
|
@@ -3132,30 +3087,41 @@ class ColorPicker {
|
|
3132
3087
|
if (![keyArrowUp, keyArrowDown, keyArrowLeft, keyArrowRight].includes(code)) return;
|
3133
3088
|
e.preventDefault();
|
3134
3089
|
|
3135
|
-
const { controlKnobs } = self;
|
3090
|
+
const { controlKnobs, visuals } = self;
|
3091
|
+
const { offsetWidth, offsetHeight } = visuals[0];
|
3136
3092
|
const [c1, c2, c3] = controlKnobs;
|
3137
3093
|
const { activeElement } = getDocument(c1);
|
3138
3094
|
const currentKnob = controlKnobs.find((x) => x === activeElement);
|
3095
|
+
const yRatio = offsetHeight / 360;
|
3139
3096
|
|
3140
3097
|
if (currentKnob) {
|
3141
3098
|
let offsetX = 0;
|
3142
3099
|
let offsetY = 0;
|
3100
|
+
|
3143
3101
|
if (target === c1) {
|
3102
|
+
const xRatio = offsetWidth / 100;
|
3103
|
+
|
3144
3104
|
if ([keyArrowLeft, keyArrowRight].includes(code)) {
|
3145
|
-
self.controlPositions.c1x += code === keyArrowRight ?
|
3105
|
+
self.controlPositions.c1x += code === keyArrowRight ? xRatio : -xRatio;
|
3146
3106
|
} else if ([keyArrowUp, keyArrowDown].includes(code)) {
|
3147
|
-
self.controlPositions.c1y += code === keyArrowDown ?
|
3107
|
+
self.controlPositions.c1y += code === keyArrowDown ? yRatio : -yRatio;
|
3148
3108
|
}
|
3149
3109
|
|
3150
3110
|
offsetX = self.controlPositions.c1x;
|
3151
3111
|
offsetY = self.controlPositions.c1y;
|
3152
3112
|
self.changeControl1(offsetX, offsetY);
|
3153
3113
|
} else if (target === c2) {
|
3154
|
-
self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
|
3114
|
+
self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code)
|
3115
|
+
? yRatio
|
3116
|
+
: -yRatio;
|
3117
|
+
|
3155
3118
|
offsetY = self.controlPositions.c2y;
|
3156
3119
|
self.changeControl2(offsetY);
|
3157
3120
|
} else if (target === c3) {
|
3158
|
-
self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
|
3121
|
+
self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code)
|
3122
|
+
? yRatio
|
3123
|
+
: -yRatio;
|
3124
|
+
|
3159
3125
|
offsetY = self.controlPositions.c3y;
|
3160
3126
|
self.changeAlpha(offsetY);
|
3161
3127
|
}
|
@@ -3183,7 +3149,7 @@ class ColorPicker {
|
|
3183
3149
|
if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
|
3184
3150
|
if (activeElement === input) {
|
3185
3151
|
if (isNonColorValue) {
|
3186
|
-
colorSource = '
|
3152
|
+
colorSource = currentValue === 'transparent' ? 'rgba(0,0,0,0)' : 'rgb(0,0,0)';
|
3187
3153
|
} else {
|
3188
3154
|
colorSource = currentValue;
|
3189
3155
|
}
|
@@ -3234,9 +3200,7 @@ class ColorPicker {
|
|
3234
3200
|
changeControl1(X, Y) {
|
3235
3201
|
const self = this;
|
3236
3202
|
let [offsetX, offsetY] = [0, 0];
|
3237
|
-
const {
|
3238
|
-
format, controlPositions, visuals,
|
3239
|
-
} = self;
|
3203
|
+
const { controlPositions, visuals } = self;
|
3240
3204
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3241
3205
|
|
3242
3206
|
if (X > offsetWidth) offsetX = offsetWidth;
|
@@ -3245,29 +3209,19 @@ class ColorPicker {
|
|
3245
3209
|
if (Y > offsetHeight) offsetY = offsetHeight;
|
3246
3210
|
else if (Y >= 0) offsetY = Y;
|
3247
3211
|
|
3248
|
-
const hue =
|
3249
|
-
? offsetX / offsetWidth
|
3250
|
-
: controlPositions.c2y / offsetHeight;
|
3212
|
+
const hue = controlPositions.c2y / offsetHeight;
|
3251
3213
|
|
3252
|
-
const saturation =
|
3253
|
-
? 1 - controlPositions.c2y / offsetHeight
|
3254
|
-
: offsetX / offsetWidth;
|
3214
|
+
const saturation = offsetX / offsetWidth;
|
3255
3215
|
|
3256
3216
|
const lightness = 1 - offsetY / offsetHeight;
|
3257
3217
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
3258
3218
|
|
3259
|
-
const colorObject = format === 'hsl'
|
3260
|
-
? {
|
3261
|
-
h: hue, s: saturation, l: lightness, a: alpha,
|
3262
|
-
}
|
3263
|
-
: {
|
3264
|
-
h: hue, s: saturation, v: lightness, a: alpha,
|
3265
|
-
};
|
3266
|
-
|
3267
3219
|
// new color
|
3268
3220
|
const {
|
3269
3221
|
r, g, b, a,
|
3270
|
-
} = new Color(
|
3222
|
+
} = new Color({
|
3223
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
3224
|
+
});
|
3271
3225
|
|
3272
3226
|
ObjectAssign(self.color, {
|
3273
3227
|
r, g, b, a,
|
@@ -3294,7 +3248,7 @@ class ColorPicker {
|
|
3294
3248
|
changeControl2(Y) {
|
3295
3249
|
const self = this;
|
3296
3250
|
const {
|
3297
|
-
|
3251
|
+
controlPositions, visuals,
|
3298
3252
|
} = self;
|
3299
3253
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3300
3254
|
|
@@ -3303,26 +3257,17 @@ class ColorPicker {
|
|
3303
3257
|
if (Y > offsetHeight) offsetY = offsetHeight;
|
3304
3258
|
else if (Y >= 0) offsetY = Y;
|
3305
3259
|
|
3306
|
-
const hue =
|
3307
|
-
|
3308
|
-
: offsetY / offsetHeight;
|
3309
|
-
const saturation = format === 'hsl'
|
3310
|
-
? 1 - offsetY / offsetHeight
|
3311
|
-
: controlPositions.c1x / offsetWidth;
|
3260
|
+
const hue = offsetY / offsetHeight;
|
3261
|
+
const saturation = controlPositions.c1x / offsetWidth;
|
3312
3262
|
const lightness = 1 - controlPositions.c1y / offsetHeight;
|
3313
3263
|
const alpha = 1 - controlPositions.c3y / offsetHeight;
|
3314
|
-
const colorObject = format === 'hsl'
|
3315
|
-
? {
|
3316
|
-
h: hue, s: saturation, l: lightness, a: alpha,
|
3317
|
-
}
|
3318
|
-
: {
|
3319
|
-
h: hue, s: saturation, v: lightness, a: alpha,
|
3320
|
-
};
|
3321
3264
|
|
3322
3265
|
// new color
|
3323
3266
|
const {
|
3324
3267
|
r, g, b, a,
|
3325
|
-
} = new Color(
|
3268
|
+
} = new Color({
|
3269
|
+
h: hue, s: saturation, v: lightness, a: alpha,
|
3270
|
+
});
|
3326
3271
|
|
3327
3272
|
ObjectAssign(self.color, {
|
3328
3273
|
r, g, b, a,
|
@@ -3409,18 +3354,18 @@ class ColorPicker {
|
|
3409
3354
|
setControlPositions() {
|
3410
3355
|
const self = this;
|
3411
3356
|
const {
|
3412
|
-
|
3357
|
+
visuals, color, hsv,
|
3413
3358
|
} = self;
|
3414
3359
|
const { offsetHeight, offsetWidth } = visuals[0];
|
3415
3360
|
const alpha = color.a;
|
3416
|
-
const hue =
|
3361
|
+
const hue = hsv.h;
|
3417
3362
|
|
3418
|
-
const saturation =
|
3419
|
-
const lightness =
|
3363
|
+
const saturation = hsv.s;
|
3364
|
+
const lightness = hsv.v;
|
3420
3365
|
|
3421
|
-
self.controlPositions.c1x =
|
3366
|
+
self.controlPositions.c1x = saturation * offsetWidth;
|
3422
3367
|
self.controlPositions.c1y = (1 - lightness) * offsetHeight;
|
3423
|
-
self.controlPositions.c2y =
|
3368
|
+
self.controlPositions.c2y = hue * offsetHeight;
|
3424
3369
|
self.controlPositions.c3y = (1 - alpha) * offsetHeight;
|
3425
3370
|
}
|
3426
3371
|
|
@@ -3428,78 +3373,40 @@ class ColorPicker {
|
|
3428
3373
|
updateAppearance() {
|
3429
3374
|
const self = this;
|
3430
3375
|
const {
|
3431
|
-
componentLabels,
|
3432
|
-
|
3376
|
+
componentLabels, color, parent,
|
3377
|
+
hsv, hex, format, controlKnobs,
|
3433
3378
|
} = self;
|
3434
3379
|
const {
|
3435
3380
|
appearanceLabel, hexLabel, valueLabel,
|
3436
3381
|
} = componentLabels;
|
3437
|
-
|
3382
|
+
let { r, g, b } = color.toRgb();
|
3438
3383
|
const [knob1, knob2, knob3] = controlKnobs;
|
3439
|
-
const hue = roundPart(
|
3384
|
+
const hue = roundPart(hsv.h * 360);
|
3440
3385
|
const alpha = color.a;
|
3441
|
-
const
|
3442
|
-
const
|
3443
|
-
const
|
3444
|
-
const hsvl = hsv.v * 100;
|
3445
|
-
let colorName;
|
3446
|
-
|
3447
|
-
// determine color appearance
|
3448
|
-
if (lightness === 100 && saturation === 0) {
|
3449
|
-
colorName = colorLabels.white;
|
3450
|
-
} else if (lightness === 0) {
|
3451
|
-
colorName = colorLabels.black;
|
3452
|
-
} else if (saturation === 0) {
|
3453
|
-
colorName = colorLabels.grey;
|
3454
|
-
} else if (hue < 15 || hue >= 345) {
|
3455
|
-
colorName = colorLabels.red;
|
3456
|
-
} else if (hue >= 15 && hue < 45) {
|
3457
|
-
colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
|
3458
|
-
} else if (hue >= 45 && hue < 75) {
|
3459
|
-
const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
|
3460
|
-
const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
|
3461
|
-
colorName = isGold ? colorLabels.gold : colorLabels.yellow;
|
3462
|
-
colorName = isOlive ? colorLabels.olive : colorName;
|
3463
|
-
} else if (hue >= 75 && hue < 155) {
|
3464
|
-
colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
|
3465
|
-
} else if (hue >= 155 && hue < 175) {
|
3466
|
-
colorName = colorLabels.teal;
|
3467
|
-
} else if (hue >= 175 && hue < 195) {
|
3468
|
-
colorName = colorLabels.cyan;
|
3469
|
-
} else if (hue >= 195 && hue < 255) {
|
3470
|
-
colorName = colorLabels.blue;
|
3471
|
-
} else if (hue >= 255 && hue < 270) {
|
3472
|
-
colorName = colorLabels.violet;
|
3473
|
-
} else if (hue >= 270 && hue < 295) {
|
3474
|
-
colorName = colorLabels.magenta;
|
3475
|
-
} else if (hue >= 295 && hue < 345) {
|
3476
|
-
colorName = colorLabels.pink;
|
3477
|
-
}
|
3386
|
+
const saturation = roundPart(hsv.s * 100);
|
3387
|
+
const lightness = roundPart(hsv.v * 100);
|
3388
|
+
const colorName = self.appearance;
|
3478
3389
|
|
3479
3390
|
let colorLabel = `${hexLabel} ${hex.split('').join(' ')}`;
|
3480
3391
|
|
3481
|
-
if (format === '
|
3482
|
-
colorLabel = `HSL: ${hue}°, ${saturation}%, ${lightness}%`;
|
3483
|
-
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3484
|
-
setAttribute(knob1, ariaValueText, `${hue}° & ${lightness}%`);
|
3485
|
-
setAttribute(knob1, ariaValueNow, `${hue}`);
|
3486
|
-
setAttribute(knob2, ariaValueText, `${saturation}%`);
|
3487
|
-
setAttribute(knob2, ariaValueNow, `${saturation}`);
|
3488
|
-
} else if (format === 'hwb') {
|
3392
|
+
if (format === 'hwb') {
|
3489
3393
|
const { hwb } = self;
|
3490
3394
|
const whiteness = roundPart(hwb.w * 100);
|
3491
3395
|
const blackness = roundPart(hwb.b * 100);
|
3492
3396
|
colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
|
3493
|
-
setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3494
3397
|
setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
|
3495
3398
|
setAttribute(knob1, ariaValueNow, `${whiteness}`);
|
3399
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3496
3400
|
setAttribute(knob2, ariaValueText, `${hue}%`);
|
3497
3401
|
setAttribute(knob2, ariaValueNow, `${hue}`);
|
3498
3402
|
} else {
|
3403
|
+
[r, g, b] = [r, g, b].map(roundPart);
|
3404
|
+
colorLabel = format === 'hsl' ? `HSL: ${hue}°, ${saturation}%, ${lightness}%` : colorLabel;
|
3499
3405
|
colorLabel = format === 'rgb' ? `RGB: ${r}, ${g}, ${b}` : colorLabel;
|
3500
|
-
|
3406
|
+
|
3501
3407
|
setAttribute(knob1, ariaValueText, `${lightness}% & ${saturation}%`);
|
3502
3408
|
setAttribute(knob1, ariaValueNow, `${lightness}`);
|
3409
|
+
setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
|
3503
3410
|
setAttribute(knob2, ariaValueText, `${hue}°`);
|
3504
3411
|
setAttribute(knob2, ariaValueNow, `${hue}`);
|
3505
3412
|
}
|
@@ -3527,10 +3434,12 @@ class ColorPicker {
|
|
3527
3434
|
/** Updates the control knobs actual positions. */
|
3528
3435
|
updateControls() {
|
3529
3436
|
const { controlKnobs, controlPositions } = this;
|
3530
|
-
|
3437
|
+
let {
|
3531
3438
|
c1x, c1y, c2y, c3y,
|
3532
3439
|
} = controlPositions;
|
3533
3440
|
const [control1, control2, control3] = controlKnobs;
|
3441
|
+
// round control positions
|
3442
|
+
[c1x, c1y, c2y, c3y] = [c1x, c1y, c2y, c3y].map(roundPart);
|
3534
3443
|
|
3535
3444
|
setElementStyle(control1, { transform: `translate3d(${c1x - 4}px,${c1y - 4}px,0)` });
|
3536
3445
|
setElementStyle(control2, { transform: `translate3d(0,${c2y - 4}px,0)` });
|
@@ -3573,7 +3482,8 @@ class ColorPicker {
|
|
3573
3482
|
i3.value = `${blackness}`;
|
3574
3483
|
i4.value = `${alpha}`;
|
3575
3484
|
} else if (format === 'rgb') {
|
3576
|
-
|
3485
|
+
let { r, g, b } = self.rgb;
|
3486
|
+
[r, g, b] = [r, g, b].map(roundPart);
|
3577
3487
|
|
3578
3488
|
newColor = self.color.toRgbString();
|
3579
3489
|
i1.value = `${r}`;
|
@@ -3591,37 +3501,13 @@ class ColorPicker {
|
|
3591
3501
|
}
|
3592
3502
|
}
|
3593
3503
|
|
3594
|
-
/**
|
3595
|
-
* The `Space` & `Enter` keys specific event listener.
|
3596
|
-
* Toggle visibility of the `ColorPicker` / the presets menu, showing one will hide the other.
|
3597
|
-
* @param {KeyboardEvent} e
|
3598
|
-
* @this {ColorPicker}
|
3599
|
-
*/
|
3600
|
-
keyToggle(e) {
|
3601
|
-
const self = this;
|
3602
|
-
const { menuToggle } = self;
|
3603
|
-
const { activeElement } = getDocument(menuToggle);
|
3604
|
-
const { code } = e;
|
3605
|
-
|
3606
|
-
if ([keyEnter, keySpace].includes(code)) {
|
3607
|
-
if ((menuToggle && activeElement === menuToggle) || !activeElement) {
|
3608
|
-
e.preventDefault();
|
3609
|
-
if (!activeElement) {
|
3610
|
-
self.togglePicker(e);
|
3611
|
-
} else {
|
3612
|
-
self.toggleMenu();
|
3613
|
-
}
|
3614
|
-
}
|
3615
|
-
}
|
3616
|
-
}
|
3617
|
-
|
3618
3504
|
/**
|
3619
3505
|
* Toggle the `ColorPicker` dropdown visibility.
|
3620
|
-
* @param {Event} e
|
3506
|
+
* @param {Event=} e
|
3621
3507
|
* @this {ColorPicker}
|
3622
3508
|
*/
|
3623
3509
|
togglePicker(e) {
|
3624
|
-
e.preventDefault();
|
3510
|
+
if (e) e.preventDefault();
|
3625
3511
|
const self = this;
|
3626
3512
|
const { colorPicker } = self;
|
3627
3513
|
|
@@ -3642,8 +3528,13 @@ class ColorPicker {
|
|
3642
3528
|
}
|
3643
3529
|
}
|
3644
3530
|
|
3645
|
-
/**
|
3646
|
-
|
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();
|
3647
3538
|
const self = this;
|
3648
3539
|
const { colorMenu } = self;
|
3649
3540
|
|
@@ -3669,6 +3560,10 @@ class ColorPicker {
|
|
3669
3560
|
const relatedBtn = openPicker ? pickerToggle : menuToggle;
|
3670
3561
|
const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
|
3671
3562
|
|
3563
|
+
// if (!self.isValid) {
|
3564
|
+
self.value = self.color.toString(true);
|
3565
|
+
// }
|
3566
|
+
|
3672
3567
|
if (openDropdown) {
|
3673
3568
|
removeClass(openDropdown, 'show');
|
3674
3569
|
setAttribute(relatedBtn, ariaExpanded, 'false');
|
@@ -3682,9 +3577,6 @@ class ColorPicker {
|
|
3682
3577
|
}, animationDuration);
|
3683
3578
|
}
|
3684
3579
|
|
3685
|
-
if (!self.isValid) {
|
3686
|
-
self.value = self.color.toString();
|
3687
|
-
}
|
3688
3580
|
if (!focusPrevented) {
|
3689
3581
|
focus(pickerToggle);
|
3690
3582
|
}
|
@@ -3727,4 +3619,4 @@ ObjectAssign(ColorPicker, {
|
|
3727
3619
|
getBoundingClientRect,
|
3728
3620
|
});
|
3729
3621
|
|
3730
|
-
export default
|
3622
|
+
export { ColorPicker as default };
|