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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +40 -19
  3. package/dist/css/color-picker.css +481 -337
  4. package/dist/css/color-picker.min.css +2 -0
  5. package/dist/css/color-picker.rtl.css +506 -0
  6. package/dist/css/color-picker.rtl.min.css +2 -0
  7. package/dist/js/color-picker-element-esm.js +3810 -2
  8. package/dist/js/color-picker-element-esm.min.js +2 -0
  9. package/dist/js/color-picker-element.js +2009 -1242
  10. package/dist/js/color-picker-element.min.js +2 -2
  11. package/dist/js/color-picker-esm.js +3704 -0
  12. package/dist/js/color-picker-esm.min.js +2 -0
  13. package/dist/js/color-picker.js +1962 -1256
  14. package/dist/js/color-picker.min.js +2 -2
  15. package/package.json +18 -9
  16. package/src/js/color-palette.js +62 -0
  17. package/src/js/color-picker-element.js +55 -13
  18. package/src/js/color-picker.js +686 -595
  19. package/src/js/color.js +615 -349
  20. package/src/js/index.js +0 -9
  21. package/src/js/util/colorNames.js +2 -152
  22. package/src/js/util/colorPickerLabels.js +22 -0
  23. package/src/js/util/getColorControls.js +103 -0
  24. package/src/js/util/getColorForm.js +27 -19
  25. package/src/js/util/getColorMenu.js +95 -0
  26. package/src/js/util/isValidJSON.js +13 -0
  27. package/src/js/util/nonColors.js +5 -0
  28. package/src/js/util/templates.js +1 -0
  29. package/src/scss/color-picker.rtl.scss +23 -0
  30. package/src/scss/color-picker.scss +430 -0
  31. package/types/cp.d.ts +263 -160
  32. package/types/index.d.ts +9 -2
  33. package/types/source/source.ts +2 -1
  34. package/types/source/types.d.ts +28 -5
  35. package/dist/js/color-picker.esm.js +0 -2998
  36. package/dist/js/color-picker.esm.min.js +0 -2
  37. package/src/js/util/getColorControl.js +0 -49
  38. package/src/js/util/init.js +0 -14
@@ -1,2 +1,3810 @@
1
- // ColorPickerElement v0.0.1alpha1 | thednp © 2022 | MIT-License
2
- function e(e){return e instanceof HTMLElement?e.ownerDocument:e instanceof Window?e.document:window.document}const t=[Document,Element,HTMLElement],o=[Element,HTMLElement];function n(n,r){const a=t.some(e=>r instanceof e)?r:e();return o.some(e=>n instanceof e)?n:a.querySelector(n)}const r=(e,t)=>Object.assign(e,t);function a(t){if("string"==typeof t)return e().createElement(t);const{tagName:o}=t,n={...t},s=a(o);return delete n.tagName,r(s,n),s}const s=(e,t)=>{r(e.style,t)},i=["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkgrey","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkslategrey","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dimgrey","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","goldenrod","gold","gray","green","greenyellow","grey","honeydew","hotpink","indianred","indigo","ivory","khaki","lavenderblush","lavender","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightgrey","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightslategrey","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","slategrey","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"],l="(?:[-\\+]?\\d*\\.\\d+%?)|(?:[-\\+]?\\d+%?)",c=`[\\s|\\(]+(${l})[,|\\s]+(${l})[,|\\s]+(${l})\\s*\\)?`,h=`[\\s|\\(]+(${l})[,|\\s]+(${l})[,|\\s]+(${l})[,|\\s]+(${l})\\s*\\)?`,u={CSS_UNIT:new RegExp(l),rgb:new RegExp("rgb"+c),rgba:new RegExp("rgba"+h),hsl:new RegExp("hsl"+c),hsla:new RegExp("hsla"+h),hsv:new RegExp("hsv"+c),hsva:new RegExp("hsva"+h),hex3:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex6:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,hex4:/^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,hex8:/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/};function d(e){return"string"==typeof e&&e.includes(".")&&1===parseFloat(e)}function g(e){return"string"==typeof e&&e.includes("%")}function p(e){return Boolean(u.CSS_UNIT.exec(String(e)))}function m(e,t){let o=e;return d(e)&&(o="100%"),o=360===t?o:Math.min(t,Math.max(0,parseFloat(o))),g(o)&&(o=parseInt(String(o*t),10)/100),Math.abs(o-t)<1e-6?1:(o=360===t?(o<0?o%t+t:o%t)/parseFloat(String(t)):o%t/parseFloat(String(t)),o)}function f(e){let t=parseFloat(e);return(Number.isNaN(t)||t<0||t>1)&&(t=1),t}function b(e){return Math.min(1,Math.max(0,e))}function w(t){const o=e(n).head;var n;s(o,{color:t});const r=function(e,t){const o=getComputedStyle(e);return t in o?o[t]:""}(o,"color");return s(o,{color:""}),r}function v(e){return e<=1?100*Number(e)+"%":e}function k(e){return 1===e.length?"0"+e:String(e)}function x(e,t,o){return{r:255*m(e,255),g:255*m(t,255),b:255*m(o,255)}}function y(e,t,o){const n=m(e,255),r=m(t,255),a=m(o,255),s=Math.max(n,r,a),i=Math.min(n,r,a);let l=0,c=0;const h=(s+i)/2;if(s===i)c=0,l=0;else{const e=s-i;switch(c=h>.5?e/(2-s-i):e/(s+i),s){case n:l=(r-a)/e+(r<a?6:0);break;case r:l=(a-n)/e+2;break;case a:l=(n-r)/e+4}l/=6}return{h:l,s:c,l:h}}function S(e,t,o){let n=o;return n<0&&(n+=1),n>1&&(n-=1),n<1/6?e+6*n*(t-e):n<.5?t:n<2/3?e+(t-e)*(2/3-n)*6:e}function M(e,t,o){let n=0,r=0,a=0;const s=m(e,360),i=m(t,100),l=m(o,100);if(0===i)r=l,a=l,n=l;else{const e=l<.5?l*(1+i):l+i-l*i,t=2*l-e;n=S(t,e,s+1/3),r=S(t,e,s),a=S(t,e,s-1/3)}return{r:255*n,g:255*r,b:255*a}}function C(e,t,o){const n=m(e,255),r=m(t,255),a=m(o,255),s=Math.max(n,r,a),i=Math.min(n,r,a);let l=0;const c=s,h=s-i,u=0===s?0:h/s;if(s===i)l=0;else{switch(s){case n:l=(r-a)/h+(r<a?6:0);break;case r:l=(a-n)/h+2;break;case a:l=(n-r)/h+4}l/=6}return{h:l,s:u,v:c}}function $(e,t,o){const n=6*m(e,360),r=m(t,100),a=m(o,100),s=Math.floor(n),i=n-s,l=a*(1-r),c=a*(1-i*r),h=a*(1-(1-i)*r),u=s%6;return{r:255*[a,c,l,l,h,a][u],g:255*[h,a,a,c,l,l][u],b:255*[l,l,h,a,a,c][u]}}function L(e,t,o){return[k(Math.round(e).toString(16)),k(Math.round(t).toString(16)),k(Math.round(o).toString(16))].join("")}function N(e){return P(e)/255}function P(e){return parseInt(e,16)}function H(e){return{r:e>>16,g:(65280&e)>>8,b:255&e}}function E(e){let t=e.trim().toLowerCase();if(0===t.length)return{r:0,g:0,b:0,a:0};let o=!1;if(i.includes(t))t=w(t),o=!0;else if("transparent"===t)return{r:0,g:0,b:0,a:0,format:"name"};let n=u.rgb.exec(t);return n?{r:n[1],g:n[2],b:n[3]}:(n=u.rgba.exec(t),n?{r:n[1],g:n[2],b:n[3],a:n[4]}:(n=u.hsl.exec(t),n?{h:n[1],s:n[2],l:n[3]}:(n=u.hsla.exec(t),n?{h:n[1],s:n[2],l:n[3],a:n[4]}:(n=u.hsv.exec(t),n?{h:n[1],s:n[2],v:n[3]}:(n=u.hsva.exec(t),n?{h:n[1],s:n[2],v:n[3],a:n[4]}:(n=u.hex8.exec(t),n?{r:P(n[1]),g:P(n[2]),b:P(n[3]),a:N(n[4]),format:o?"name":"hex8"}:(n=u.hex6.exec(t),n?{r:P(n[1]),g:P(n[2]),b:P(n[3]),format:o?"name":"hex"}:(n=u.hex4.exec(t),n?{r:P(n[1]+n[1]),g:P(n[2]+n[2]),b:P(n[3]+n[3]),a:N(n[4]+n[4]),format:o?"name":"hex8"}:(n=u.hex3.exec(t),!!n&&{r:P(n[1]+n[1]),g:P(n[2]+n[2]),b:P(n[3]+n[3]),format:o?"name":"hex"})))))))))}function A(e){let t,o={r:0,g:0,b:0},n=e,r=null,a=null,s=null,i=!1,l="hex";return"string"==typeof e&&(n=E(e),n&&(i=!0)),"object"==typeof n&&(p(n.r)&&p(n.g)&&p(n.b)?(o=x(n.r,n.g,n.b),i=!0,l="%"===(""+n.r).slice(-1)?"prgb":"rgb"):p(n.h)&&p(n.s)&&p(n.v)?(r=v(n.s),a=v(n.v),o=$(n.h,r,a),i=!0,l="hsv"):p(n.h)&&p(n.s)&&p(n.l)&&(r=v(n.s),s=v(n.l),o=M(n.h,r,s),i=!0,l="hsl"),"a"in n&&(t=n.a)),{ok:i,format:n.format||l,r:Math.min(255,Math.max(o.r,0)),g:Math.min(255,Math.max(o.g,0)),b:Math.min(255,Math.max(o.b,0)),a:f(t)}}const T={format:"hex"};class R{constructor(e,t){let o=e;const n="object"==typeof t?r(T,t):r({},T);o instanceof R&&(o=A(o)),"number"==typeof o&&(o=H(o));const{r:a,g:s,b:i,a:l,ok:c,format:h}=A(o);this.originalInput=o,this.r=a,this.g=s,this.b=i,this.a=l,this.ok=c,this.roundA=Math.round(100*this.a)/100,this.format=n.format||h,this.r<1&&(this.r=Math.round(this.r)),this.g<1&&(this.g=Math.round(this.g)),this.b<1&&(this.b=Math.round(this.b))}get isValid(){return this.ok}get isDark(){return this.brightness<128}get luminance(){const{r:e,g:t,b:o}=this;let n=0,r=0,a=0;const s=e/255,i=t/255,l=o/255;return n=s<=.03928?s/12.92:((s+.055)/1.055)**2.4,r=i<=.03928?i/12.92:((i+.055)/1.055)**2.4,a=l<=.03928?l/12.92:((l+.055)/1.055)**2.4,.2126*n+.7152*r+.0722*a}get brightness(){const{r:e,g:t,b:o}=this;return(299*e+587*t+114*o)/1e3}toRgb(){return{r:Math.round(this.r),g:Math.round(this.g),b:Math.round(this.b),a:this.a}}toRgbString(){const e=Math.round(this.r),t=Math.round(this.g),o=Math.round(this.b);return 1===this.a?`rgb(${e},${t},${o})`:`rgba(${e},${t},${o},${this.roundA})`}toHex(){return L(this.r,this.g,this.b)}toHexString(){return"#"+this.toHex()}toHsv(){const{h:e,s:t,v:o}=C(this.r,this.g,this.b);return{h:360*e,s:t,v:o,a:this.a}}toHsl(){const{h:e,s:t,l:o}=y(this.r,this.g,this.b);return{h:360*e,s:t,l:o,a:this.a}}toHslString(){const e=this.toHsl(),t=Math.round(e.h),o=Math.round(100*e.s),n=Math.round(100*e.l);return 1===this.a?`hsl(${t},${o}%,${n}%)`:`hsla(${t},${o}%,${n}%,${this.roundA})`}setAlpha(e){return this.a=f(e),this.roundA=Math.round(100*this.a)/100,this}saturate(e){if(!e)return this;const t=this.toHsl();return t.s+=e/100,t.s=b(t.s),new R(t)}desaturate(e){return e?this.saturate(-e):this}greyscale(){return this.desaturate(100)}clone(){return new R(this)}toString(){const{format:e}=this;return"rgb"===e?this.toRgbString():"hsl"===e?this.toHslString():this.toHexString()}}r(R,{colorNames:i,CSS_INTEGER:"[-\\+]?\\d+%?",CSS_NUMBER:"[-\\+]?\\d*\\.\\d+%?",CSS_UNIT:l,PERMISSIVE_MATCH3:c,PERMISSIVE_MATCH4:h,matchers:u,isOnePointZero:d,isPercentage:g,isValidCSSUnit:p,bound01:m,boundAlpha:f,clamp01:b,getHexFromColorName:w,convertToPercentage:v,convertHexToDecimal:N,pad2:k,rgbToRgb:x,rgbToHsl:y,rgbToHex:L,rgbToHsv:C,hslToRgb:M,hsvToRgb:$,hue2rgb:S,parseIntFromHex:P,numberInputToObject:H,stringInputToObject:E,inputToRGB:A});const F={};function D(e){const t=this,{type:o}=e;(F[o]?[...F[o]]:[]).forEach(n=>{const[r,a]=n;[...a].forEach(n=>{if(r===t){const[t,a]=n;t.apply(r,[e]),a&&a.once&&O(r,o,t,a)}})})}const I=(e,t,o,n)=>{F[t]||(F[t]=new Map);const r=F[t];r.has(e)||r.set(e,new Map);const a=r.get(e),{size:s}=a;a&&a.set(o,n),s||e.addEventListener(t,D,n)},O=(e,t,o,n)=>{const r=F[t],a=r&&r.get(e),s=a&&a.get(o),{options:i}=void 0!==s?s:{options:n};a&&a.has(o)&&a.delete(o),!r||a&&a.size||r.delete(e),r&&r.size||delete F[t],a&&a.size||e.removeEventListener(t,D,i)},q="ArrowDown",Y="ArrowUp",_="ArrowLeft",V="ArrowRight",K="Enter",G="Space",{userAgentData:U}=navigator,X=U,{userAgent:j}=navigator,B=j,z=/iPhone|iPad|iPod|Android/i;let W=!1;W=X?X.brands.some(e=>z.test(e.brand)):z.test(B);const J=W;let Z=1;const Q=new Map;function ee(e,t){const{width:o,height:n,top:r,right:a,bottom:s,left:i}=e.getBoundingClientRect();let l=1,c=1;if(t&&e instanceof HTMLElement){const{offsetWidth:t,offsetHeight:r}=e;l=t>0&&Math.round(o)/t||1,c=r>0&&Math.round(n)/r||1}return{width:o/l,height:n/c,top:r/c,right:a/l,bottom:s/c,left:i/l,x:i/l,y:r/c}}function te(o,n){return(n&&t.some(e=>n instanceof e)?n:e()).querySelectorAll(o)}function oe(t,o){if("string"==typeof o)return e().createElementNS(t,o);const{tagName:n}=o,a={...o},s=oe(t,n);return delete a.tagName,r(s,a),s}const ne=new Map,re={set:(e,t,o)=>{const r=n(e);if(!r)return;ne.has(t)||ne.set(t,new Map);ne.get(t).set(r,o)},getAllFor:e=>ne.get(e)||null,get:(e,t)=>{const o=n(e),r=re.getAllFor(t);return o&&r&&r.get(o)||null},remove:(e,t)=>{const o=n(e),r=ne.get(t);r&&o&&(r.delete(o),0===r.size&&ne.delete(t))}};function ae(e,t){return e.classList.contains(t)}function se(e,t){e.classList.add(t)}function ie(e,t){e.classList.remove(t)}const le=(e,t,o)=>e.setAttribute(t,o),ce=(e,t)=>e.getAttribute(t),he="v-hidden";function ue(e,t,o,n,r){const s=`appearance${e}_${t}`,i=1===e?"color-pointer":"color-slider",l=a({tagName:"div",className:"color-control"});le(l,"role","presentation"),l.append(a({id:s,tagName:"label",className:"color-label v-hidden",ariaLive:"polite"}),a({tagName:"canvas",className:"visual-control"+e,ariaHidden:"true",width:""+o,height:""+n}));const c=a({tagName:"div",className:i+" knob"});return le(c,"aria-labelledby",r||s),le(c,"tabindex","0"),l.append(c),l}const de=["transparent","currentColor","inherit","initial"],ge=["white","black","grey","red","orange","brown","gold","olive","yellow","lime","green","teal","cyan","blue","violet","magenta","pink"],pe={pickerLabel:"Colour Picker",toggleLabel:"Select colour",menuLabel:"Select colour preset",requiredLabel:"Required",formatLabel:"Colour Format",formatHEX:"Hexadecimal Format",formatRGB:"RGB Format",formatHSL:"HSL Format",alphaLabel:"Alpha",appearanceLabel:"Colour Appearance",hexLabel:"Hexadecimal",hueLabel:"Hue",saturationLabel:"Saturation",lightnessLabel:"Lightness",redLabel:"Red",greenLabel:"Green",blueLabel:"Blue"};function me(e,t){const o=t?I:O,{input:n,pickerToggle:r,menuToggle:a}=e;o(n,"focusin",e.showPicker),o(r,"click",e.togglePicker),o(n,"keydown",e.keyHandler),a&&o(a,"click",e.toggleMenu)}function fe(e){const{input:t,parent:o,format:n,id:s,componentLabels:i,keywords:l}=e,c=ce(t,"value")||"#fff",{toggleLabel:h,menuLabel:u,formatLabel:d,pickerLabel:g,appearanceLabel:p}=i,m=de.includes(c)?"#fff":c;e.color=new R(m,{format:n});const f=J?150:230,b=J?150:230,w=J?" mobile":"",v="hsl"===n?`appearance_${s} appearance1_${s}`:"appearance1_"+s,k="hsl"===n?"appearance2_"+s:`appearance_${s} appearance2_${s}`,x=a({tagName:"button",className:"picker-toggle button-appearance",ariaExpanded:"false",ariaHasPopup:"true",ariaLive:"polite"});le(x,"tabindex","-1"),x.append(a({tagName:"span",className:he,innerText:"Open Color Picker"}));const y=a({tagName:"div",className:"color-dropdown picker"+w});le(y,"aria-labelledby",`picker-label-${s} format-label-${s}`),le(y,"role","group"),y.append(a({tagName:"label",className:he,ariaHidden:"true",id:"picker-label-"+s,innerText:""+g}),a({tagName:"label",className:he,ariaHidden:"true",id:"format-label-"+s,innerText:""+d}),a({tagName:"label",className:"color-appearance v-hidden",ariaHidden:"true",ariaLive:"polite",id:"appearance_"+s,innerText:""+p}));const S=a({tagName:"div",className:"color-controls "+n});S.append(ue(1,s,f,b,v),ue(2,s,21,b,k)),"hex"!==n&&S.append(ue(3,s,21,b));const M=function(e){const{format:t,id:o}=e,n=a({tagName:"div",className:"color-form "+t});let s=["hex"];return"rgb"===t?s=["red","green","blue","alpha"]:"hsl"===t&&(s=["hue","saturation","lightness","alpha"]),s.forEach(e=>{const[s]="hex"===t?["#"]:(i=e,i.toUpperCase()).split("");var i;const l=`color_${t}_${e}_${o}`,c=a({tagName:"label"});le(c,"for",l),c.append(a({tagName:"span",ariaHidden:"true",innerText:s+":"}),a({tagName:"span",className:he,innerText:""+e}));const h=a({tagName:"input",id:l,type:"hex"===t?"text":"number",value:"alpha"===e?"1":"0",className:"color-input "+e,autocomplete:"off",spellcheck:"false"});if("hex"!==t){let o="1",n="0.01";"alpha"!==e&&("rgb"===t?(o="255",n="1"):"hue"===e?(o="360",n="1"):(o="100",n="1")),r(h,{min:"0",max:o,step:n})}n.append(c,h)}),n}(e);if(y.append(S,M),o.append(x,y),l){const e=l,r=a({tagName:"div",className:"color-dropdown menu"+w}),s=a({tagName:"ul",ariaLabel:""+u,className:"color-menu"});le(s,"role","listbox"),r.append(s),e.forEach(e=>{const o=e.trim(),r=new R(o,{format:n}).toString()===ce(t,"value"),i=a({tagName:"li",className:"color-option"+(r?" active":""),ariaSelected:r?"true":"false",innerText:""+e});le(i,"role","option"),le(i,"tabindex","0"),le(i,"data-value",""+o),s.append(i)});const i=a({tagName:"button",className:"menu-toggle button-appearance",ariaExpanded:"false",ariaHasPopup:"true"}),c=encodeURI("http://www.w3.org/2000/svg"),d=oe(c,{tagName:"svg"});le(d,"xmlns",c),le(d,"aria-hidden","true"),le(d,"viewBox","0 0 512 512");const g=oe(c,{tagName:"path"});le(g,"d","M98,158l157,156L411,158l27,27L255,368L71,185L98,158z"),le(g,"fill","#fff"),d.append(g),i.append(a({tagName:"span",className:he,innerText:""+h}),d),o.append(i,r)}l&&de.includes(c)&&(e.value=c)}function be(e,t){const o=t?I:O,n="ontouchstart"in document?{down:"touchstart",move:"touchmove",up:"touchend"}:{down:"mousedown",move:"mousemove",up:"mouseup"};o(e.controls,n.down,e.pointerDown),e.controlKnobs.forEach(t=>o(t,"keydown",e.handleKnobs)),o(window,"scroll",e.handleScroll),[e.input,...e.inputs].forEach(t=>o(t,"change",e.changeHandler)),e.colorMenu&&(o(e.colorMenu,"click",e.menuClickHandler),o(e.colorMenu,"keydown",e.menuKeyHandler)),o(document,n.move,e.pointerMove),o(document,n.up,e.pointerUp),o(window,"keyup",e.handleDismiss),o(e.parent,"focusout",e.handleFocusOut)}function we(e){var t,o;t=e.input,o=new CustomEvent("colorpicker.change"),t.dispatchEvent(o)}function ve(e,t){const o=t?ae:ie;return!!e&&["show","show-top"][t?"some":"forEach"](t=>o(e,t))}class ke{constructor(e){const t=this;if(t.input=n(e),!t.input)throw new TypeError(`ColorPicker target ${e} cannot be found.`);const{input:o}=t;if(t.parent=function e(t,o){return t?t.closest(o)||e(t.getRootNode().host,o):null}(o,".color-picker,color-picker"),!t.parent)throw new TypeError("ColorPicker requires a specific markup to work.");t.id=function(e,t){Z+=1;let o=Q.get(e),n=Z;if(t&&t.length)if(o){const e=o.get(t);Number.isNaN(e)?o.set(t,n):n=e}else Q.set(e,new Map),o=Q.get(e),o.set(t,n);else Number.isNaN(o)?Q.set(e,n):n=o;return n}(o,"color-picker"),t.dragElement=null,t.isOpen=!1,t.controlPositions={c1x:0,c1y:0,c2y:0,c3y:0},t.colorLabels={},t.keywords=!1,t.color=new R("white",{format:t.format}),t.componentLabels=r({},pe);const{componentLabels:a,colorLabels:s,keywords:i}=o.dataset,l=a?JSON.parse(a):{};t.componentLabels=r(t.componentLabels,l);const c=s&&17===s.split(",").length?s.split(","):ge;ge.forEach((e,o)=>{t.colorLabels[e]=c[o]}),"false"!==i&&(t.keywords=i?i.split(","):de),t.showPicker=t.showPicker.bind(t),t.togglePicker=t.togglePicker.bind(t),t.toggleMenu=t.toggleMenu.bind(t),t.menuClickHandler=t.menuClickHandler.bind(t),t.menuKeyHandler=t.menuKeyHandler.bind(t),t.pointerDown=t.pointerDown.bind(t),t.pointerMove=t.pointerMove.bind(t),t.pointerUp=t.pointerUp.bind(t),t.handleScroll=t.handleScroll.bind(t),t.handleFocusOut=t.handleFocusOut.bind(t),t.changeHandler=t.changeHandler.bind(t),t.handleDismiss=t.handleDismiss.bind(t),t.keyHandler=t.keyHandler.bind(t),t.handleKnobs=t.handleKnobs.bind(t),fe(t);const{parent:h}=t;t.pickerToggle=n(".picker-toggle",h),t.menuToggle=n(".menu-toggle",h),t.colorMenu=n(".color-dropdown.menu",h),t.colorPicker=n(".color-dropdown.picker",h),t.controls=n(".color-controls",h),t.inputs=[...te(".color-input",h)],t.controlKnobs=[...te(".knob",h)],t.visuals=[...te("canvas",t.controls)],t.knobLabels=[...te(".color-label",h)],t.appearance=n(".color-appearance",h);const[u,d,g]=t.visuals;t.width1=u.width,t.height1=u.height,t.width2=d.width,t.height2=d.height,t.ctx1=u.getContext("2d"),t.ctx2=d.getContext("2d"),t.ctx1.rect(0,0,t.width1,t.height1),t.ctx2.rect(0,0,t.width2,t.height2),t.width3=0,t.height3=0,"hex"!==t.format&&(t.width3=g.width,t.height3=g.height,this.ctx3=g.getContext("2d"),t.ctx3.rect(0,0,t.width3,t.height3)),this.setControlPositions(),this.setColorAppearence(),this.updateInputs(!0),this.updateControls(),this.updateVisuals(),me(t,!0),re.set(o,"color-picker",t)}get value(){return this.input.value}set value(e){this.input.value=e}get required(){return e=this.input,t="required",e.hasAttribute(t);var e,t}get format(){return ce(this.input,"format")||"hex"}get name(){return ce(this.input,"name")}get label(){return n(`[for="${this.input.id}"]`)}get includeNonColor(){return this.keywords instanceof Array&&this.keywords.some(e=>de.includes(e))}get hex(){return this.color.toHex()}get hsv(){return this.color.toHsv()}get hsl(){return this.color.toHsl()}get rgb(){return this.color.toRgb()}get brightness(){return this.color.brightness}get luminance(){return this.color.luminance}get isDark(){const{rgb:e,brightness:t}=this;return t<120&&e.a>.33}get isValid(){const e=this.input.value;return""!==e&&new R(e).isValid}updateVisuals(){const{color:e,format:t,controlPositions:o,width1:n,width2:r,width3:a,height1:s,height2:i,height3:l,ctx1:c,ctx2:h,ctx3:u}=this,{r:d,g:g,b:p}=e;if("hsl"!==t){const e=Math.round(o.c2y/i*360);c.fillStyle=new R(`hsl(${e},100%,50%})`).toRgbString(),c.fillRect(0,0,n,s);const t=h.createLinearGradient(0,0,n,0);t.addColorStop(0,"rgba(255,255,255,1)"),t.addColorStop(1,"rgba(255,255,255,0)"),c.fillStyle=t,c.fillRect(0,0,n,s);const a=h.createLinearGradient(0,0,0,s);a.addColorStop(0,"rgba(0,0,0,0)"),a.addColorStop(1,"rgba(0,0,0,1)"),c.fillStyle=a,c.fillRect(0,0,n,s);const l=h.createLinearGradient(0,0,0,s);l.addColorStop(0,"rgba(255,0,0,1)"),l.addColorStop(.17,"rgba(255,255,0,1)"),l.addColorStop(.34,"rgba(0,255,0,1)"),l.addColorStop(.51,"rgba(0,255,255,1)"),l.addColorStop(.68,"rgba(0,0,255,1)"),l.addColorStop(.85,"rgba(255,0,255,1)"),l.addColorStop(1,"rgba(255,0,0,1)"),h.fillStyle=l,h.fillRect(0,0,r,i)}else{const t=c.createLinearGradient(0,0,n,0),r=Math.round(100*(1-o.c2y/i));t.addColorStop(0,new R("rgba(255,0,0,1)").desaturate(100-r).toRgbString()),t.addColorStop(.17,new R("rgba(255,255,0,1)").desaturate(100-r).toRgbString()),t.addColorStop(.34,new R("rgba(0,255,0,1)").desaturate(100-r).toRgbString()),t.addColorStop(.51,new R("rgba(0,255,255,1)").desaturate(100-r).toRgbString()),t.addColorStop(.68,new R("rgba(0,0,255,1)").desaturate(100-r).toRgbString()),t.addColorStop(.85,new R("rgba(255,0,255,1)").desaturate(100-r).toRgbString()),t.addColorStop(1,new R("rgba(255,0,0,1)").desaturate(100-r).toRgbString()),c.fillStyle=t,c.fillRect(0,0,n,s);const u=c.createLinearGradient(0,0,0,s);u.addColorStop(0,"rgba(255,255,255,1)"),u.addColorStop(.5,"rgba(255,255,255,0)"),c.fillStyle=u,c.fillRect(0,0,n,s);const m=c.createLinearGradient(0,0,0,s);m.addColorStop(.5,"rgba(0,0,0,0)"),m.addColorStop(1,"rgba(0,0,0,1)"),c.fillStyle=m,c.fillRect(0,0,n,s);const f=h.createLinearGradient(0,0,0,i),b=e.clone().greyscale().toRgb();f.addColorStop(0,`rgba(${d},${g},${p},1)`),f.addColorStop(1,`rgba(${b.r},${b.g},${b.b},1)`),h.fillStyle=f,h.fillRect(0,0,a,l)}if("hex"!==t){u.clearRect(0,0,a,l);const e=u.createLinearGradient(0,0,0,l);e.addColorStop(0,`rgba(${d},${g},${p},1)`),e.addColorStop(1,`rgba(${d},${g},${p},0)`),u.fillStyle=e,u.fillRect(0,0,a,l)}}handleFocusOut({relatedTarget:e}){e&&!this.parent.contains(e)&&this.hide(!0)}handleDismiss({code:e}){const t=this;t.isOpen&&"Escape"===e&&t.hide()}handleScroll(e){const{activeElement:t}=document;(J&&this.dragElement||t&&this.controlKnobs.includes(t))&&(e.stopPropagation(),e.preventDefault()),this.updateDropdownPosition()}menuKeyHandler(e){const{target:t,code:o}=e;[q,Y].includes(o)?e.preventDefault():[K,G].includes(o)&&this.menuClickHandler({target:t})}menuClickHandler(e){const t=this,{target:o}=e,{format:n}=t,r=(ce(o,"data-value")||"").trim(),a=t.colorMenu.querySelector("li.active"),s=de.includes(r)?"white":r;var i;t.color=new R(s,{format:n}),t.setControlPositions(),t.setColorAppearence(),t.updateInputs(!0),t.updateControls(),t.updateVisuals(),a&&(ie(a,"active"),i="aria-selected",a.removeAttribute(i)),a!==o&&(se(o,"active"),le(o,"aria-selected","true"),de.includes(r)&&(t.value=r,we(t)))}pointerDown(e){const t=this,{type:o,target:r,touches:a,pageX:s,pageY:i}=e,{visuals:l,controlKnobs:c,format:h}=t,[u,d,g]=l,[p,m,f]=c,b="canvas"===r.tagName?r:n("canvas",r.parentElement),w=ee(b),v="touchstart"===o?a[0].pageX:s,k="touchstart"===o?a[0].pageY:i,x=v-window.pageXOffset-w.left,y=k-window.pageYOffset-w.top;r===u||r===p?(t.dragElement=b,t.changeControl1({offsetX:x,offsetY:y})):r===d||r===m?(t.dragElement=b,t.changeControl2({offsetY:y})):"hex"===h||r!==g&&r!==f||(t.dragElement=b,t.changeAlpha({offsetY:y})),e.preventDefault()}pointerUp({target:e}){const t=this,o=document.getSelection();t.dragElement||o.toString().length||t.parent.contains(e)||t.hide(),t.dragElement=null}pointerMove(e){const t=this,{dragElement:o,visuals:n,format:r}=t,[a,s,i]=n,{type:l,touches:c,pageX:h,pageY:u}=e;if(!o)return;const d=ee(o),g="touchmove"===l?c[0].pageX:h,p="touchmove"===l?c[0].pageY:u,m=g-window.pageXOffset-d.left,f=p-window.pageYOffset-d.top;o===a&&t.changeControl1({offsetX:m,offsetY:f}),o===s&&t.changeControl2({offsetY:f}),o===i&&"hex"!==r&&t.changeAlpha({offsetY:f})}handleKnobs(e){const{target:t,code:o}=e,n=this;if(![Y,q,_,V].includes(o))return;e.preventDefault();const{activeElement:r}=document,{controlKnobs:a}=n,s=a.find(e=>e===r),[i,l,c]=a;if(s){let r=0,a=0;t===i?([_,V].includes(o)?n.controlPositions.c1x+=o===V?1:-1:[Y,q].includes(o)&&(n.controlPositions.c1y+=o===q?1:-1),r=n.controlPositions.c1x,a=n.controlPositions.c1y,n.changeControl1({offsetX:r,offsetY:a})):t===l?(n.controlPositions.c2y+=[q,V].includes(o)?1:-1,a=n.controlPositions.c2y,n.changeControl2({offsetY:a})):t===c&&(n.controlPositions.c3y+=[q,V].includes(o)?1:-1,a=n.controlPositions.c3y,n.changeAlpha({offsetY:a})),n.setColorAppearence(),n.updateInputs(),n.updateControls(),n.updateVisuals(),n.handleScroll(e)}}changeHandler(){const e=this;let t;const{activeElement:o}=document,{inputs:n,format:r,value:a,input:s}=e,[i,l,c,h]=n,u=e.includeNonColor&&de.includes(a);(o===s||o&&n.includes(o))&&(t=o===s?u?"white":a:"hex"===r?i.value:"hsl"===r?`hsla(${i.value},${l.value}%,${c.value}%,${h.value})`:`rgba(${n.map(e=>e.value).join(",")})`,e.color=new R(t,{format:r}),e.setControlPositions(),e.setColorAppearence(),e.updateInputs(),e.updateControls(),e.updateVisuals(),o===s&&u&&(e.value=a))}changeControl1(e){let[t,o]=[0,0];const{offsetX:n,offsetY:r}=e,{format:a,controlPositions:s,height1:i,height2:l,height3:c,width1:h}=this;n>h?t=h:n>=0&&(t=n),r>i?o=i:r>=0&&(o=r);const u="hsl"!==a?Math.round(s.c2y/l*360):Math.round(t/h*360),d="hsl"!==a?Math.round(t/h*100):Math.round(100*(1-s.c2y/l)),g=Math.round(100*(1-o/i)),p="hex"!==a?Math.round(100*(1-s.c3y/c))/100:1,m="hsl"!==a?"hsva":"hsla";this.color=new R(`${m}(${u},${d}%,${g}%,${p})`,{format:a}),this.controlPositions.c1x=t,this.controlPositions.c1y=o,this.setColorAppearence(),this.updateInputs(),this.updateControls(),this.updateVisuals()}changeControl2(e){const{offsetY:t}=e,{format:o,width1:n,height1:r,height2:a,height3:s,controlPositions:i}=this;let l=0;t>a?l=a:t>=0&&(l=t);const c="hsl"!==o?Math.round(l/a*360):Math.round(i.c1x/n*360),h="hsl"!==o?Math.round(i.c1x/n*100):Math.round(100*(1-l/a)),u=Math.round(100*(1-i.c1y/r)),d="hex"!==o?Math.round(100*(1-i.c3y/s))/100:1,g="hsl"!==o?"hsva":"hsla";this.color=new R(`${g}(${c},${h}%,${u}%,${d})`,{format:o}),this.controlPositions.c2y=l,this.setColorAppearence(),this.updateInputs(),this.updateControls(),this.updateVisuals()}changeAlpha(e){const{height3:t}=this,{offsetY:o}=e;let n=0;o>t?n=t:o>=0&&(n=o);const r=Math.round(100*(1-n/t));this.color.setAlpha(r/100),this.controlPositions.c3y=n,this.updateInputs(),this.updateControls(),this.updateVisuals()}updateDropdownPosition(){const{input:e,colorPicker:t,colorMenu:o}=this,n=ee(e),{offsetHeight:r}=e,a=document.documentElement.clientHeight,s=ve(t,!0)?t:o,{offsetHeight:i}=s,l=a-n.bottom,c=n.top,h=n.top+i+r>a,u=n.top-i<0;ae(s,"show")&&l<c&&h&&(ie(s,"show"),se(s,"show-top")),ae(s,"show-top")&&l>c&&u&&(ie(s,"show-top"),se(s,"show"))}setControlPositions(){const e=this,{hsv:t,hsl:o,format:n,height1:r,height2:a,height3:s,width1:i}=e,l=o.h,c="hsl"!==n?t.s:o.s,h="hsl"!==n?t.v:o.l,u=t.a;e.controlPositions.c1x="hsl"!==n?c*i:l/360*i,e.controlPositions.c1y=(1-h)*r,e.controlPositions.c2y="hsl"!==n?l/360*a:(1-c)*a,"hex"!==n&&(e.controlPositions.c3y=(1-u)*s)}setColorAppearence(){const e=this,{componentLabels:t,colorLabels:o,hsl:n,hsv:r,hex:a,format:s,knobLabels:i}=e,{lightnessLabel:l,saturationLabel:c,hueLabel:h,alphaLabel:u,appearanceLabel:d,hexLabel:g}=t;let{requiredLabel:p}=t;const[m,f,b]=i,w=Math.round(n.h),v=r.a,k="hsl"===s?n.s:r.s,x=Math.round(100*k),y=Math.round(100*n.l),S=100*r.v;let M;if(100===y&&0===x)M=o.white;else if(0===y)M=o.black;else if(0===x)M=o.grey;else if(w<15||w>=345)M=o.red;else if(w>=15&&w<45)M=S>80&&x>80?o.orange:o.brown;else if(w>=45&&w<75){const e=w>=54&&w<75&&S<80;M=w>46&&w<54&&S<80&&x>90?o.gold:o.yellow,M=e?o.olive:M}else w>=75&&w<155?M=S<68?o.green:o.lime:w>=155&&w<175?M=o.teal:w>=175&&w<195?M=o.cyan:w>=195&&w<255?M=o.blue:w>=255&&w<270?M=o.violet:w>=270&&w<295?M=o.magenta:w>=295&&w<345&&(M=o.pink);if("hsl"===s?(m.innerText=`${h}: ${w}°. ${l}: ${y}%`,f.innerText=`${c}: ${x}%`):(m.innerText=`${l}: ${y}%. ${c}: ${x}%`,f.innerText=`${h}: ${w}°`),"hex"!==s){const e=Math.round(100*v);b.innerText=`${u}: ${e}%`}e.appearance.innerText=`${d}: ${M}.`;const C="hex"===s?`${g} ${a.split("").join(" ")}.`:e.value.toUpperCase();if(e.label){const t=e.label.innerText.replace("*","").trim(),[o]=e.pickerToggle.children;p=e.required?" "+p:"",o.innerText=`${t}: ${C}${p}`}}updateControls(){const{format:e,controlKnobs:t,controlPositions:o}=this,[n,r,a]=t;n.style.transform=`translate3d(${o.c1x-3}px,${o.c1y-3}px,0)`,r.style.transform=`translate3d(0,${o.c2y-3}px,0)`,"hex"!==e&&(a.style.transform=`translate3d(0,${o.c3y-3}px,0)`)}updateInputs(e){const t=this,{value:o,rgb:n,hsl:a,hsv:s,format:i,parent:l,input:c,inputs:h}=t,[u,d,g,p]=h,m=a.a,f=Math.round(a.h),b=Math.round(100*a.s),w="hsl"===i?a.l:s.v,v=Math.round(100*w);let k;"hex"===i?(k=t.color.toHexString(),u.value=t.hex):"hsl"===i?(k=t.color.toHslString(),u.value=""+f,d.value=""+b,g.value=""+v,p.value=""+m):"rgb"===i&&(k=t.color.toRgbString(),u.value=""+n.r,d.value=""+n.g,g.value=""+n.b,p.value=""+m),t.value=""+k,r(c.style,{backgroundColor:k}),t.isDark?(ae(l,"light")&&ie(l,"light"),ae(l,"dark")||se(l,"dark")):(ae(l,"dark")&&ie(l,"dark"),ae(l,"light")||se(l,"light")),e||k===o||we(t)}keyHandler(e){const t=this,{menuToggle:o}=t,{activeElement:n}=document,{code:r}=e;[K,G].includes(r)&&(o&&n===o||!n)&&(e.preventDefault(),n?t.toggleMenu():t.togglePicker(e))}togglePicker(e){e.preventDefault();const t=this,o=ve(t.colorPicker,!0);t.isOpen&&o?t.hide(!0):t.showPicker()}showPicker(){ve(this.colorMenu),se(this.colorPicker,"show"),this.input.focus(),this.show(),le(this.pickerToggle,"aria-expanded","true")}toggleMenu(){const e=this,t=ve(e.colorMenu,!0);e.isOpen&&t?e.hide(!0):function(e){ve(e.colorPicker),se(e.colorMenu,"show"),e.show(),le(e.menuToggle,"aria-expanded","true")}(e)}show(){const e=this;e.isOpen||(se(e.parent,"open"),be(e,!0),e.updateDropdownPosition(),e.isOpen=!0)}hide(e){const t=this;if(t.isOpen){const{pickerToggle:o,colorMenu:n}=t;be(t),ie(t.parent,"open"),ve(t.colorPicker),le(o,"aria-expanded","false"),n&&(ve(n),le(t.menuToggle,"aria-expanded","false")),t.isValid||(t.value=t.color.toString()),t.isOpen=!1,e||o.focus()}}dispose(){const{input:e,parent:t}=this;this.hide(!0),me(this),[...t.children].forEach(t=>{t!==e&&t.remove()}),re.remove(e,"color-picker")}}r(ke,{Color:R,getInstance:e=>{return t=e,o="color-picker",re.get(t,o);var t,o},init:e=>new ke(e),selector:'[data-function="color-picker"]'});class xe extends HTMLElement{constructor(){super(),this.colorPicker=null,this.input=n("input",this),this.isDisconnected=!0,this.attachShadow({mode:"open"})}get value(){return this.input.value}get color(){return this.colorPicker&&this.colorPicker.color}connectedCallback(){this.colorPicker?this.isDisconnected&&(this.isDisconnected=!1):(this.colorPicker=new ke(this.input),this.isDisconnected=!1,this.shadowRoot&&this.shadowRoot.append(a("slot")))}disconnectedCallback(){this.colorPicker&&this.colorPicker.dispose(),this.isDisconnected=!0}}r(xe,{Color:R,ColorPicker:ke}),customElements.define("color-picker",xe);export default xe;
1
+ /*!
2
+ * ColorPickerElement v0.0.1alpha2 (http://thednp.github.io/color-picker)
3
+ * Copyright 2022 © thednp
4
+ * Licensed under MIT (https://github.com/thednp/color-picker/blob/master/LICENSE)
5
+ */
6
+ /**
7
+ * Returns the `document` or the `#document` element.
8
+ * @see https://github.com/floating-ui/floating-ui
9
+ * @param {(Node | HTMLElement | Element | globalThis)=} node
10
+ * @returns {Document}
11
+ */
12
+ function getDocument(node) {
13
+ if (node instanceof HTMLElement) return node.ownerDocument;
14
+ if (node instanceof Window) return node.document;
15
+ return window.document;
16
+ }
17
+
18
+ /**
19
+ * A global array of possible `ParentNode`.
20
+ */
21
+ const parentNodes = [Document, Element, HTMLElement];
22
+
23
+ /**
24
+ * Shortcut for `HTMLElement.getElementsByTagName` method. Some `Node` elements
25
+ * like `ShadowRoot` do not support `getElementsByTagName`.
26
+ *
27
+ * @param {string} selector the tag name
28
+ * @param {(HTMLElement | Element | Document)=} parent optional Element to look into
29
+ * @return {HTMLCollectionOf<HTMLElement | Element>} the 'HTMLCollection'
30
+ */
31
+ function getElementsByTagName(selector, parent) {
32
+ const lookUp = parent && parentNodes
33
+ .some((x) => parent instanceof x) ? parent : getDocument();
34
+ return lookUp.getElementsByTagName(selector);
35
+ }
36
+
37
+ /**
38
+ * Shortcut for `Object.assign()` static method.
39
+ * @param {Record<string, any>} obj a target object
40
+ * @param {Record<string, any>} source a source object
41
+ */
42
+ const ObjectAssign = (obj, source) => Object.assign(obj, source);
43
+
44
+ /**
45
+ * This is a shortie for `document.createElement` method
46
+ * which allows you to create a new `HTMLElement` for a given `tagName`
47
+ * or based on an object with specific non-readonly attributes:
48
+ * `id`, `className`, `textContent`, `style`, etc.
49
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
50
+ *
51
+ * @param {Record<string, string> | string} param `tagName` or object
52
+ * @return {HTMLElement | Element} a new `HTMLElement` or `Element`
53
+ */
54
+ function createElement(param) {
55
+ if (typeof param === 'string') {
56
+ return getDocument().createElement(param);
57
+ }
58
+
59
+ const { tagName } = param;
60
+ const attr = { ...param };
61
+ const newElement = createElement(tagName);
62
+ delete attr.tagName;
63
+ ObjectAssign(newElement, attr);
64
+ return newElement;
65
+ }
66
+
67
+ /**
68
+ * Shortcut for `HTMLElement.setAttribute()` method.
69
+ * @param {HTMLElement | Element} element target element
70
+ * @param {string} attribute attribute name
71
+ * @param {string} value attribute value
72
+ * @returns {void}
73
+ */
74
+ const setAttribute = (element, attribute, value) => element.setAttribute(attribute, value);
75
+
76
+ /**
77
+ * Shortcut for `HTMLElement.getAttribute()` method.
78
+ * @param {HTMLElement | Element} element target element
79
+ * @param {string} attribute attribute name
80
+ * @returns {string?} attribute value
81
+ */
82
+ const getAttribute = (element, attribute) => element.getAttribute(attribute);
83
+
84
+ /**
85
+ * Returns the `document.head` or the `<head>` element.
86
+ *
87
+ * @param {(Node | HTMLElement | Element | globalThis)=} node
88
+ * @returns {HTMLElement | HTMLHeadElement}
89
+ */
90
+ function getDocumentHead(node) {
91
+ return getDocument(node).head;
92
+ }
93
+
94
+ /**
95
+ * Shortcut for `window.getComputedStyle(element).propertyName`
96
+ * static method.
97
+ *
98
+ * * If `element` parameter is not an `HTMLElement`, `getComputedStyle`
99
+ * throws a `ReferenceError`.
100
+ *
101
+ * @param {HTMLElement | Element} element target
102
+ * @param {string} property the css property
103
+ * @return {string} the css property value
104
+ */
105
+ function getElementStyle(element, property) {
106
+ const computedStyle = getComputedStyle(element);
107
+
108
+ // @ts-ignore -- must use camelcase strings,
109
+ // or non-camelcase strings with `getPropertyValue`
110
+ return property in computedStyle ? computedStyle[property] : '';
111
+ }
112
+
113
+ /**
114
+ * Shortcut for multiple uses of `HTMLElement.style.propertyName` method.
115
+ * @param {HTMLElement | Element} element target element
116
+ * @param {Partial<CSSStyleDeclaration>} styles attribute value
117
+ */
118
+ // @ts-ignore
119
+ const setElementStyle = (element, styles) => { ObjectAssign(element.style, styles); };
120
+
121
+ /**
122
+ * A list of explicit default non-color values.
123
+ */
124
+ const nonColors = ['transparent', 'currentColor', 'inherit', 'revert', 'initial'];
125
+
126
+ // Color supported formats
127
+ const COLOR_FORMAT = ['rgb', 'hex', 'hsl', 'hsb', 'hwb'];
128
+
129
+ // Hue angles
130
+ const ANGLES = 'deg|rad|grad|turn';
131
+
132
+ // <http://www.w3.org/TR/css3-values/#integers>
133
+ const CSS_INTEGER = '[-\\+]?\\d+%?';
134
+
135
+ // Include CSS3 Module
136
+ // <http://www.w3.org/TR/css3-values/#number-value>
137
+ const CSS_NUMBER = '[-\\+]?\\d*\\.\\d+%?';
138
+
139
+ // Include CSS4 Module Hue degrees unit
140
+ // <https://www.w3.org/TR/css3-values/#angle-value>
141
+ const CSS_ANGLE = `[-\\+]?\\d*\\.?\\d+(?:${ANGLES})?`;
142
+
143
+ // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
144
+ const CSS_UNIT = `(?:${CSS_NUMBER})|(?:${CSS_INTEGER})`;
145
+
146
+ // Add angles to the mix
147
+ const CSS_UNIT2 = `(?:${CSS_UNIT})|(?:${CSS_ANGLE})`;
148
+
149
+ // Actual matching.
150
+ // Parentheses and commas are optional, but not required.
151
+ // Whitespace can take the place of commas or opening paren
152
+ const PERMISSIVE_MATCH = `[\\s|\\(]+(${CSS_UNIT2})[,|\\s]+(${CSS_UNIT})[,|\\s]+(${CSS_UNIT})[,|\\s|\\/\\s]*(${CSS_UNIT})?\\s*\\)?`;
153
+
154
+ const matchers = {
155
+ CSS_UNIT: new RegExp(CSS_UNIT2),
156
+ hwb: new RegExp(`hwb${PERMISSIVE_MATCH}`),
157
+ rgb: new RegExp(`rgb(?:a)?${PERMISSIVE_MATCH}`),
158
+ hsl: new RegExp(`hsl(?:a)?${PERMISSIVE_MATCH}`),
159
+ hsv: new RegExp(`hsv(?:a)?${PERMISSIVE_MATCH}`),
160
+ hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
161
+ hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
162
+ hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
163
+ hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
164
+ };
165
+
166
+ /**
167
+ * Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
168
+ * <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
169
+ * @param {string} n testing number
170
+ * @returns {boolean} the query result
171
+ */
172
+ function isOnePointZero(n) {
173
+ return `${n}`.includes('.') && parseFloat(n) === 1;
174
+ }
175
+
176
+ /**
177
+ * Check to see if string passed in is a percentage
178
+ * @param {string} n testing number
179
+ * @returns {boolean} the query result
180
+ */
181
+ function isPercentage(n) {
182
+ return `${n}`.includes('%');
183
+ }
184
+
185
+ /**
186
+ * Check to see if string passed in is an angle
187
+ * @param {string} n testing string
188
+ * @returns {boolean} the query result
189
+ */
190
+ function isAngle(n) {
191
+ return ANGLES.split('|').some((a) => `${n}`.includes(a));
192
+ }
193
+
194
+ /**
195
+ * Check to see if string passed is a web safe colour.
196
+ * @param {string} color a colour name, EG: *red*
197
+ * @returns {boolean} the query result
198
+ */
199
+ function isColorName(color) {
200
+ return !['#', ...COLOR_FORMAT].some((s) => color.includes(s))
201
+ && !/[0-9]/.test(color);
202
+ }
203
+
204
+ /**
205
+ * Check to see if it looks like a CSS unit
206
+ * (see `matchers` above for definition).
207
+ * @param {string | number} color testing value
208
+ * @returns {boolean} the query result
209
+ */
210
+ function isValidCSSUnit(color) {
211
+ return Boolean(matchers.CSS_UNIT.exec(String(color)));
212
+ }
213
+
214
+ /**
215
+ * Take input from [0, n] and return it as [0, 1]
216
+ * @param {*} N the input number
217
+ * @param {number} max the number maximum value
218
+ * @returns {number} the number in [0, 1] value range
219
+ */
220
+ function bound01(N, max) {
221
+ let n = N;
222
+ if (isOnePointZero(n)) n = '100%';
223
+
224
+ n = max === 360 ? n : Math.min(max, Math.max(0, parseFloat(n)));
225
+
226
+ // Handle hue angles
227
+ if (isAngle(N)) n = N.replace(new RegExp(ANGLES), '');
228
+
229
+ // Automatically convert percentage into number
230
+ if (isPercentage(n)) n = parseInt(String(n * max), 10) / 100;
231
+
232
+ // Handle floating point rounding errors
233
+ if (Math.abs(n - max) < 0.000001) {
234
+ return 1;
235
+ }
236
+ // Convert into [0, 1] range if it isn't already
237
+ if (max === 360) {
238
+ // If n is a hue given in degrees,
239
+ // wrap around out-of-range values into [0, 360] range
240
+ // then convert into [0, 1].
241
+ n = (n < 0 ? (n % max) + max : n % max) / parseFloat(String(max));
242
+ } else {
243
+ // If n not a hue given in degrees
244
+ // Convert into [0, 1] range if it isn't already.
245
+ n = (n % max) / parseFloat(String(max));
246
+ }
247
+ return n;
248
+ }
249
+
250
+ /**
251
+ * Return a valid alpha value [0,1] with all invalid values being set to 1.
252
+ * @param {string | number} a transparency value
253
+ * @returns {number} a transparency value in the [0, 1] range
254
+ */
255
+ function boundAlpha(a) {
256
+ let na = parseFloat(`${a}`);
257
+
258
+ if (Number.isNaN(na) || na < 0 || na > 1) {
259
+ na = 1;
260
+ }
261
+
262
+ return na;
263
+ }
264
+
265
+ /**
266
+ * Force a number between 0 and 1.
267
+ * @param {number} v the float number
268
+ * @returns {number} - the resulting number
269
+ */
270
+ function clamp01(v) {
271
+ return Math.min(1, Math.max(0, v));
272
+ }
273
+
274
+ /**
275
+ * Returns the hexadecimal value of a web safe colour.
276
+ * @param {string} name
277
+ * @returns {string}
278
+ */
279
+ function getRGBFromName(name) {
280
+ const documentHead = getDocumentHead();
281
+ setElementStyle(documentHead, { color: name });
282
+ const colorName = getElementStyle(documentHead, 'color');
283
+ setElementStyle(documentHead, { color: '' });
284
+ return colorName;
285
+ }
286
+
287
+ /**
288
+ * Converts a decimal value to hexadecimal.
289
+ * @param {number} d the input number
290
+ * @returns {string} - the hexadecimal value
291
+ */
292
+ function convertDecimalToHex(d) {
293
+ return Math.round(d * 255).toString(16);
294
+ }
295
+
296
+ /**
297
+ * Converts a hexadecimal value to decimal.
298
+ * @param {string} h hexadecimal value
299
+ * @returns {number} number in decimal format
300
+ */
301
+ function convertHexToDecimal(h) {
302
+ return parseIntFromHex(h) / 255;
303
+ }
304
+
305
+ /**
306
+ * Converts a base-16 hexadecimal value into a base-10 integer.
307
+ * @param {string} val
308
+ * @returns {number}
309
+ */
310
+ function parseIntFromHex(val) {
311
+ return parseInt(val, 16);
312
+ }
313
+
314
+ /**
315
+ * Force a hexadecimal value to have 2 characters.
316
+ * @param {string} c string with [0-9A-F] ranged values
317
+ * @returns {string} 0 => 00, a => 0a
318
+ */
319
+ function pad2(c) {
320
+ return c.length === 1 ? `0${c}` : String(c);
321
+ }
322
+
323
+ /**
324
+ * Converts an RGB colour value to HSL.
325
+ *
326
+ * @param {number} R Red component [0, 255]
327
+ * @param {number} G Green component [0, 255]
328
+ * @param {number} B Blue component [0, 255]
329
+ * @returns {CP.HSL} {h,s,l} object with [0, 1] ranged values
330
+ */
331
+ function rgbToHsl(R, G, B) {
332
+ const r = R / 255;
333
+ const g = G / 255;
334
+ const b = B / 255;
335
+ const max = Math.max(r, g, b);
336
+ const min = Math.min(r, g, b);
337
+ let h = 0;
338
+ let s = 0;
339
+ const l = (max + min) / 2;
340
+ if (max === min) {
341
+ s = 0;
342
+ h = 0; // achromatic
343
+ } else {
344
+ const d = max - min;
345
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
346
+ switch (max) {
347
+ case r:
348
+ h = (g - b) / d + (g < b ? 6 : 0);
349
+ break;
350
+ case g:
351
+ h = (b - r) / d + 2;
352
+ break;
353
+ case b:
354
+ h = (r - g) / d + 4;
355
+ break;
356
+ }
357
+ h /= 6;
358
+ }
359
+ return { h, s, l };
360
+ }
361
+
362
+ /**
363
+ * Returns a normalized RGB component value.
364
+ * @param {number} p
365
+ * @param {number} q
366
+ * @param {number} t
367
+ * @returns {number}
368
+ */
369
+ function hueToRgb(p, q, t) {
370
+ let T = t;
371
+ if (T < 0) T += 1;
372
+ if (T > 1) T -= 1;
373
+ if (T < 1 / 6) return p + (q - p) * (6 * T);
374
+ if (T < 1 / 2) return q;
375
+ if (T < 2 / 3) return p + (q - p) * (2 / 3 - T) * 6;
376
+ return p;
377
+ }
378
+
379
+ /**
380
+ * Returns an HWB colour object from an RGB colour object.
381
+ * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
382
+ * @link http://alvyray.com/Papers/CG/hwb2rgb.htm
383
+ *
384
+ * @param {number} R Red component [0, 255]
385
+ * @param {number} G Green [0, 255]
386
+ * @param {number} B Blue [0, 255]
387
+ * @return {CP.HWB} {h,w,b} object with [0, 1] ranged values
388
+ */
389
+ function rgbToHwb(R, G, B) {
390
+ const r = R / 255;
391
+ const g = G / 255;
392
+ const b = B / 255;
393
+
394
+ let f = 0;
395
+ let i = 0;
396
+ const whiteness = Math.min(r, g, b);
397
+ const max = Math.max(r, g, b);
398
+ const black = 1 - max;
399
+
400
+ if (max === whiteness) return { h: 0, w: whiteness, b: black };
401
+ if (r === whiteness) {
402
+ f = g - b;
403
+ i = 3;
404
+ } else {
405
+ f = g === whiteness ? b - r : r - g;
406
+ i = g === whiteness ? 5 : 1;
407
+ }
408
+
409
+ const h = (i - f / (max - whiteness)) / 6;
410
+ return {
411
+ h: h === 1 ? 0 : h,
412
+ w: whiteness,
413
+ b: black,
414
+ };
415
+ }
416
+
417
+ /**
418
+ * Returns an RGB colour object from an HWB colour.
419
+ *
420
+ * @param {number} H Hue Angle [0, 1]
421
+ * @param {number} W Whiteness [0, 1]
422
+ * @param {number} B Blackness [0, 1]
423
+ * @return {CP.RGB} {r,g,b} object with [0, 255] ranged values
424
+ *
425
+ * @link https://www.w3.org/TR/css-color-4/#hwb-to-rgb
426
+ * @link http://alvyray.com/Papers/CG/hwb2rgb.htm
427
+ */
428
+ function hwbToRgb(H, W, B) {
429
+ if (W + B >= 1) {
430
+ const gray = (W / (W + B)) * 255;
431
+ return { r: gray, g: gray, b: gray };
432
+ }
433
+ let { r, g, b } = hslToRgb(H, 1, 0.5);
434
+ [r, g, b] = [r, g, b]
435
+ .map((v) => (v / 255) * (1 - W - B) + W)
436
+ .map((v) => v * 255);
437
+
438
+ return { r, g, b };
439
+ }
440
+
441
+ /**
442
+ * Converts an HSL colour value to RGB.
443
+ *
444
+ * @param {number} h Hue Angle [0, 1]
445
+ * @param {number} s Saturation [0, 1]
446
+ * @param {number} l Lightness Angle [0, 1]
447
+ * @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
448
+ */
449
+ function hslToRgb(h, s, l) {
450
+ let r = 0;
451
+ let g = 0;
452
+ let b = 0;
453
+
454
+ if (s === 0) {
455
+ // achromatic
456
+ g = l;
457
+ b = l;
458
+ r = l;
459
+ } else {
460
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
461
+ const p = 2 * l - q;
462
+ r = hueToRgb(p, q, h + 1 / 3);
463
+ g = hueToRgb(p, q, h);
464
+ b = hueToRgb(p, q, h - 1 / 3);
465
+ }
466
+ [r, g, b] = [r, g, b].map((x) => x * 255);
467
+
468
+ return { r, g, b };
469
+ }
470
+
471
+ /**
472
+ * Converts an RGB colour value to HSV.
473
+ *
474
+ * @param {number} R Red component [0, 255]
475
+ * @param {number} G Green [0, 255]
476
+ * @param {number} B Blue [0, 255]
477
+ * @returns {CP.HSV} {h,s,v} object with [0, 1] ranged values
478
+ */
479
+ function rgbToHsv(R, G, B) {
480
+ const r = R / 255;
481
+ const g = G / 255;
482
+ const b = B / 255;
483
+ const max = Math.max(r, g, b);
484
+ const min = Math.min(r, g, b);
485
+ let h = 0;
486
+ const v = max;
487
+ const d = max - min;
488
+ const s = max === 0 ? 0 : d / max;
489
+ if (max === min) {
490
+ h = 0; // achromatic
491
+ } else {
492
+ switch (max) {
493
+ case r:
494
+ h = (g - b) / d + (g < b ? 6 : 0);
495
+ break;
496
+ case g:
497
+ h = (b - r) / d + 2;
498
+ break;
499
+ case b:
500
+ h = (r - g) / d + 4;
501
+ break;
502
+ }
503
+ h /= 6;
504
+ }
505
+ return { h, s, v };
506
+ }
507
+
508
+ /**
509
+ * Converts an HSV colour value to RGB.
510
+ *
511
+ * @param {number} H Hue Angle [0, 1]
512
+ * @param {number} S Saturation [0, 1]
513
+ * @param {number} V Brightness Angle [0, 1]
514
+ * @returns {CP.RGB} {r,g,b} object with [0, 1] ranged values
515
+ */
516
+ function hsvToRgb(H, S, V) {
517
+ const h = H * 6;
518
+ const s = S;
519
+ const v = V;
520
+ const i = Math.floor(h);
521
+ const f = h - i;
522
+ const p = v * (1 - s);
523
+ const q = v * (1 - f * s);
524
+ const t = v * (1 - (1 - f) * s);
525
+ const mod = i % 6;
526
+ const r = [v, q, p, p, t, v][mod];
527
+ const g = [t, v, v, q, p, p][mod];
528
+ const b = [p, p, t, v, v, q][mod];
529
+ return { r: r * 255, g: g * 255, b: b * 255 };
530
+ }
531
+
532
+ /**
533
+ * Converts an RGB colour to hex
534
+ *
535
+ * Assumes r, g, and b are contained in the set [0, 255]
536
+ * Returns a 3 or 6 character hex
537
+ * @param {number} r Red component [0, 255]
538
+ * @param {number} g Green [0, 255]
539
+ * @param {number} b Blue [0, 255]
540
+ * @param {boolean=} allow3Char
541
+ * @returns {string}
542
+ */
543
+ function rgbToHex(r, g, b, allow3Char) {
544
+ const hex = [
545
+ pad2(Math.round(r).toString(16)),
546
+ pad2(Math.round(g).toString(16)),
547
+ pad2(Math.round(b).toString(16)),
548
+ ];
549
+
550
+ // Return a 3 character hex if possible
551
+ if (allow3Char && hex[0].charAt(0) === hex[0].charAt(1)
552
+ && hex[1].charAt(0) === hex[1].charAt(1)
553
+ && hex[2].charAt(0) === hex[2].charAt(1)) {
554
+ return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
555
+ }
556
+
557
+ return hex.join('');
558
+ }
559
+
560
+ /**
561
+ * Converts an RGBA color plus alpha transparency to hex8.
562
+ *
563
+ * @param {number} r Red component [0, 255]
564
+ * @param {number} g Green [0, 255]
565
+ * @param {number} b Blue [0, 255]
566
+ * @param {number} a Alpha transparency [0, 1]
567
+ * @param {boolean=} allow4Char when *true* it will also find hex shorthand
568
+ * @returns {string} a hexadecimal value with alpha transparency
569
+ */
570
+ function rgbaToHex(r, g, b, a, allow4Char) {
571
+ const hex = [
572
+ pad2(Math.round(r).toString(16)),
573
+ pad2(Math.round(g).toString(16)),
574
+ pad2(Math.round(b).toString(16)),
575
+ pad2(convertDecimalToHex(a)),
576
+ ];
577
+
578
+ // Return a 4 character hex if possible
579
+ if (allow4Char && hex[0].charAt(0) === hex[0].charAt(1)
580
+ && hex[1].charAt(0) === hex[1].charAt(1)
581
+ && hex[2].charAt(0) === hex[2].charAt(1)
582
+ && hex[3].charAt(0) === hex[3].charAt(1)) {
583
+ return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0) + hex[3].charAt(0);
584
+ }
585
+ return hex.join('');
586
+ }
587
+
588
+ /**
589
+ * Returns a colour object corresponding to a given number.
590
+ * @param {number} color input number
591
+ * @returns {CP.RGB} {r,g,b} object with [0, 255] ranged values
592
+ */
593
+ function numberInputToObject(color) {
594
+ /* eslint-disable no-bitwise */
595
+ return {
596
+ r: color >> 16,
597
+ g: (color & 0xff00) >> 8,
598
+ b: color & 0xff,
599
+ };
600
+ /* eslint-enable no-bitwise */
601
+ }
602
+
603
+ /**
604
+ * Permissive string parsing. Take in a number of formats, and output an object
605
+ * based on detected format. Returns {r,g,b} or {h,s,l} or {h,s,v}
606
+ * @param {string} input colour value in any format
607
+ * @returns {Record<string, (number | string)> | false} an object matching the RegExp
608
+ */
609
+ function stringInputToObject(input) {
610
+ let color = input.trim().toLowerCase();
611
+ if (color.length === 0) {
612
+ return {
613
+ r: 0, g: 0, b: 0, a: 0,
614
+ };
615
+ }
616
+ let named = false;
617
+ if (isColorName(color)) {
618
+ color = getRGBFromName(color);
619
+ named = true;
620
+ } else if (nonColors.includes(color)) {
621
+ const isTransparent = color === 'transparent';
622
+ const rgb = isTransparent ? 0 : 255;
623
+ const a = isTransparent ? 0 : 1;
624
+ return {
625
+ r: rgb, g: rgb, b: rgb, a, format: 'rgb',
626
+ };
627
+ }
628
+
629
+ // Try to match string input using regular expressions.
630
+ // Keep most of the number bounding out of this function,
631
+ // don't worry about [0,1] or [0,100] or [0,360]
632
+ // Just return an object and let the conversion functions handle that.
633
+ // This way the result will be the same whether Color is initialized with string or object.
634
+ let [, m1, m2, m3, m4] = matchers.rgb.exec(color) || [];
635
+ if (m1 && m2 && m3/* && m4 */) {
636
+ return {
637
+ r: m1, g: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'rgb',
638
+ };
639
+ }
640
+ [, m1, m2, m3, m4] = matchers.hsl.exec(color) || [];
641
+ if (m1 && m2 && m3/* && m4 */) {
642
+ return {
643
+ h: m1, s: m2, l: m3, a: m4 !== undefined ? m4 : 1, format: 'hsl',
644
+ };
645
+ }
646
+ [, m1, m2, m3, m4] = matchers.hsv.exec(color) || [];
647
+ if (m1 && m2 && m3/* && m4 */) {
648
+ return {
649
+ h: m1, s: m2, v: m3, a: m4 !== undefined ? m4 : 1, format: 'hsv',
650
+ };
651
+ }
652
+ [, m1, m2, m3, m4] = matchers.hwb.exec(color) || [];
653
+ if (m1 && m2 && m3) {
654
+ return {
655
+ h: m1, w: m2, b: m3, a: m4 !== undefined ? m4 : 1, format: 'hwb',
656
+ };
657
+ }
658
+ [, m1, m2, m3, m4] = matchers.hex8.exec(color) || [];
659
+ if (m1 && m2 && m3 && m4) {
660
+ return {
661
+ r: parseIntFromHex(m1),
662
+ g: parseIntFromHex(m2),
663
+ b: parseIntFromHex(m3),
664
+ a: convertHexToDecimal(m4),
665
+ // format: named ? 'rgb' : 'hex8',
666
+ format: named ? 'rgb' : 'hex',
667
+ };
668
+ }
669
+ [, m1, m2, m3] = matchers.hex6.exec(color) || [];
670
+ if (m1 && m2 && m3) {
671
+ return {
672
+ r: parseIntFromHex(m1),
673
+ g: parseIntFromHex(m2),
674
+ b: parseIntFromHex(m3),
675
+ format: named ? 'rgb' : 'hex',
676
+ };
677
+ }
678
+ [, m1, m2, m3, m4] = matchers.hex4.exec(color) || [];
679
+ if (m1 && m2 && m3 && m4) {
680
+ return {
681
+ r: parseIntFromHex(m1 + m1),
682
+ g: parseIntFromHex(m2 + m2),
683
+ b: parseIntFromHex(m3 + m3),
684
+ a: convertHexToDecimal(m4 + m4),
685
+ // format: named ? 'rgb' : 'hex8',
686
+ format: named ? 'rgb' : 'hex',
687
+ };
688
+ }
689
+ [, m1, m2, m3] = matchers.hex3.exec(color) || [];
690
+ if (m1 && m2 && m3) {
691
+ return {
692
+ r: parseIntFromHex(m1 + m1),
693
+ g: parseIntFromHex(m2 + m2),
694
+ b: parseIntFromHex(m3 + m3),
695
+ format: named ? 'rgb' : 'hex',
696
+ };
697
+ }
698
+ return false;
699
+ }
700
+
701
+ /**
702
+ * Given a string or object, convert that input to RGB
703
+ *
704
+ * Possible string inputs:
705
+ * ```
706
+ * "red"
707
+ * "#f00" or "f00"
708
+ * "#ff0000" or "ff0000"
709
+ * "#ff000000" or "ff000000" // CSS4 Module
710
+ * "rgb 255 0 0" or "rgb (255, 0, 0)"
711
+ * "rgb 1.0 0 0" or "rgb (1, 0, 0)"
712
+ * "rgba(255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
713
+ * "rgba(1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
714
+ * "rgb(255 0 0 / 10%)" or "rgb 255 0 0 0.1" // CSS4 Module
715
+ * "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
716
+ * "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
717
+ * "hsl(0deg 100% 50% / 50%)" or "hsl 0 100 50 50" // CSS4 Module
718
+ * "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
719
+ * "hsva(0, 100%, 100%, 0.1)" or "hsva 0 100% 100% 0.1"
720
+ * "hsv(0deg 100% 100% / 10%)" or "hsv 0 100 100 0.1" // CSS4 Module
721
+ * "hwb(0deg, 100%, 100%, 100%)" or "hwb 0 100% 100% 0.1" // CSS4 Module
722
+ * ```
723
+ * @param {string | Record<string, any>} input
724
+ * @returns {CP.ColorObject}
725
+ */
726
+ function inputToRGB(input) {
727
+ let rgb = { r: 0, g: 0, b: 0 };
728
+ let color = input;
729
+ let a = 1;
730
+ let s = null;
731
+ let v = null;
732
+ let l = null;
733
+ let w = null;
734
+ let b = null;
735
+ let h = null;
736
+ let ok = false;
737
+ let format = 'hex';
738
+
739
+ if (typeof input === 'string') {
740
+ // @ts-ignore -- this now is converted to object
741
+ color = stringInputToObject(input);
742
+ if (color) ok = true;
743
+ }
744
+ if (typeof color === 'object') {
745
+ if (isValidCSSUnit(color.r) && isValidCSSUnit(color.g) && isValidCSSUnit(color.b)) {
746
+ rgb = { r: color.r, g: color.g, b: color.b }; // RGB values in [0, 255] range
747
+ ok = true;
748
+ format = 'rgb';
749
+ } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.v)) {
750
+ ({ h, s, v } = color);
751
+ h = typeof h === 'number' ? h : bound01(h, 360); // hue can be `5deg` or a [0, 1] value
752
+ s = typeof s === 'number' ? s : bound01(s, 100); // saturation can be `5%` or a [0, 1] value
753
+ v = typeof v === 'number' ? v : bound01(v, 100); // brightness can be `5%` or a [0, 1] value
754
+ rgb = hsvToRgb(h, s, v);
755
+ ok = true;
756
+ format = 'hsv';
757
+ } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.s) && isValidCSSUnit(color.l)) {
758
+ ({ h, s, l } = color);
759
+ h = typeof h === 'number' ? h : bound01(h, 360); // hue can be `5deg` or a [0, 1] value
760
+ s = typeof s === 'number' ? s : bound01(s, 100); // saturation can be `5%` or a [0, 1] value
761
+ l = typeof l === 'number' ? l : bound01(l, 100); // lightness can be `5%` or a [0, 1] value
762
+ rgb = hslToRgb(h, s, l);
763
+ ok = true;
764
+ format = 'hsl';
765
+ } else if (isValidCSSUnit(color.h) && isValidCSSUnit(color.w) && isValidCSSUnit(color.b)) {
766
+ ({ h, w, b } = color);
767
+ h = typeof h === 'number' ? h : bound01(h, 360); // hue can be `5deg` or a [0, 1] value
768
+ w = typeof w === 'number' ? w : bound01(w, 100); // whiteness can be `5%` or a [0, 1] value
769
+ b = typeof b === 'number' ? b : bound01(b, 100); // blackness can be `5%` or a [0, 1] value
770
+ rgb = hwbToRgb(h, w, b);
771
+ ok = true;
772
+ format = 'hwb';
773
+ }
774
+ if (isValidCSSUnit(color.a)) {
775
+ a = color.a;
776
+ a = isPercentage(`${a}`) ? bound01(a, 100) : a;
777
+ }
778
+ }
779
+
780
+ return {
781
+ ok, // @ts-ignore
782
+ format: color.format || format,
783
+ r: Math.min(255, Math.max(rgb.r, 0)),
784
+ g: Math.min(255, Math.max(rgb.g, 0)),
785
+ b: Math.min(255, Math.max(rgb.b, 0)),
786
+ a: boundAlpha(a),
787
+ };
788
+ }
789
+
790
+ /**
791
+ * @class
792
+ * Returns a new `Color` instance.
793
+ * @see https://github.com/bgrins/TinyColor
794
+ */
795
+ class Color {
796
+ /**
797
+ * @constructor
798
+ * @param {CP.ColorInput} input the given colour value
799
+ * @param {CP.ColorFormats=} config the given format
800
+ */
801
+ constructor(input, config) {
802
+ let color = input;
803
+ const configFormat = config && COLOR_FORMAT.includes(config)
804
+ ? config : 'rgb';
805
+
806
+ // If input is already a `Color`, return itself
807
+ if (color instanceof Color) {
808
+ color = inputToRGB(color);
809
+ }
810
+ if (typeof color === 'number') {
811
+ color = numberInputToObject(color);
812
+ }
813
+ const {
814
+ r, g, b, a, ok, format,
815
+ } = inputToRGB(color);
816
+
817
+ // bind
818
+ const self = this;
819
+
820
+ /** @type {CP.ColorInput} */
821
+ self.originalInput = color;
822
+ /** @type {number} */
823
+ self.r = r;
824
+ /** @type {number} */
825
+ self.g = g;
826
+ /** @type {number} */
827
+ self.b = b;
828
+ /** @type {number} */
829
+ self.a = a;
830
+ /** @type {boolean} */
831
+ self.ok = ok;
832
+ /** @type {CP.ColorFormats} */
833
+ self.format = configFormat || format;
834
+
835
+ // Don't let the range of [0,255] come back in [0,1].
836
+ // Potentially lose a little bit of precision here, but will fix issues where
837
+ // .5 gets interpreted as half of the total, instead of half of 1
838
+ // If it was supposed to be 128, this was already taken care of by `inputToRgb`
839
+ if (r < 1) self.r = Math.round(r);
840
+ if (g < 1) self.g = Math.round(g);
841
+ if (b < 1) self.b = Math.round(b);
842
+ }
843
+
844
+ /**
845
+ * Checks if the current input value is a valid colour.
846
+ * @returns {boolean} the query result
847
+ */
848
+ get isValid() {
849
+ return this.ok;
850
+ }
851
+
852
+ /**
853
+ * Checks if the current colour requires a light text colour.
854
+ * @returns {boolean} the query result
855
+ */
856
+ get isDark() {
857
+ return this.brightness < 128;
858
+ }
859
+
860
+ /**
861
+ * Returns the perceived luminance of a colour.
862
+ * @see http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
863
+ * @returns {number} a number in the [0, 1] range
864
+ */
865
+ get luminance() {
866
+ const { r, g, b } = this;
867
+ let R = 0;
868
+ let G = 0;
869
+ let B = 0;
870
+ const rp = r / 255;
871
+ const rg = g / 255;
872
+ const rb = b / 255;
873
+
874
+ if (rp <= 0.03928) {
875
+ R = rp / 12.92;
876
+ } else {
877
+ R = ((rp + 0.055) / 1.055) ** 2.4;
878
+ }
879
+ if (rg <= 0.03928) {
880
+ G = rg / 12.92;
881
+ } else {
882
+ G = ((rg + 0.055) / 1.055) ** 2.4;
883
+ }
884
+ if (rb <= 0.03928) {
885
+ B = rb / 12.92;
886
+ } else {
887
+ B = ((rb + 0.055) / 1.055) ** 2.4;
888
+ }
889
+ return 0.2126 * R + 0.7152 * G + 0.0722 * B;
890
+ }
891
+
892
+ /**
893
+ * Returns the perceived brightness of the colour.
894
+ * @returns {number} a number in the [0, 255] range
895
+ */
896
+ get brightness() {
897
+ const { r, g, b } = this;
898
+ return (r * 299 + g * 587 + b * 114) / 1000;
899
+ }
900
+
901
+ /**
902
+ * Returns the colour as an RGBA object.
903
+ * @returns {CP.RGBA} an {r,g,b,a} object with [0, 255] ranged values
904
+ */
905
+ toRgb() {
906
+ const {
907
+ r, g, b, a,
908
+ } = this;
909
+ const [R, G, B] = [r, g, b].map((x) => Math.round(x));
910
+
911
+ return {
912
+ r: R,
913
+ g: G,
914
+ b: B,
915
+ a: Math.round(a * 100) / 100,
916
+ };
917
+ }
918
+
919
+ /**
920
+ * Returns the RGBA values concatenated into a CSS3 Module string format.
921
+ * * rgb(255,255,255)
922
+ * * rgba(255,255,255,0.5)
923
+ * @returns {string} the CSS valid colour in RGB/RGBA format
924
+ */
925
+ toRgbString() {
926
+ const {
927
+ r, g, b, a,
928
+ } = this.toRgb();
929
+
930
+ return a === 1
931
+ ? `rgb(${r}, ${g}, ${b})`
932
+ : `rgba(${r}, ${g}, ${b}, ${a})`;
933
+ }
934
+
935
+ /**
936
+ * Returns the RGBA values concatenated into a CSS4 Module string format.
937
+ * * rgb(255 255 255)
938
+ * * rgb(255 255 255 / 50%)
939
+ * @returns {string} the CSS valid colour in CSS4 RGB format
940
+ */
941
+ toRgbCSS4String() {
942
+ const {
943
+ r, g, b, a,
944
+ } = this.toRgb();
945
+ const A = a === 1 ? '' : ` / ${Math.round(a * 100)}%`;
946
+
947
+ return `rgb(${r} ${g} ${b}${A})`;
948
+ }
949
+
950
+ /**
951
+ * Returns the hexadecimal value of the colour. When the parameter is *true*
952
+ * it will find a 3 characters shorthand of the decimal value.
953
+ *
954
+ * @param {boolean=} allow3Char when `true` returns shorthand HEX
955
+ * @returns {string} the hexadecimal colour format
956
+ */
957
+ toHex(allow3Char) {
958
+ const {
959
+ r, g, b, a,
960
+ } = this.toRgb();
961
+
962
+ return a === 1
963
+ ? rgbToHex(r, g, b, allow3Char)
964
+ : rgbaToHex(r, g, b, a, allow3Char);
965
+ }
966
+
967
+ /**
968
+ * Returns the CSS valid hexadecimal vaue of the colour. When the parameter is *true*
969
+ * it will find a 3 characters shorthand of the value.
970
+ *
971
+ * @param {boolean=} allow3Char when `true` returns shorthand HEX
972
+ * @returns {string} the CSS valid colour in hexadecimal format
973
+ */
974
+ toHexString(allow3Char) {
975
+ return `#${this.toHex(allow3Char)}`;
976
+ }
977
+
978
+ /**
979
+ * Returns the HEX8 value of the colour.
980
+ * @param {boolean=} allow4Char when `true` returns shorthand HEX
981
+ * @returns {string} the CSS valid colour in hexadecimal format
982
+ */
983
+ toHex8(allow4Char) {
984
+ const {
985
+ r, g, b, a,
986
+ } = this.toRgb();
987
+
988
+ return rgbaToHex(r, g, b, a, allow4Char);
989
+ }
990
+
991
+ /**
992
+ * Returns the HEX8 value of the colour.
993
+ * @param {boolean=} allow4Char when `true` returns shorthand HEX
994
+ * @returns {string} the CSS valid colour in hexadecimal format
995
+ */
996
+ toHex8String(allow4Char) {
997
+ return `#${this.toHex8(allow4Char)}`;
998
+ }
999
+
1000
+ /**
1001
+ * Returns the colour as a HSVA object.
1002
+ * @returns {CP.HSVA} the `{h,s,v,a}` object with [0, 1] ranged values
1003
+ */
1004
+ toHsv() {
1005
+ const {
1006
+ r, g, b, a,
1007
+ } = this.toRgb();
1008
+ const { h, s, v } = rgbToHsv(r, g, b);
1009
+
1010
+ return {
1011
+ h, s, v, a,
1012
+ };
1013
+ }
1014
+
1015
+ /**
1016
+ * Returns the colour as an HSLA object.
1017
+ * @returns {CP.HSLA} the `{h,s,l,a}` object with [0, 1] ranged values
1018
+ */
1019
+ toHsl() {
1020
+ const {
1021
+ r, g, b, a,
1022
+ } = this.toRgb();
1023
+ const { h, s, l } = rgbToHsl(r, g, b);
1024
+
1025
+ return {
1026
+ h, s, l, a,
1027
+ };
1028
+ }
1029
+
1030
+ /**
1031
+ * Returns the HSLA values concatenated into a CSS3 Module format string.
1032
+ * * `hsl(150, 100%, 50%)`
1033
+ * * `hsla(150, 100%, 50%, 0.5)`
1034
+ * @returns {string} the CSS valid colour in HSL/HSLA format
1035
+ */
1036
+ toHslString() {
1037
+ let {
1038
+ h, s, l, a,
1039
+ } = this.toHsl();
1040
+ h = Math.round(h * 360);
1041
+ s = Math.round(s * 100);
1042
+ l = Math.round(l * 100);
1043
+ a = Math.round(a * 100) / 100;
1044
+
1045
+ return a === 1
1046
+ ? `hsl(${h}, ${s}%, ${l}%)`
1047
+ : `hsla(${h}, ${s}%, ${l}%, ${a})`;
1048
+ }
1049
+
1050
+ /**
1051
+ * Returns the HSLA values concatenated into a CSS4 Module format string.
1052
+ * * `hsl(150deg 100% 50%)`
1053
+ * * `hsl(150deg 100% 50% / 50%)`
1054
+ * @returns {string} the CSS valid colour in CSS4 HSL format
1055
+ */
1056
+ toHslCSS4String() {
1057
+ let {
1058
+ h, s, l, a,
1059
+ } = this.toHsl();
1060
+ h = Math.round(h * 360);
1061
+ s = Math.round(s * 100);
1062
+ l = Math.round(l * 100);
1063
+ a = Math.round(a * 100);
1064
+ const A = a < 100 ? ` / ${Math.round(a)}%` : '';
1065
+
1066
+ return `hsl(${h}deg ${s}% ${l}%${A})`;
1067
+ }
1068
+
1069
+ /**
1070
+ * Returns the colour as an HWBA object.
1071
+ * @returns {CP.HWBA} the `{h,w,b,a}` object with [0, 1] ranged values
1072
+ */
1073
+ toHwb() {
1074
+ const {
1075
+ r, g, b, a,
1076
+ } = this;
1077
+ const { h, w, b: bl } = rgbToHwb(r, g, b);
1078
+ return {
1079
+ h, w, b: bl, a,
1080
+ };
1081
+ }
1082
+
1083
+ /**
1084
+ * Returns the HWBA values concatenated into a string.
1085
+ * @returns {string} the CSS valid colour in HWB format
1086
+ */
1087
+ toHwbString() {
1088
+ let {
1089
+ h, w, b, a,
1090
+ } = this.toHwb();
1091
+ h = Math.round(h * 360);
1092
+ w = Math.round(w * 100);
1093
+ b = Math.round(b * 100);
1094
+ a = Math.round(a * 100);
1095
+ const A = a < 100 ? ` / ${Math.round(a)}%` : '';
1096
+
1097
+ return `hwb(${h}deg ${w}% ${b}%${A})`;
1098
+ }
1099
+
1100
+ /**
1101
+ * Sets the alpha value of the current colour.
1102
+ * @param {number} alpha a new alpha value in the [0, 1] range.
1103
+ * @returns {Color} the `Color` instance
1104
+ */
1105
+ setAlpha(alpha) {
1106
+ const self = this;
1107
+ self.a = boundAlpha(alpha);
1108
+ return self;
1109
+ }
1110
+
1111
+ /**
1112
+ * Saturate the colour with a given amount.
1113
+ * @param {number=} amount a value in the [0, 100] range
1114
+ * @returns {Color} the `Color` instance
1115
+ */
1116
+ saturate(amount) {
1117
+ const self = this;
1118
+ if (typeof amount !== 'number') return self;
1119
+ const { h, s, l } = self.toHsl();
1120
+ const { r, g, b } = hslToRgb(h, clamp01(s + amount / 100), l);
1121
+
1122
+ ObjectAssign(self, { r, g, b });
1123
+ return self;
1124
+ }
1125
+
1126
+ /**
1127
+ * Desaturate the colour with a given amount.
1128
+ * @param {number=} amount a value in the [0, 100] range
1129
+ * @returns {Color} the `Color` instance
1130
+ */
1131
+ desaturate(amount) {
1132
+ return typeof amount === 'number' ? this.saturate(-amount) : this;
1133
+ }
1134
+
1135
+ /**
1136
+ * Completely desaturates a colour into greyscale.
1137
+ * Same as calling `desaturate(100)`
1138
+ * @returns {Color} the `Color` instance
1139
+ */
1140
+ greyscale() {
1141
+ return this.saturate(-100);
1142
+ }
1143
+
1144
+ /**
1145
+ * Increase the colour lightness with a given amount.
1146
+ * @param {number=} amount a value in the [0, 100] range
1147
+ * @returns {Color} the `Color` instance
1148
+ */
1149
+ lighten(amount) {
1150
+ const self = this;
1151
+ if (typeof amount !== 'number') return self;
1152
+
1153
+ const { h, s, l } = self.toHsl();
1154
+ const { r, g, b } = hslToRgb(h, s, clamp01(l + amount / 100));
1155
+
1156
+ ObjectAssign(self, { r, g, b });
1157
+ return self;
1158
+ }
1159
+
1160
+ /**
1161
+ * Decrease the colour lightness with a given amount.
1162
+ * @param {number=} amount a value in the [0, 100] range
1163
+ * @returns {Color} the `Color` instance
1164
+ */
1165
+ darken(amount) {
1166
+ return typeof amount === 'number' ? this.lighten(-amount) : this;
1167
+ }
1168
+
1169
+ /**
1170
+ * Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
1171
+ * Values outside of this range will be wrapped into this range.
1172
+ *
1173
+ * @param {number=} amount a value in the [0, 100] range
1174
+ * @returns {Color} the `Color` instance
1175
+ */
1176
+ spin(amount) {
1177
+ const self = this;
1178
+ if (typeof amount !== 'number') return self;
1179
+
1180
+ const { h, s, l } = self.toHsl();
1181
+ const { r, g, b } = hslToRgb(clamp01(((h * 360 + amount) % 360) / 360), s, l);
1182
+
1183
+ ObjectAssign(self, { r, g, b });
1184
+ return self;
1185
+ }
1186
+
1187
+ /** Returns a clone of the current `Color` instance. */
1188
+ clone() {
1189
+ return new Color(this);
1190
+ }
1191
+
1192
+ /**
1193
+ * Returns the colour value in CSS valid string format.
1194
+ * @param {boolean=} allowShort when *true*, HEX values can be shorthand
1195
+ * @returns {string} the CSS valid colour in the configured format
1196
+ */
1197
+ toString(allowShort) {
1198
+ const self = this;
1199
+ const { format } = self;
1200
+
1201
+ if (format === 'hex') return self.toHexString(allowShort);
1202
+ if (format === 'hsl') return self.toHslString();
1203
+ if (format === 'hwb') return self.toHwbString();
1204
+
1205
+ return self.toRgbString();
1206
+ }
1207
+ }
1208
+
1209
+ ObjectAssign(Color, {
1210
+ ANGLES,
1211
+ CSS_ANGLE,
1212
+ CSS_INTEGER,
1213
+ CSS_NUMBER,
1214
+ CSS_UNIT,
1215
+ CSS_UNIT2,
1216
+ PERMISSIVE_MATCH,
1217
+ matchers,
1218
+ isOnePointZero,
1219
+ isPercentage,
1220
+ isValidCSSUnit,
1221
+ pad2,
1222
+ clamp01,
1223
+ bound01,
1224
+ boundAlpha,
1225
+ getRGBFromName,
1226
+ convertHexToDecimal,
1227
+ convertDecimalToHex,
1228
+ rgbToHsl,
1229
+ rgbToHex,
1230
+ rgbToHsv,
1231
+ rgbToHwb,
1232
+ rgbaToHex,
1233
+ hslToRgb,
1234
+ hsvToRgb,
1235
+ hueToRgb,
1236
+ hwbToRgb,
1237
+ parseIntFromHex,
1238
+ numberInputToObject,
1239
+ stringInputToObject,
1240
+ inputToRGB,
1241
+ ObjectAssign,
1242
+ });
1243
+
1244
+ /** @type {Record<string, any>} */
1245
+ const EventRegistry = {};
1246
+
1247
+ /**
1248
+ * The global event listener.
1249
+ *
1250
+ * @this {Element | HTMLElement | Window | Document}
1251
+ * @param {Event} e
1252
+ * @returns {void}
1253
+ */
1254
+ function globalListener(e) {
1255
+ const that = this;
1256
+ const { type } = e;
1257
+ const oneEvMap = EventRegistry[type] ? [...EventRegistry[type]] : [];
1258
+
1259
+ oneEvMap.forEach((elementsMap) => {
1260
+ const [element, listenersMap] = elementsMap;
1261
+ [...listenersMap].forEach((listenerMap) => {
1262
+ if (element === that) {
1263
+ const [listener, options] = listenerMap;
1264
+ listener.apply(element, [e]);
1265
+
1266
+ if (options && options.once) {
1267
+ removeListener(element, type, listener, options);
1268
+ }
1269
+ }
1270
+ });
1271
+ });
1272
+ }
1273
+
1274
+ /**
1275
+ * Register a new listener with its options and attach the `globalListener`
1276
+ * to the target if this is the first listener.
1277
+ *
1278
+ * @param {Element | HTMLElement | Window | Document} element
1279
+ * @param {string} eventType
1280
+ * @param {EventListenerObject['handleEvent']} listener
1281
+ * @param {AddEventListenerOptions=} options
1282
+ */
1283
+ const addListener = (element, eventType, listener, options) => {
1284
+ // get element listeners first
1285
+ if (!EventRegistry[eventType]) {
1286
+ EventRegistry[eventType] = new Map();
1287
+ }
1288
+ const oneEventMap = EventRegistry[eventType];
1289
+
1290
+ if (!oneEventMap.has(element)) {
1291
+ oneEventMap.set(element, new Map());
1292
+ }
1293
+ const oneElementMap = oneEventMap.get(element);
1294
+
1295
+ // get listeners size
1296
+ const { size } = oneElementMap;
1297
+
1298
+ // register listener with its options
1299
+ if (oneElementMap) {
1300
+ oneElementMap.set(listener, options);
1301
+ }
1302
+
1303
+ // add listener last
1304
+ if (!size) {
1305
+ element.addEventListener(eventType, globalListener, options);
1306
+ }
1307
+ };
1308
+
1309
+ /**
1310
+ * Remove a listener from registry and detach the `globalListener`
1311
+ * if no listeners are found in the registry.
1312
+ *
1313
+ * @param {Element | HTMLElement | Window | Document} element
1314
+ * @param {string} eventType
1315
+ * @param {EventListenerObject['handleEvent']} listener
1316
+ * @param {AddEventListenerOptions=} options
1317
+ */
1318
+ const removeListener = (element, eventType, listener, options) => {
1319
+ // get listener first
1320
+ const oneEventMap = EventRegistry[eventType];
1321
+ const oneElementMap = oneEventMap && oneEventMap.get(element);
1322
+ const savedOptions = oneElementMap && oneElementMap.get(listener);
1323
+
1324
+ // also recover initial options
1325
+ const { options: eventOptions } = savedOptions !== undefined
1326
+ ? savedOptions
1327
+ : { options };
1328
+
1329
+ // unsubscribe second, remove from registry
1330
+ if (oneElementMap && oneElementMap.has(listener)) oneElementMap.delete(listener);
1331
+ if (oneEventMap && (!oneElementMap || !oneElementMap.size)) oneEventMap.delete(element);
1332
+ if (!oneEventMap || !oneEventMap.size) delete EventRegistry[eventType];
1333
+
1334
+ // remove listener last
1335
+ if (!oneElementMap || !oneElementMap.size) {
1336
+ element.removeEventListener(eventType, globalListener, eventOptions);
1337
+ }
1338
+ };
1339
+
1340
+ /**
1341
+ * A global namespace for aria-description.
1342
+ * @type {string}
1343
+ */
1344
+ const ariaDescription = 'aria-description';
1345
+
1346
+ /**
1347
+ * A global namespace for aria-selected.
1348
+ * @type {string}
1349
+ */
1350
+ const ariaSelected = 'aria-selected';
1351
+
1352
+ /**
1353
+ * A global namespace for aria-expanded.
1354
+ * @type {string}
1355
+ */
1356
+ const ariaExpanded = 'aria-expanded';
1357
+
1358
+ /**
1359
+ * A global namespace for aria-valuetext.
1360
+ * @type {string}
1361
+ */
1362
+ const ariaValueText = 'aria-valuetext';
1363
+
1364
+ /**
1365
+ * A global namespace for aria-valuenow.
1366
+ * @type {string}
1367
+ */
1368
+ const ariaValueNow = 'aria-valuenow';
1369
+
1370
+ /**
1371
+ * A global namespace for aria-haspopup.
1372
+ * @type {string}
1373
+ */
1374
+ const ariaHasPopup = 'aria-haspopup';
1375
+
1376
+ /**
1377
+ * A global namespace for aria-hidden.
1378
+ * @type {string}
1379
+ */
1380
+ const ariaHidden = 'aria-hidden';
1381
+
1382
+ /**
1383
+ * A global namespace for aria-labelledby.
1384
+ * @type {string}
1385
+ */
1386
+ const ariaLabelledBy = 'aria-labelledby';
1387
+
1388
+ /**
1389
+ * A global namespace for `ArrowDown` key.
1390
+ * @type {string} e.which = 40 equivalent
1391
+ */
1392
+ const keyArrowDown = 'ArrowDown';
1393
+
1394
+ /**
1395
+ * A global namespace for `ArrowUp` key.
1396
+ * @type {string} e.which = 38 equivalent
1397
+ */
1398
+ const keyArrowUp = 'ArrowUp';
1399
+
1400
+ /**
1401
+ * A global namespace for `ArrowLeft` key.
1402
+ * @type {string} e.which = 37 equivalent
1403
+ */
1404
+ const keyArrowLeft = 'ArrowLeft';
1405
+
1406
+ /**
1407
+ * A global namespace for `ArrowRight` key.
1408
+ * @type {string} e.which = 39 equivalent
1409
+ */
1410
+ const keyArrowRight = 'ArrowRight';
1411
+
1412
+ /**
1413
+ * A global namespace for `Enter` key.
1414
+ * @type {string} e.which = 13 equivalent
1415
+ */
1416
+ const keyEnter = 'Enter';
1417
+
1418
+ /**
1419
+ * A global namespace for `Space` key.
1420
+ * @type {string} e.which = 32 equivalent
1421
+ */
1422
+ const keySpace = 'Space';
1423
+
1424
+ /**
1425
+ * A global namespace for `Escape` key.
1426
+ * @type {string} e.which = 27 equivalent
1427
+ */
1428
+ const keyEscape = 'Escape';
1429
+
1430
+ /**
1431
+ * A global namespace for `focusin` event.
1432
+ * @type {string}
1433
+ */
1434
+ const focusinEvent = 'focusin';
1435
+
1436
+ /**
1437
+ * A global namespace for `click` event.
1438
+ * @type {string}
1439
+ */
1440
+ const mouseclickEvent = 'click';
1441
+
1442
+ /**
1443
+ * A global namespace for `keydown` event.
1444
+ * @type {string}
1445
+ */
1446
+ const keydownEvent = 'keydown';
1447
+
1448
+ /**
1449
+ * A global namespace for `change` event.
1450
+ * @type {string}
1451
+ */
1452
+ const changeEvent = 'change';
1453
+
1454
+ /**
1455
+ * A global namespace for `touchstart` event.
1456
+ * @type {string}
1457
+ */
1458
+ const touchstartEvent = 'touchstart';
1459
+
1460
+ /**
1461
+ * A global namespace for `touchmove` event.
1462
+ * @type {string}
1463
+ */
1464
+ const touchmoveEvent = 'touchmove';
1465
+
1466
+ /**
1467
+ * A global namespace for `touchend` event.
1468
+ * @type {string}
1469
+ */
1470
+ const touchendEvent = 'touchend';
1471
+
1472
+ /**
1473
+ * A global namespace for `mousedown` event.
1474
+ * @type {string}
1475
+ */
1476
+ const mousedownEvent = 'mousedown';
1477
+
1478
+ /**
1479
+ * A global namespace for `mousemove` event.
1480
+ * @type {string}
1481
+ */
1482
+ const mousemoveEvent = 'mousemove';
1483
+
1484
+ /**
1485
+ * A global namespace for `mouseup` event.
1486
+ * @type {string}
1487
+ */
1488
+ const mouseupEvent = 'mouseup';
1489
+
1490
+ /**
1491
+ * A global namespace for `scroll` event.
1492
+ * @type {string}
1493
+ */
1494
+ const scrollEvent = 'scroll';
1495
+
1496
+ /**
1497
+ * A global namespace for `keyup` event.
1498
+ * @type {string}
1499
+ */
1500
+ const keyupEvent = 'keyup';
1501
+
1502
+ /**
1503
+ * A global namespace for `resize` event.
1504
+ * @type {string}
1505
+ */
1506
+ const resizeEvent = 'resize';
1507
+
1508
+ /**
1509
+ * A global namespace for `focusout` event.
1510
+ * @type {string}
1511
+ */
1512
+ const focusoutEvent = 'focusout';
1513
+
1514
+ // @ts-ignore
1515
+ const { userAgentData: uaDATA } = navigator;
1516
+
1517
+ /**
1518
+ * A global namespace for `userAgentData` object.
1519
+ */
1520
+ const userAgentData = uaDATA;
1521
+
1522
+ const { userAgent: userAgentString } = navigator;
1523
+
1524
+ /**
1525
+ * A global namespace for `navigator.userAgent` string.
1526
+ */
1527
+ const userAgent = userAgentString;
1528
+
1529
+ const mobileBrands = /iPhone|iPad|iPod|Android/i;
1530
+ let isMobileCheck = false;
1531
+
1532
+ if (userAgentData) {
1533
+ isMobileCheck = userAgentData.brands
1534
+ .some((/** @type {Record<String, any>} */x) => mobileBrands.test(x.brand));
1535
+ } else {
1536
+ isMobileCheck = mobileBrands.test(userAgent);
1537
+ }
1538
+
1539
+ /**
1540
+ * A global `boolean` for mobile detection.
1541
+ * @type {boolean}
1542
+ */
1543
+ const isMobile = isMobileCheck;
1544
+
1545
+ /**
1546
+ * Returns the `document.documentElement` or the `<html>` element.
1547
+ *
1548
+ * @param {(Node | HTMLElement | Element | globalThis)=} node
1549
+ * @returns {HTMLElement | HTMLHtmlElement}
1550
+ */
1551
+ function getDocumentElement(node) {
1552
+ return getDocument(node).documentElement;
1553
+ }
1554
+
1555
+ /**
1556
+ * Returns the `Window` object of a target node.
1557
+ * @see https://github.com/floating-ui/floating-ui
1558
+ *
1559
+ * @param {(Node | HTMLElement | Element | Window)=} node target node
1560
+ * @returns {globalThis}
1561
+ */
1562
+ function getWindow(node) {
1563
+ if (node == null) {
1564
+ return window;
1565
+ }
1566
+
1567
+ if (!(node instanceof Window)) {
1568
+ const { ownerDocument } = node;
1569
+ return ownerDocument ? ownerDocument.defaultView || window : window;
1570
+ }
1571
+
1572
+ // @ts-ignore
1573
+ return node;
1574
+ }
1575
+
1576
+ let elementUID = 0;
1577
+ let elementMapUID = 0;
1578
+ const elementIDMap = new Map();
1579
+
1580
+ /**
1581
+ * Returns a unique identifier for popover, tooltip, scrollspy.
1582
+ *
1583
+ * @param {HTMLElement | Element} element target element
1584
+ * @param {string=} key predefined key
1585
+ * @returns {number} an existing or new unique ID
1586
+ */
1587
+ function getUID(element, key) {
1588
+ let result = key ? elementUID : elementMapUID;
1589
+
1590
+ if (key) {
1591
+ const elID = getUID(element);
1592
+ const elMap = elementIDMap.get(elID) || new Map();
1593
+ if (!elementIDMap.has(elID)) {
1594
+ elementIDMap.set(elID, elMap);
1595
+ }
1596
+ if (!elMap.has(key)) {
1597
+ elMap.set(key, result);
1598
+ elementUID += 1;
1599
+ } else result = elMap.get(key);
1600
+ } else {
1601
+ const elkey = element.id || element;
1602
+
1603
+ if (!elementIDMap.has(elkey)) {
1604
+ elementIDMap.set(elkey, result);
1605
+ elementMapUID += 1;
1606
+ } else result = elementIDMap.get(elkey);
1607
+ }
1608
+ return result;
1609
+ }
1610
+
1611
+ /**
1612
+ * Returns the bounding client rect of a target `HTMLElement`.
1613
+ *
1614
+ * @see https://github.com/floating-ui/floating-ui
1615
+ *
1616
+ * @param {HTMLElement | Element} element event.target
1617
+ * @param {boolean=} includeScale when *true*, the target scale is also computed
1618
+ * @returns {SHORTER.BoundingClientRect} the bounding client rect object
1619
+ */
1620
+ function getBoundingClientRect(element, includeScale) {
1621
+ const {
1622
+ width, height, top, right, bottom, left,
1623
+ } = element.getBoundingClientRect();
1624
+ let scaleX = 1;
1625
+ let scaleY = 1;
1626
+
1627
+ if (includeScale && element instanceof HTMLElement) {
1628
+ const { offsetWidth, offsetHeight } = element;
1629
+ scaleX = offsetWidth > 0 ? Math.round(width) / offsetWidth || 1 : 1;
1630
+ scaleY = offsetHeight > 0 ? Math.round(height) / offsetHeight || 1 : 1;
1631
+ }
1632
+
1633
+ return {
1634
+ width: width / scaleX,
1635
+ height: height / scaleY,
1636
+ top: top / scaleY,
1637
+ right: right / scaleX,
1638
+ bottom: bottom / scaleY,
1639
+ left: left / scaleX,
1640
+ x: left / scaleX,
1641
+ y: top / scaleY,
1642
+ };
1643
+ }
1644
+
1645
+ /**
1646
+ * A global namespace for 'transitionDuration' string.
1647
+ * @type {string}
1648
+ */
1649
+ const transitionDuration = 'transitionDuration';
1650
+
1651
+ /**
1652
+ * A global namespace for `transitionProperty` string for modern browsers.
1653
+ *
1654
+ * @type {string}
1655
+ */
1656
+ const transitionProperty = 'transitionProperty';
1657
+
1658
+ /**
1659
+ * Utility to get the computed `transitionDuration`
1660
+ * from Element in miliseconds.
1661
+ *
1662
+ * @param {HTMLElement | Element} element target
1663
+ * @return {number} the value in miliseconds
1664
+ */
1665
+ function getElementTransitionDuration(element) {
1666
+ const propertyValue = getElementStyle(element, transitionProperty);
1667
+ const durationValue = getElementStyle(element, transitionDuration);
1668
+ const durationScale = durationValue.includes('ms') ? 1 : 1000;
1669
+ const duration = propertyValue && propertyValue !== 'none'
1670
+ ? parseFloat(durationValue) * durationScale : 0;
1671
+
1672
+ return !Number.isNaN(duration) ? duration : 0;
1673
+ }
1674
+
1675
+ /**
1676
+ * A global array with `Element` | `HTMLElement`.
1677
+ */
1678
+ const elementNodes = [Element, HTMLElement];
1679
+
1680
+ /**
1681
+ * Utility to check if target is typeof `HTMLElement`, `Element`, `Node`
1682
+ * or find one that matches a selector.
1683
+ *
1684
+ * @param {HTMLElement | Element | string} selector the input selector or target element
1685
+ * @param {(HTMLElement | Element | Document)=} parent optional node to look into
1686
+ * @return {(HTMLElement | Element)?} the `HTMLElement` or `querySelector` result
1687
+ */
1688
+ function querySelector(selector, parent) {
1689
+ const lookUp = parentNodes.some((x) => parent instanceof x)
1690
+ ? parent : getDocument();
1691
+
1692
+ // @ts-ignore
1693
+ return elementNodes.some((x) => selector instanceof x)
1694
+ // @ts-ignore
1695
+ ? selector : lookUp.querySelector(selector);
1696
+ }
1697
+
1698
+ /**
1699
+ * Shortcut for `HTMLElement.closest` method which also works
1700
+ * with children of `ShadowRoot`. The order of the parameters
1701
+ * is intentional since they're both required.
1702
+ *
1703
+ * @see https://stackoverflow.com/q/54520554/803358
1704
+ *
1705
+ * @param {HTMLElement | Element} element Element to look into
1706
+ * @param {string} selector the selector name
1707
+ * @return {(HTMLElement | Element)?} the query result
1708
+ */
1709
+ function closest(element, selector) {
1710
+ return element ? (element.closest(selector)
1711
+ // @ts-ignore -- break out of `ShadowRoot`
1712
+ || closest(element.getRootNode().host, selector)) : null;
1713
+ }
1714
+
1715
+ /**
1716
+ * Shortcut for `HTMLElement.getElementsByClassName` method. Some `Node` elements
1717
+ * like `ShadowRoot` do not support `getElementsByClassName`.
1718
+ *
1719
+ * @param {string} selector the class name
1720
+ * @param {(HTMLElement | Element | Document)=} parent optional Element to look into
1721
+ * @return {HTMLCollectionOf<HTMLElement | Element>} the 'HTMLCollection'
1722
+ */
1723
+ function getElementsByClassName(selector, parent) {
1724
+ const lookUp = parent && parentNodes.some((x) => parent instanceof x)
1725
+ ? parent : getDocument();
1726
+ return lookUp.getElementsByClassName(selector);
1727
+ }
1728
+
1729
+ /**
1730
+ * This is a shortie for `document.createElementNS` method
1731
+ * which allows you to create a new `HTMLElement` for a given `tagName`
1732
+ * or based on an object with specific non-readonly attributes:
1733
+ * `id`, `className`, `textContent`, `style`, etc.
1734
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElementNS
1735
+ *
1736
+ * @param {string} namespace `namespaceURI` to associate with the new `HTMLElement`
1737
+ * @param {Record<string, string> | string} param `tagName` or object
1738
+ * @return {HTMLElement | Element} a new `HTMLElement` or `Element`
1739
+ */
1740
+ function createElementNS(namespace, param) {
1741
+ if (typeof param === 'string') {
1742
+ return getDocument().createElementNS(namespace, param);
1743
+ }
1744
+
1745
+ const { tagName } = param;
1746
+ const attr = { ...param };
1747
+ const newElement = createElementNS(namespace, tagName);
1748
+ delete attr.tagName;
1749
+ ObjectAssign(newElement, attr);
1750
+ return newElement;
1751
+ }
1752
+
1753
+ /**
1754
+ * Shortcut for the `Element.dispatchEvent(Event)` method.
1755
+ *
1756
+ * @param {HTMLElement | Element} element is the target
1757
+ * @param {Event} event is the `Event` object
1758
+ */
1759
+ const dispatchEvent = (element, event) => element.dispatchEvent(event);
1760
+
1761
+ /** @type {Map<string, Map<HTMLElement | Element, Record<string, any>>>} */
1762
+ const componentData = new Map();
1763
+ /**
1764
+ * An interface for web components background data.
1765
+ * @see https://github.com/thednp/bootstrap.native/blob/master/src/components/base-component.js
1766
+ */
1767
+ const Data = {
1768
+ /**
1769
+ * Sets web components data.
1770
+ * @param {HTMLElement | Element | string} target target element
1771
+ * @param {string} component the component's name or a unique key
1772
+ * @param {Record<string, any>} instance the component instance
1773
+ */
1774
+ set: (target, component, instance) => {
1775
+ const element = querySelector(target);
1776
+ if (!element) return;
1777
+
1778
+ if (!componentData.has(component)) {
1779
+ componentData.set(component, new Map());
1780
+ }
1781
+
1782
+ const instanceMap = componentData.get(component);
1783
+ // @ts-ignore - not undefined, but defined right above
1784
+ instanceMap.set(element, instance);
1785
+ },
1786
+
1787
+ /**
1788
+ * Returns all instances for specified component.
1789
+ * @param {string} component the component's name or a unique key
1790
+ * @returns {Map<HTMLElement | Element, Record<string, any>>?} all the component instances
1791
+ */
1792
+ getAllFor: (component) => {
1793
+ const instanceMap = componentData.get(component);
1794
+
1795
+ return instanceMap || null;
1796
+ },
1797
+
1798
+ /**
1799
+ * Returns the instance associated with the target.
1800
+ * @param {HTMLElement | Element | string} target target element
1801
+ * @param {string} component the component's name or a unique key
1802
+ * @returns {Record<string, any>?} the instance
1803
+ */
1804
+ get: (target, component) => {
1805
+ const element = querySelector(target);
1806
+ const allForC = Data.getAllFor(component);
1807
+ const instance = element && allForC && allForC.get(element);
1808
+
1809
+ return instance || null;
1810
+ },
1811
+
1812
+ /**
1813
+ * Removes web components data.
1814
+ * @param {HTMLElement | Element | string} target target element
1815
+ * @param {string} component the component's name or a unique key
1816
+ */
1817
+ remove: (target, component) => {
1818
+ const element = querySelector(target);
1819
+ const instanceMap = componentData.get(component);
1820
+ if (!instanceMap || !element) return;
1821
+
1822
+ instanceMap.delete(element);
1823
+
1824
+ if (instanceMap.size === 0) {
1825
+ componentData.delete(component);
1826
+ }
1827
+ },
1828
+ };
1829
+
1830
+ /**
1831
+ * An alias for `Data.get()`.
1832
+ * @type {SHORTER.getInstance<any>}
1833
+ */
1834
+ const getInstance = (target, component) => Data.get(target, component);
1835
+
1836
+ /**
1837
+ * The raw value or a given component option.
1838
+ *
1839
+ * @typedef {string | HTMLElement | Function | number | boolean | null} niceValue
1840
+ */
1841
+
1842
+ /**
1843
+ * Utility to normalize component options
1844
+ *
1845
+ * @param {any} value the input value
1846
+ * @return {niceValue} the normalized value
1847
+ */
1848
+ function normalizeValue(value) {
1849
+ if (value === 'true') { // boolean
1850
+ return true;
1851
+ }
1852
+
1853
+ if (value === 'false') { // boolean
1854
+ return false;
1855
+ }
1856
+
1857
+ if (!Number.isNaN(+value)) { // number
1858
+ return +value;
1859
+ }
1860
+
1861
+ if (value === '' || value === 'null') { // null
1862
+ return null;
1863
+ }
1864
+
1865
+ // string / function / HTMLElement / object
1866
+ return value;
1867
+ }
1868
+
1869
+ /**
1870
+ * Shortcut for `Object.keys()` static method.
1871
+ * @param {Record<string, any>} obj a target object
1872
+ * @returns {string[]}
1873
+ */
1874
+ const ObjectKeys = (obj) => Object.keys(obj);
1875
+
1876
+ /**
1877
+ * Shortcut for `String.toLowerCase()`.
1878
+ *
1879
+ * @param {string} source input string
1880
+ * @returns {string} lowercase output string
1881
+ */
1882
+ const toLowerCase = (source) => source.toLowerCase();
1883
+
1884
+ /**
1885
+ * Utility to normalize component options.
1886
+ *
1887
+ * @param {HTMLElement | Element} element target
1888
+ * @param {Record<string, any>} defaultOps component default options
1889
+ * @param {Record<string, any>} inputOps component instance options
1890
+ * @param {string=} ns component namespace
1891
+ * @return {Record<string, any>} normalized component options object
1892
+ */
1893
+ function normalizeOptions(element, defaultOps, inputOps, ns) {
1894
+ // @ts-ignore -- our targets are always `HTMLElement`
1895
+ const data = { ...element.dataset };
1896
+ /** @type {Record<string, any>} */
1897
+ const normalOps = {};
1898
+ /** @type {Record<string, any>} */
1899
+ const dataOps = {};
1900
+ const title = 'title';
1901
+
1902
+ ObjectKeys(data).forEach((k) => {
1903
+ const key = ns && k.includes(ns)
1904
+ ? k.replace(ns, '').replace(/[A-Z]/, (match) => toLowerCase(match))
1905
+ : k;
1906
+
1907
+ dataOps[key] = normalizeValue(data[k]);
1908
+ });
1909
+
1910
+ ObjectKeys(inputOps).forEach((k) => {
1911
+ inputOps[k] = normalizeValue(inputOps[k]);
1912
+ });
1913
+
1914
+ ObjectKeys(defaultOps).forEach((k) => {
1915
+ if (k in inputOps) {
1916
+ normalOps[k] = inputOps[k];
1917
+ } else if (k in dataOps) {
1918
+ normalOps[k] = dataOps[k];
1919
+ } else {
1920
+ normalOps[k] = k === title
1921
+ ? getAttribute(element, title)
1922
+ : defaultOps[k];
1923
+ }
1924
+ });
1925
+
1926
+ return normalOps;
1927
+ }
1928
+
1929
+ /**
1930
+ * Utility to force re-paint of an `HTMLElement` target.
1931
+ *
1932
+ * @param {HTMLElement | Element} element is the target
1933
+ * @return {number} the `Element.offsetHeight` value
1934
+ */
1935
+ // @ts-ignore
1936
+ const reflow = (element) => element.offsetHeight;
1937
+
1938
+ /**
1939
+ * Utility to focus an `HTMLElement` target.
1940
+ *
1941
+ * @param {HTMLElement | Element} element is the target
1942
+ */
1943
+ // @ts-ignore -- `Element`s resulted from querySelector can focus too
1944
+ const focus = (element) => element.focus();
1945
+
1946
+ /**
1947
+ * Check class in `HTMLElement.classList`.
1948
+ *
1949
+ * @param {HTMLElement | Element} element target
1950
+ * @param {string} classNAME to check
1951
+ * @returns {boolean}
1952
+ */
1953
+ function hasClass(element, classNAME) {
1954
+ return element.classList.contains(classNAME);
1955
+ }
1956
+
1957
+ /**
1958
+ * Add class to `HTMLElement.classList`.
1959
+ *
1960
+ * @param {HTMLElement | Element} element target
1961
+ * @param {string} classNAME to add
1962
+ * @returns {void}
1963
+ */
1964
+ function addClass(element, classNAME) {
1965
+ element.classList.add(classNAME);
1966
+ }
1967
+
1968
+ /**
1969
+ * Remove class from `HTMLElement.classList`.
1970
+ *
1971
+ * @param {HTMLElement | Element} element target
1972
+ * @param {string} classNAME to remove
1973
+ * @returns {void}
1974
+ */
1975
+ function removeClass(element, classNAME) {
1976
+ element.classList.remove(classNAME);
1977
+ }
1978
+
1979
+ /**
1980
+ * Shortcut for `HTMLElement.removeAttribute()` method.
1981
+ * @param {HTMLElement | Element} element target element
1982
+ * @param {string} attribute attribute name
1983
+ * @returns {void}
1984
+ */
1985
+ const removeAttribute = (element, attribute) => element.removeAttribute(attribute);
1986
+
1987
+ /** @type {Record<string, string>} */
1988
+ const colorPickerLabels = {
1989
+ pickerLabel: 'Colour Picker',
1990
+ appearanceLabel: 'Colour Appearance',
1991
+ valueLabel: 'Colour Value',
1992
+ toggleLabel: 'Select Colour',
1993
+ presetsLabel: 'Colour Presets',
1994
+ defaultsLabel: 'Colour Defaults',
1995
+ formatLabel: 'Format',
1996
+ alphaLabel: 'Alpha',
1997
+ hexLabel: 'Hexadecimal',
1998
+ hueLabel: 'Hue',
1999
+ whitenessLabel: 'Whiteness',
2000
+ blacknessLabel: 'Blackness',
2001
+ saturationLabel: 'Saturation',
2002
+ lightnessLabel: 'Lightness',
2003
+ redLabel: 'Red',
2004
+ greenLabel: 'Green',
2005
+ blueLabel: 'Blue',
2006
+ };
2007
+
2008
+ /**
2009
+ * A list of 17 color names used for WAI-ARIA compliance.
2010
+ * @type {string[]}
2011
+ */
2012
+ const colorNames = ['white', 'black', 'grey', 'red', 'orange', 'brown', 'gold', 'olive', 'yellow', 'lime', 'green', 'teal', 'cyan', 'blue', 'violet', 'magenta', 'pink'];
2013
+
2014
+ /**
2015
+ * Shortcut for `String.toUpperCase()`.
2016
+ *
2017
+ * @param {string} source input string
2018
+ * @returns {string} uppercase output string
2019
+ */
2020
+ const toUpperCase = (source) => source.toUpperCase();
2021
+
2022
+ const vHidden = 'v-hidden';
2023
+
2024
+ /**
2025
+ * Returns the color form for `ColorPicker`.
2026
+ *
2027
+ * @param {CP.ColorPicker} self the `ColorPicker` instance
2028
+ * @returns {HTMLElement | Element} a new `<div>` element with color component `<input>`
2029
+ */
2030
+ function getColorForm(self) {
2031
+ const { format, id, componentLabels } = self;
2032
+ const colorForm = createElement({
2033
+ tagName: 'div',
2034
+ className: `color-form ${format}`,
2035
+ });
2036
+
2037
+ let components = ['hex'];
2038
+ if (format === 'rgb') components = ['red', 'green', 'blue', 'alpha'];
2039
+ else if (format === 'hsl') components = ['hue', 'saturation', 'lightness', 'alpha'];
2040
+ else if (format === 'hwb') components = ['hue', 'whiteness', 'blackness', 'alpha'];
2041
+
2042
+ components.forEach((c) => {
2043
+ const [C] = format === 'hex' ? ['#'] : toUpperCase(c).split('');
2044
+ const cID = `color_${format}_${c}_${id}`;
2045
+ const formatLabel = componentLabels[`${c}Label`];
2046
+ const cInputLabel = createElement({ tagName: 'label' });
2047
+ setAttribute(cInputLabel, 'for', cID);
2048
+ cInputLabel.append(
2049
+ createElement({ tagName: 'span', ariaHidden: 'true', innerText: `${C}:` }),
2050
+ createElement({ tagName: 'span', className: vHidden, innerText: formatLabel }),
2051
+ );
2052
+ const cInput = createElement({
2053
+ tagName: 'input',
2054
+ id: cID,
2055
+ // name: cID, - prevent saving the value to a form
2056
+ type: format === 'hex' ? 'text' : 'number',
2057
+ value: c === 'alpha' ? '100' : '0',
2058
+ className: `color-input ${c}`,
2059
+ });
2060
+ setAttribute(cInput, 'autocomplete', 'off');
2061
+ setAttribute(cInput, 'spellcheck', 'false');
2062
+
2063
+ // alpha
2064
+ let max = '100';
2065
+ let step = '1';
2066
+ if (c !== 'alpha') {
2067
+ if (format === 'rgb') {
2068
+ max = '255'; step = '1';
2069
+ } else if (c === 'hue') {
2070
+ max = '360'; step = '1';
2071
+ }
2072
+ }
2073
+ ObjectAssign(cInput, {
2074
+ min: '0',
2075
+ max,
2076
+ step,
2077
+ });
2078
+ // }
2079
+ colorForm.append(cInputLabel, cInput);
2080
+ });
2081
+ return colorForm;
2082
+ }
2083
+
2084
+ /**
2085
+ * A global namespace for aria-label.
2086
+ * @type {string}
2087
+ */
2088
+ const ariaLabel = 'aria-label';
2089
+
2090
+ /**
2091
+ * A global namespace for aria-valuemin.
2092
+ * @type {string}
2093
+ */
2094
+ const ariaValueMin = 'aria-valuemin';
2095
+
2096
+ /**
2097
+ * A global namespace for aria-valuemax.
2098
+ * @type {string}
2099
+ */
2100
+ const ariaValueMax = 'aria-valuemax';
2101
+
2102
+ /**
2103
+ * Returns all color controls for `ColorPicker`.
2104
+ *
2105
+ * @param {CP.ColorPicker} self the `ColorPicker` instance
2106
+ * @returns {HTMLElement | Element} color controls
2107
+ */
2108
+ function getColorControls(self) {
2109
+ const { format, componentLabels } = self;
2110
+ const {
2111
+ hueLabel, alphaLabel, lightnessLabel, saturationLabel,
2112
+ whitenessLabel, blacknessLabel,
2113
+ } = componentLabels;
2114
+
2115
+ const max1 = format === 'hsl' ? 360 : 100;
2116
+ const max2 = format === 'hsl' ? 100 : 360;
2117
+ const max3 = 100;
2118
+
2119
+ let ctrl1Label = format === 'hsl'
2120
+ ? `${hueLabel} & ${lightnessLabel}`
2121
+ : `${lightnessLabel} & ${saturationLabel}`;
2122
+
2123
+ ctrl1Label = format === 'hwb'
2124
+ ? `${whitenessLabel} & ${blacknessLabel}`
2125
+ : ctrl1Label;
2126
+
2127
+ const ctrl2Label = format === 'hsl'
2128
+ ? `${saturationLabel}`
2129
+ : `${hueLabel}`;
2130
+
2131
+ const colorControls = createElement({
2132
+ tagName: 'div',
2133
+ className: `color-controls ${format}`,
2134
+ });
2135
+
2136
+ const colorPointer = 'color-pointer';
2137
+ const colorSlider = 'color-slider';
2138
+
2139
+ const controls = [
2140
+ {
2141
+ i: 1,
2142
+ c: colorPointer,
2143
+ l: ctrl1Label,
2144
+ min: 0,
2145
+ max: max1,
2146
+ },
2147
+ {
2148
+ i: 2,
2149
+ c: colorSlider,
2150
+ l: ctrl2Label,
2151
+ min: 0,
2152
+ max: max2,
2153
+ },
2154
+ {
2155
+ i: 3,
2156
+ c: colorSlider,
2157
+ l: alphaLabel,
2158
+ min: 0,
2159
+ max: max3,
2160
+ },
2161
+ ];
2162
+
2163
+ controls.forEach((template) => {
2164
+ const {
2165
+ i, c, l, min, max,
2166
+ } = template;
2167
+ // const hidden = i === 2 && format === 'hwb' ? ' v-hidden' : '';
2168
+ const control = createElement({
2169
+ tagName: 'div',
2170
+ // className: `color-control${hidden}`,
2171
+ className: 'color-control',
2172
+ });
2173
+ setAttribute(control, 'role', 'presentation');
2174
+
2175
+ control.append(
2176
+ createElement({
2177
+ tagName: 'div',
2178
+ className: `visual-control visual-control${i}`,
2179
+ }),
2180
+ );
2181
+
2182
+ const knob = createElement({
2183
+ tagName: 'div',
2184
+ className: `${c} knob`,
2185
+ ariaLive: 'polite',
2186
+ });
2187
+
2188
+ setAttribute(knob, ariaLabel, l);
2189
+ setAttribute(knob, 'role', 'slider');
2190
+ setAttribute(knob, 'tabindex', '0');
2191
+ setAttribute(knob, ariaValueMin, `${min}`);
2192
+ setAttribute(knob, ariaValueMax, `${max}`);
2193
+ control.append(knob);
2194
+ colorControls.append(control);
2195
+ });
2196
+
2197
+ return colorControls;
2198
+ }
2199
+
2200
+ /**
2201
+ * @class
2202
+ * Returns a color palette with a given set of parameters.
2203
+ * @example
2204
+ * new ColorPalette(0, 12, 10);
2205
+ * // => { hue: 0, hueSteps: 12, lightSteps: 10, colors: array }
2206
+ */
2207
+ class ColorPalette {
2208
+ /**
2209
+ * The `hue` parameter is optional, which would be set to 0.
2210
+ * @param {number[]} args represeinting hue, hueSteps, lightSteps
2211
+ * * `args.hue` the starting Hue [0, 360]
2212
+ * * `args.hueSteps` Hue Steps Count [5, 13]
2213
+ * * `args.lightSteps` Lightness Steps Count [8, 10]
2214
+ */
2215
+ constructor(...args) {
2216
+ let hue = 0;
2217
+ let hueSteps = 12;
2218
+ let lightSteps = 10;
2219
+ let lightnessArray = [0.5];
2220
+
2221
+ if (args.length === 3) {
2222
+ [hue, hueSteps, lightSteps] = args;
2223
+ } else if (args.length === 2) {
2224
+ [hueSteps, lightSteps] = args;
2225
+ } else {
2226
+ throw TypeError('The ColorPalette requires minimum 2 arguments');
2227
+ }
2228
+
2229
+ /** @type {string[]} */
2230
+ const colors = [];
2231
+
2232
+ const hueStep = 360 / hueSteps;
2233
+ const lightStep = 100 / (lightSteps + (lightSteps % 2 ? 0 : 1)) / 100;
2234
+ const half = Math.round((lightSteps - (lightSteps % 2 ? 1 : 0)) / 2);
2235
+
2236
+ // light tints
2237
+ for (let i = 0; i < half; i += 1) {
2238
+ lightnessArray = [...lightnessArray, (0.5 + lightStep * (i + 1))];
2239
+ }
2240
+
2241
+ // dark tints
2242
+ for (let i = 0; i < lightSteps - half - 1; i += 1) {
2243
+ lightnessArray = [(0.5 - lightStep * (i + 1)), ...lightnessArray];
2244
+ }
2245
+
2246
+ // feed `colors` Array
2247
+ for (let i = 0; i < hueSteps; i += 1) {
2248
+ const currentHue = ((hue + i * hueStep) % 360) / 360;
2249
+ lightnessArray.forEach((l) => {
2250
+ colors.push(new Color({ h: currentHue, s: 1, l }).toHexString());
2251
+ });
2252
+ }
2253
+
2254
+ this.hue = hue;
2255
+ this.hueSteps = hueSteps;
2256
+ this.lightSteps = lightSteps;
2257
+ this.colors = colors;
2258
+ }
2259
+ }
2260
+
2261
+ /**
2262
+ * Returns a color-defaults with given values and class.
2263
+ * @param {CP.ColorPicker} self
2264
+ * @param {CP.ColorPalette | string[]} colorsSource
2265
+ * @param {string} menuClass
2266
+ * @returns {HTMLElement | Element}
2267
+ */
2268
+ function getColorMenu(self, colorsSource, menuClass) {
2269
+ const { input, format, componentLabels } = self;
2270
+ const { defaultsLabel, presetsLabel } = componentLabels;
2271
+ const isOptionsMenu = menuClass === 'color-options';
2272
+ const isPalette = colorsSource instanceof ColorPalette;
2273
+ const menuLabel = isOptionsMenu ? presetsLabel : defaultsLabel;
2274
+ let colorsArray = isPalette ? colorsSource.colors : colorsSource;
2275
+ colorsArray = colorsArray instanceof Array ? colorsArray : [];
2276
+ const colorsCount = colorsArray.length;
2277
+ const { lightSteps } = isPalette ? colorsSource : { lightSteps: null };
2278
+ let fit = lightSteps
2279
+ || Math.max(...[5, 6, 7, 8, 9, 10].filter((x) => colorsCount > (x * 2) && !(colorsCount % x)));
2280
+ fit = Number.isFinite(fit) ? fit : 5;
2281
+ const isMultiLine = isOptionsMenu && colorsCount > fit;
2282
+ let rowCountHover = 1;
2283
+ rowCountHover = isMultiLine && colorsCount < 27 ? 2 : rowCountHover;
2284
+ rowCountHover = colorsCount >= 27 ? 3 : rowCountHover;
2285
+ rowCountHover = colorsCount >= 36 ? 4 : rowCountHover;
2286
+ rowCountHover = colorsCount >= 45 ? 5 : rowCountHover;
2287
+ const rowCount = rowCountHover - (colorsCount < 27 ? 1 : 2);
2288
+ const isScrollable = isMultiLine && colorsCount > rowCountHover * fit;
2289
+ let finalClass = menuClass;
2290
+ finalClass += isScrollable ? ' scrollable' : '';
2291
+ finalClass += isMultiLine ? ' multiline' : '';
2292
+ const gap = isMultiLine ? '1px' : '0.25rem';
2293
+ let optionSize = isMultiLine ? 1.75 : 2;
2294
+ optionSize = !(colorsCount % 10) && isMultiLine ? 1.5 : optionSize;
2295
+ const menuHeight = `${(rowCount || 1) * optionSize}rem`;
2296
+ const menuHeightHover = `calc(${rowCountHover} * ${optionSize}rem + ${rowCountHover - 1} * ${gap})`;
2297
+ const gridTemplateColumns = `repeat(${fit}, ${optionSize}rem)`;
2298
+ const gridTemplateRows = `repeat(auto-fill, ${optionSize}rem)`;
2299
+
2300
+ const menu = createElement({
2301
+ tagName: 'ul',
2302
+ className: finalClass,
2303
+ });
2304
+ setAttribute(menu, 'role', 'listbox');
2305
+ setAttribute(menu, ariaLabel, `${menuLabel}`);
2306
+
2307
+ if (isOptionsMenu) {
2308
+ if (isScrollable) {
2309
+ const styleText = 'this.style.height=';
2310
+ setAttribute(menu, 'onmouseout', `${styleText}'${menuHeight}'`);
2311
+ setAttribute(menu, 'onmouseover', `${styleText}'${menuHeightHover}'`);
2312
+ }
2313
+ const menuStyle = {
2314
+ height: isScrollable ? menuHeight : '', gridTemplateColumns, gridTemplateRows, gap,
2315
+ };
2316
+ setElementStyle(menu, menuStyle);
2317
+ }
2318
+
2319
+ colorsArray.forEach((x) => {
2320
+ const [value, label] = x.trim().split(':');
2321
+ const xRealColor = new Color(value, format).toString();
2322
+ const isActive = xRealColor === getAttribute(input, 'value');
2323
+ const active = isActive ? ' active' : '';
2324
+
2325
+ const option = createElement({
2326
+ tagName: 'li',
2327
+ className: `color-option${active}`,
2328
+ innerText: `${label || x}`,
2329
+ });
2330
+
2331
+ setAttribute(option, 'tabindex', '0');
2332
+ setAttribute(option, 'data-value', `${value}`);
2333
+ setAttribute(option, 'role', 'option');
2334
+ setAttribute(option, ariaSelected, isActive ? 'true' : 'false');
2335
+
2336
+ if (isOptionsMenu) {
2337
+ setElementStyle(option, {
2338
+ width: `${optionSize}rem`, height: `${optionSize}rem`, backgroundColor: x,
2339
+ });
2340
+ }
2341
+
2342
+ menu.append(option);
2343
+ });
2344
+ return menu;
2345
+ }
2346
+
2347
+ /**
2348
+ * Check if a string is valid JSON string.
2349
+ * @param {string} str the string input
2350
+ * @returns {boolean} the query result
2351
+ */
2352
+ function isValidJSON(str) {
2353
+ try {
2354
+ JSON.parse(str);
2355
+ } catch (e) {
2356
+ return false;
2357
+ }
2358
+ return true;
2359
+ }
2360
+
2361
+ var version = "0.0.1alpha2";
2362
+
2363
+ // @ts-ignore
2364
+
2365
+ const Version = version;
2366
+
2367
+ // ColorPicker GC
2368
+ // ==============
2369
+ const colorPickerString = 'color-picker';
2370
+ const colorPickerSelector = `[data-function="${colorPickerString}"]`;
2371
+ const colorPickerParentSelector = `.${colorPickerString},${colorPickerString}`;
2372
+ const colorPickerDefaults = {
2373
+ componentLabels: colorPickerLabels,
2374
+ colorLabels: colorNames,
2375
+ format: 'rgb',
2376
+ colorPresets: undefined,
2377
+ colorKeywords: nonColors,
2378
+ };
2379
+
2380
+ // ColorPicker Static Methods
2381
+ // ==========================
2382
+
2383
+ /** @type {CP.GetInstance<ColorPicker>} */
2384
+ const getColorPickerInstance = (element) => getInstance(element, colorPickerString);
2385
+
2386
+ /** @type {CP.InitCallback<ColorPicker>} */
2387
+ const initColorPicker = (element) => new ColorPicker(element);
2388
+
2389
+ // ColorPicker Private Methods
2390
+ // ===========================
2391
+
2392
+ /**
2393
+ * Generate HTML markup and update instance properties.
2394
+ * @param {ColorPicker} self
2395
+ */
2396
+ function initCallback(self) {
2397
+ const {
2398
+ input, parent, format, id, componentLabels, colorKeywords, colorPresets,
2399
+ } = self;
2400
+ const colorValue = getAttribute(input, 'value') || '#fff';
2401
+
2402
+ const {
2403
+ toggleLabel, pickerLabel, formatLabel, hexLabel,
2404
+ } = componentLabels;
2405
+
2406
+ // update color
2407
+ const color = nonColors.includes(colorValue) ? '#fff' : colorValue;
2408
+ self.color = new Color(color, format);
2409
+
2410
+ // set initial controls dimensions
2411
+ // make the controls smaller on mobile
2412
+ const dropClass = isMobile ? ' mobile' : '';
2413
+ const formatString = format === 'hex' ? hexLabel : format.toUpperCase();
2414
+
2415
+ const pickerBtn = createElement({
2416
+ id: `picker-btn-${id}`,
2417
+ tagName: 'button',
2418
+ className: 'picker-toggle btn-appearance',
2419
+ });
2420
+ setAttribute(pickerBtn, ariaExpanded, 'false');
2421
+ setAttribute(pickerBtn, ariaHasPopup, 'true');
2422
+ pickerBtn.append(createElement({
2423
+ tagName: 'span',
2424
+ className: vHidden,
2425
+ innerText: `${pickerLabel}. ${formatLabel}: ${formatString}`,
2426
+ }));
2427
+
2428
+ const pickerDropdown = createElement({
2429
+ tagName: 'div',
2430
+ className: `color-dropdown picker${dropClass}`,
2431
+ });
2432
+ setAttribute(pickerDropdown, ariaLabelledBy, `picker-btn-${id}`);
2433
+ setAttribute(pickerDropdown, 'role', 'group');
2434
+
2435
+ const colorControls = getColorControls(self);
2436
+ const colorForm = getColorForm(self);
2437
+
2438
+ pickerDropdown.append(colorControls, colorForm);
2439
+ input.before(pickerBtn);
2440
+ parent.append(pickerDropdown);
2441
+
2442
+ // set colour key menu template
2443
+ if (colorKeywords || colorPresets) {
2444
+ const presetsDropdown = createElement({
2445
+ tagName: 'div',
2446
+ className: `color-dropdown scrollable menu${dropClass}`,
2447
+ });
2448
+
2449
+ // color presets
2450
+ if ((colorPresets instanceof Array && colorPresets.length)
2451
+ || (colorPresets instanceof ColorPalette && colorPresets.colors)) {
2452
+ const presetsMenu = getColorMenu(self, colorPresets, 'color-options');
2453
+ presetsDropdown.append(presetsMenu);
2454
+ }
2455
+
2456
+ // explicit defaults [reset, initial, inherit, transparent, currentColor]
2457
+ if (colorKeywords && colorKeywords.length) {
2458
+ const keywordsMenu = getColorMenu(self, colorKeywords, 'color-defaults');
2459
+ presetsDropdown.append(keywordsMenu);
2460
+ }
2461
+
2462
+ const presetsBtn = createElement({
2463
+ tagName: 'button',
2464
+ className: 'menu-toggle btn-appearance',
2465
+ });
2466
+ setAttribute(presetsBtn, 'tabindex', '-1');
2467
+ setAttribute(presetsBtn, ariaExpanded, 'false');
2468
+ setAttribute(presetsBtn, ariaHasPopup, 'true');
2469
+
2470
+ const xmlns = encodeURI('http://www.w3.org/2000/svg');
2471
+ const presetsIcon = createElementNS(xmlns, { tagName: 'svg' });
2472
+ setAttribute(presetsIcon, 'xmlns', xmlns);
2473
+ setAttribute(presetsIcon, 'viewBox', '0 0 512 512');
2474
+ setAttribute(presetsIcon, ariaHidden, 'true');
2475
+
2476
+ const path = createElementNS(xmlns, { tagName: 'path' });
2477
+ setAttribute(path, 'd', 'M98,158l157,156L411,158l27,27L255,368L71,185L98,158z');
2478
+ setAttribute(path, 'fill', '#fff');
2479
+ presetsIcon.append(path);
2480
+ presetsBtn.append(createElement({
2481
+ tagName: 'span',
2482
+ className: vHidden,
2483
+ innerText: `${toggleLabel}`,
2484
+ }), presetsIcon);
2485
+
2486
+ parent.append(presetsBtn, presetsDropdown);
2487
+ }
2488
+
2489
+ // solve non-colors after settings save
2490
+ if (colorKeywords && nonColors.includes(colorValue)) {
2491
+ self.value = colorValue;
2492
+ }
2493
+ setAttribute(input, 'tabindex', '-1');
2494
+ }
2495
+
2496
+ /**
2497
+ * Add / remove `ColorPicker` main event listeners.
2498
+ * @param {ColorPicker} self
2499
+ * @param {boolean=} action
2500
+ */
2501
+ function toggleEvents(self, action) {
2502
+ const fn = action ? addListener : removeListener;
2503
+ const { input, pickerToggle, menuToggle } = self;
2504
+
2505
+ fn(input, focusinEvent, self.showPicker);
2506
+ fn(pickerToggle, mouseclickEvent, self.togglePicker);
2507
+
2508
+ fn(input, keydownEvent, self.keyToggle);
2509
+
2510
+ if (menuToggle) {
2511
+ fn(menuToggle, mouseclickEvent, self.toggleMenu);
2512
+ }
2513
+ }
2514
+
2515
+ /**
2516
+ * Add / remove `ColorPicker` event listeners active only when open.
2517
+ * @param {ColorPicker} self
2518
+ * @param {boolean=} action
2519
+ */
2520
+ function toggleEventsOnShown(self, action) {
2521
+ const fn = action ? addListener : removeListener;
2522
+ const { input, colorMenu, parent } = self;
2523
+ const doc = getDocument(input);
2524
+ const win = getWindow(input);
2525
+ const pointerEvents = `on${touchstartEvent}` in doc
2526
+ ? { down: touchstartEvent, move: touchmoveEvent, up: touchendEvent }
2527
+ : { down: mousedownEvent, move: mousemoveEvent, up: mouseupEvent };
2528
+
2529
+ fn(self.controls, pointerEvents.down, self.pointerDown);
2530
+ self.controlKnobs.forEach((x) => fn(x, keydownEvent, self.handleKnobs));
2531
+
2532
+ // @ts-ignore -- this is `Window`
2533
+ fn(win, scrollEvent, self.handleScroll);
2534
+ // @ts-ignore -- this is `Window`
2535
+ fn(win, resizeEvent, self.update);
2536
+
2537
+ [input, ...self.inputs].forEach((x) => fn(x, changeEvent, self.changeHandler));
2538
+
2539
+ if (colorMenu) {
2540
+ fn(colorMenu, mouseclickEvent, self.menuClickHandler);
2541
+ fn(colorMenu, keydownEvent, self.menuKeyHandler);
2542
+ }
2543
+
2544
+ fn(doc, pointerEvents.move, self.pointerMove);
2545
+ fn(doc, pointerEvents.up, self.pointerUp);
2546
+ fn(parent, focusoutEvent, self.handleFocusOut);
2547
+ // @ts-ignore -- this is `Window`
2548
+ fn(win, keyupEvent, self.handleDismiss);
2549
+ }
2550
+
2551
+ /**
2552
+ * Triggers the `ColorPicker` original event.
2553
+ * @param {ColorPicker} self
2554
+ */
2555
+ function firePickerChange(self) {
2556
+ dispatchEvent(self.input, new CustomEvent('colorpicker.change'));
2557
+ }
2558
+
2559
+ /**
2560
+ * Hides a visible dropdown.
2561
+ * @param {HTMLElement} element
2562
+ * @returns {void}
2563
+ */
2564
+ function removePosition(element) {
2565
+ if (element) {
2566
+ ['bottom', 'top'].forEach((x) => removeClass(element, x));
2567
+ }
2568
+ }
2569
+
2570
+ /**
2571
+ * Shows a `ColorPicker` dropdown and close the curent open dropdown.
2572
+ * @param {ColorPicker} self
2573
+ * @param {HTMLElement | Element} dropdown
2574
+ */
2575
+ function showDropdown(self, dropdown) {
2576
+ const {
2577
+ colorPicker, colorMenu, menuToggle, pickerToggle, parent,
2578
+ } = self;
2579
+ const isPicker = dropdown === colorPicker;
2580
+ const openDropdown = isPicker ? colorMenu : colorPicker;
2581
+ const activeBtn = isPicker ? menuToggle : pickerToggle;
2582
+ const nextBtn = !isPicker ? menuToggle : pickerToggle;
2583
+
2584
+ if (!hasClass(parent, 'open')) {
2585
+ addClass(parent, 'open');
2586
+ }
2587
+ if (openDropdown) {
2588
+ removeClass(openDropdown, 'show');
2589
+ removePosition(openDropdown);
2590
+ }
2591
+ addClass(dropdown, 'bottom');
2592
+ reflow(dropdown);
2593
+ addClass(dropdown, 'show');
2594
+ if (isPicker) self.update();
2595
+ self.show();
2596
+ setAttribute(nextBtn, ariaExpanded, 'true');
2597
+ if (activeBtn) {
2598
+ setAttribute(activeBtn, ariaExpanded, 'false');
2599
+ }
2600
+ }
2601
+
2602
+ /**
2603
+ * Color Picker Web Component
2604
+ * @see http://thednp.github.io/color-picker
2605
+ */
2606
+ class ColorPicker {
2607
+ /**
2608
+ * Returns a new `ColorPicker` instance. The target of this constructor
2609
+ * must be an `HTMLInputElement`.
2610
+ *
2611
+ * @param {HTMLInputElement | string} target the target `<input>` element
2612
+ * @param {CP.ColorPickerOptions=} config instance options
2613
+ */
2614
+ constructor(target, config) {
2615
+ const self = this;
2616
+ /** @type {HTMLInputElement} */
2617
+ // @ts-ignore
2618
+ const input = querySelector(target);
2619
+
2620
+ // invalidate
2621
+ if (!input) throw new TypeError(`ColorPicker target ${target} cannot be found.`);
2622
+ self.input = input;
2623
+
2624
+ const parent = closest(input, colorPickerParentSelector);
2625
+ if (!parent) throw new TypeError('ColorPicker requires a specific markup to work.');
2626
+
2627
+ /** @type {HTMLElement} */
2628
+ // @ts-ignore
2629
+ self.parent = parent;
2630
+
2631
+ /** @type {number} */
2632
+ self.id = getUID(input, colorPickerString);
2633
+
2634
+ // set initial state
2635
+ /** @type {HTMLElement?} */
2636
+ self.dragElement = null;
2637
+ /** @type {boolean} */
2638
+ self.isOpen = false;
2639
+ /** @type {Record<string, number>} */
2640
+ self.controlPositions = {
2641
+ c1x: 0, c1y: 0, c2y: 0, c3y: 0,
2642
+ };
2643
+ /** @type {Record<string, string>} */
2644
+ self.colorLabels = {};
2645
+ /** @type {string[]=} */
2646
+ self.colorKeywords = undefined;
2647
+ /** @type {(ColorPalette | string[])=} */
2648
+ self.colorPresets = undefined;
2649
+
2650
+ // process options
2651
+ const {
2652
+ format, componentLabels, colorLabels, colorKeywords, colorPresets,
2653
+ } = normalizeOptions(this.isCE ? parent : input, colorPickerDefaults, config || {});
2654
+
2655
+ let translatedColorLabels = colorNames;
2656
+ if (colorLabels instanceof Array && colorLabels.length === 17) {
2657
+ translatedColorLabels = colorLabels;
2658
+ } else if (colorLabels && colorLabels.split(',').length === 17) {
2659
+ translatedColorLabels = colorLabels.split(',');
2660
+ }
2661
+
2662
+ // expose colour labels to all methods
2663
+ colorNames.forEach((c, i) => {
2664
+ self.colorLabels[c] = translatedColorLabels[i].trim();
2665
+ });
2666
+
2667
+ // update and expose component labels
2668
+ const tempLabels = ObjectAssign({}, colorPickerLabels);
2669
+ const jsonLabels = componentLabels && isValidJSON(componentLabels)
2670
+ ? JSON.parse(componentLabels) : componentLabels || {};
2671
+
2672
+ /** @type {Record<string, string>} */
2673
+ self.componentLabels = ObjectAssign(tempLabels, jsonLabels);
2674
+
2675
+ /** @type {Color} */
2676
+ self.color = new Color('white', format);
2677
+
2678
+ /** @type {CP.ColorFormats} */
2679
+ self.format = format;
2680
+
2681
+ // set colour defaults
2682
+ if (colorKeywords instanceof Array) {
2683
+ self.colorKeywords = colorKeywords;
2684
+ } else if (typeof colorKeywords === 'string' && colorKeywords.length) {
2685
+ self.colorKeywords = colorKeywords.split(',');
2686
+ }
2687
+
2688
+ // set colour presets
2689
+ if (colorPresets instanceof Array) {
2690
+ self.colorPresets = colorPresets;
2691
+ } else if (typeof colorPresets === 'string' && colorPresets.length) {
2692
+ if (isValidJSON(colorPresets)) {
2693
+ const { hue, hueSteps, lightSteps } = JSON.parse(colorPresets);
2694
+ self.colorPresets = new ColorPalette(hue, hueSteps, lightSteps);
2695
+ } else {
2696
+ self.colorPresets = colorPresets.split(',').map((x) => x.trim());
2697
+ }
2698
+ }
2699
+
2700
+ // bind events
2701
+ self.showPicker = self.showPicker.bind(self);
2702
+ self.togglePicker = self.togglePicker.bind(self);
2703
+ self.toggleMenu = self.toggleMenu.bind(self);
2704
+ self.menuClickHandler = self.menuClickHandler.bind(self);
2705
+ self.menuKeyHandler = self.menuKeyHandler.bind(self);
2706
+ self.pointerDown = self.pointerDown.bind(self);
2707
+ self.pointerMove = self.pointerMove.bind(self);
2708
+ self.pointerUp = self.pointerUp.bind(self);
2709
+ self.update = self.update.bind(self);
2710
+ self.handleScroll = self.handleScroll.bind(self);
2711
+ self.handleFocusOut = self.handleFocusOut.bind(self);
2712
+ self.changeHandler = self.changeHandler.bind(self);
2713
+ self.handleDismiss = self.handleDismiss.bind(self);
2714
+ self.keyToggle = self.keyToggle.bind(self);
2715
+ self.handleKnobs = self.handleKnobs.bind(self);
2716
+
2717
+ // generate markup
2718
+ initCallback(self);
2719
+
2720
+ const [colorPicker, colorMenu] = getElementsByClassName('color-dropdown', parent);
2721
+ // set main elements
2722
+ /** @type {HTMLElement} */
2723
+ // @ts-ignore
2724
+ self.pickerToggle = querySelector('.picker-toggle', parent);
2725
+ /** @type {HTMLElement} */
2726
+ // @ts-ignore
2727
+ self.menuToggle = querySelector('.menu-toggle', parent);
2728
+ /** @type {HTMLElement} */
2729
+ // @ts-ignore
2730
+ self.colorPicker = colorPicker;
2731
+ /** @type {HTMLElement} */
2732
+ // @ts-ignore
2733
+ self.colorMenu = colorMenu;
2734
+ /** @type {HTMLInputElement[]} */
2735
+ // @ts-ignore
2736
+ self.inputs = [...getElementsByClassName('color-input', parent)];
2737
+ const [controls] = getElementsByClassName('color-controls', parent);
2738
+ self.controls = controls;
2739
+ /** @type {(HTMLElement | Element)[]} */
2740
+ self.controlKnobs = [...getElementsByClassName('knob', controls)];
2741
+ /** @type {(HTMLElement)[]} */
2742
+ // @ts-ignore
2743
+ self.visuals = [...getElementsByClassName('visual-control', controls)];
2744
+
2745
+ // update colour picker controls, inputs and visuals
2746
+ self.update();
2747
+
2748
+ // add main events listeners
2749
+ toggleEvents(self, true);
2750
+
2751
+ // set component data
2752
+ Data.set(input, colorPickerString, self);
2753
+ }
2754
+
2755
+ /** Returns the current colour value */
2756
+ get value() { return this.input.value; }
2757
+
2758
+ /**
2759
+ * Sets a new colour value.
2760
+ * @param {string} v new colour value
2761
+ */
2762
+ set value(v) { this.input.value = v; }
2763
+
2764
+ /** Check if the colour presets include any non-colour. */
2765
+ get includeNonColor() {
2766
+ return this.colorKeywords instanceof Array
2767
+ && this.colorKeywords.some((x) => nonColors.includes(x));
2768
+ }
2769
+
2770
+ /** Check if the parent of the target is a `ColorPickerElement` instance. */
2771
+ get isCE() { return this.parent.localName === colorPickerString; }
2772
+
2773
+ /** Returns hexadecimal value of the current colour. */
2774
+ get hex() { return this.color.toHex(true); }
2775
+
2776
+ /** Returns the current colour value in {h,s,v,a} object format. */
2777
+ get hsv() { return this.color.toHsv(); }
2778
+
2779
+ /** Returns the current colour value in {h,s,l,a} object format. */
2780
+ get hsl() { return this.color.toHsl(); }
2781
+
2782
+ /** Returns the current colour value in {h,w,b,a} object format. */
2783
+ get hwb() { return this.color.toHwb(); }
2784
+
2785
+ /** Returns the current colour value in {r,g,b,a} object format. */
2786
+ get rgb() { return this.color.toRgb(); }
2787
+
2788
+ /** Returns the current colour brightness. */
2789
+ get brightness() { return this.color.brightness; }
2790
+
2791
+ /** Returns the current colour luminance. */
2792
+ get luminance() { return this.color.luminance; }
2793
+
2794
+ /** Checks if the current colour requires a light text colour. */
2795
+ get isDark() {
2796
+ const { color, brightness } = this;
2797
+ return brightness < 120 && color.a > 0.33;
2798
+ }
2799
+
2800
+ /** Checks if the current input value is a valid colour. */
2801
+ get isValid() {
2802
+ const inputValue = this.input.value;
2803
+ return inputValue !== '' && new Color(inputValue).isValid;
2804
+ }
2805
+
2806
+ /** Updates `ColorPicker` visuals. */
2807
+ updateVisuals() {
2808
+ const self = this;
2809
+ const {
2810
+ format, controlPositions, visuals,
2811
+ } = self;
2812
+ const [v1, v2, v3] = visuals;
2813
+ const { offsetWidth, offsetHeight } = v1;
2814
+ const hue = format === 'hsl'
2815
+ ? controlPositions.c1x / offsetWidth
2816
+ : controlPositions.c2y / offsetHeight;
2817
+ // @ts-ignore - `hslToRgb` is assigned to `Color` as static method
2818
+ const { r, g, b } = Color.hslToRgb(hue, 1, 0.5);
2819
+ const whiteGrad = 'linear-gradient(rgb(255,255,255) 0%, rgb(255,255,255) 100%)';
2820
+ const alpha = 1 - controlPositions.c3y / offsetHeight;
2821
+ const roundA = Math.round((alpha * 100)) / 100;
2822
+
2823
+ if (format !== 'hsl') {
2824
+ const fill = new Color({
2825
+ h: hue, s: 1, l: 0.5, a: alpha,
2826
+ }).toRgbString();
2827
+ const hueGradient = `linear-gradient(
2828
+ rgb(255,0,0) 0%, rgb(255,255,0) 16.67%,
2829
+ rgb(0,255,0) 33.33%, rgb(0,255,255) 50%,
2830
+ rgb(0,0,255) 66.67%, rgb(255,0,255) 83.33%,
2831
+ rgb(255,0,0) 100%)`;
2832
+ setElementStyle(v1, {
2833
+ background: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,${roundA}) 100%),
2834
+ linear-gradient(to right, rgba(255,255,255,${roundA}) 0%, ${fill} 100%),
2835
+ ${whiteGrad}`,
2836
+ });
2837
+ setElementStyle(v2, { background: hueGradient });
2838
+ } else {
2839
+ const saturation = Math.round((controlPositions.c2y / offsetHeight) * 100);
2840
+ const fill0 = new Color({
2841
+ r: 255, g: 0, b: 0, a: alpha,
2842
+ }).saturate(-saturation).toRgbString();
2843
+ const fill1 = new Color({
2844
+ r: 255, g: 255, b: 0, a: alpha,
2845
+ }).saturate(-saturation).toRgbString();
2846
+ const fill2 = new Color({
2847
+ r: 0, g: 255, b: 0, a: alpha,
2848
+ }).saturate(-saturation).toRgbString();
2849
+ const fill3 = new Color({
2850
+ r: 0, g: 255, b: 255, a: alpha,
2851
+ }).saturate(-saturation).toRgbString();
2852
+ const fill4 = new Color({
2853
+ r: 0, g: 0, b: 255, a: alpha,
2854
+ }).saturate(-saturation).toRgbString();
2855
+ const fill5 = new Color({
2856
+ r: 255, g: 0, b: 255, a: alpha,
2857
+ }).saturate(-saturation).toRgbString();
2858
+ const fill6 = new Color({
2859
+ r: 255, g: 0, b: 0, a: alpha,
2860
+ }).saturate(-saturation).toRgbString();
2861
+ const fillGradient = `linear-gradient(to right,
2862
+ ${fill0} 0%, ${fill1} 16.67%, ${fill2} 33.33%, ${fill3} 50%,
2863
+ ${fill4} 66.67%, ${fill5} 83.33%, ${fill6} 100%)`;
2864
+ const lightGrad = `linear-gradient(rgba(255,255,255,${roundA}) 0%, rgba(255,255,255,0) 50%),
2865
+ linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,${roundA}) 100%)`;
2866
+
2867
+ setElementStyle(v1, { background: `${lightGrad},${fillGradient},${whiteGrad}` });
2868
+ const {
2869
+ r: gr, g: gg, b: gb,
2870
+ } = new Color({ r, g, b }).greyscale().toRgb();
2871
+
2872
+ setElementStyle(v2, {
2873
+ background: `linear-gradient(rgb(${r},${g},${b}) 0%, rgb(${gr},${gg},${gb}) 100%)`,
2874
+ });
2875
+ }
2876
+ setElementStyle(v3, {
2877
+ background: `linear-gradient(rgba(${r},${g},${b},1) 0%,rgba(${r},${g},${b},0) 100%)`,
2878
+ });
2879
+ }
2880
+
2881
+ /**
2882
+ * The `ColorPicker` *focusout* event listener when open.
2883
+ * @param {FocusEvent} e
2884
+ * @this {ColorPicker}
2885
+ */
2886
+ handleFocusOut({ relatedTarget }) {
2887
+ // @ts-ignore
2888
+ if (relatedTarget && !this.parent.contains(relatedTarget)) {
2889
+ this.hide(true);
2890
+ }
2891
+ }
2892
+
2893
+ /**
2894
+ * The `ColorPicker` *keyup* event listener when open.
2895
+ * @param {KeyboardEvent} e
2896
+ * @this {ColorPicker}
2897
+ */
2898
+ handleDismiss({ code }) {
2899
+ const self = this;
2900
+ if (self.isOpen && code === keyEscape) {
2901
+ self.hide();
2902
+ }
2903
+ }
2904
+
2905
+ /**
2906
+ * The `ColorPicker` *scroll* event listener when open.
2907
+ * @param {Event} e
2908
+ * @this {ColorPicker}
2909
+ */
2910
+ handleScroll(e) {
2911
+ const self = this;
2912
+ const { activeElement } = getDocument(self.input);
2913
+
2914
+ if ((isMobile && self.dragElement)
2915
+ || (activeElement && self.controlKnobs.includes(activeElement))) {
2916
+ e.stopPropagation();
2917
+ e.preventDefault();
2918
+ }
2919
+
2920
+ self.updateDropdownPosition();
2921
+ }
2922
+
2923
+ /**
2924
+ * The `ColorPicker` keyboard event listener for menu navigation.
2925
+ * @param {KeyboardEvent} e
2926
+ * @this {ColorPicker}
2927
+ */
2928
+ menuKeyHandler(e) {
2929
+ const { target, code } = e;
2930
+ // @ts-ignore
2931
+ const { previousElementSibling, nextElementSibling, parentElement } = target;
2932
+ const isColorOptionsMenu = parentElement && hasClass(parentElement, 'color-options');
2933
+ const allSiblings = [...parentElement.children];
2934
+ const columnsCount = isColorOptionsMenu
2935
+ && getElementStyle(parentElement, 'grid-template-columns').split(' ').length;
2936
+ const currentIndex = allSiblings.indexOf(target);
2937
+ const previousElement = currentIndex > -1
2938
+ && columnsCount && allSiblings[currentIndex - columnsCount];
2939
+ const nextElement = currentIndex > -1
2940
+ && columnsCount && allSiblings[currentIndex + columnsCount];
2941
+
2942
+ if ([keyArrowDown, keyArrowUp, keySpace].includes(code)) {
2943
+ // prevent scroll when navigating the menu via arrow keys / Space
2944
+ e.preventDefault();
2945
+ }
2946
+ if (isColorOptionsMenu) {
2947
+ if (previousElement && code === keyArrowUp) {
2948
+ focus(previousElement);
2949
+ } else if (nextElement && code === keyArrowDown) {
2950
+ focus(nextElement);
2951
+ } else if (previousElementSibling && code === keyArrowLeft) {
2952
+ focus(previousElementSibling);
2953
+ } else if (nextElementSibling && code === keyArrowRight) {
2954
+ focus(nextElementSibling);
2955
+ }
2956
+ } else if (previousElementSibling && [keyArrowLeft, keyArrowUp].includes(code)) {
2957
+ focus(previousElementSibling);
2958
+ } else if (nextElementSibling && [keyArrowRight, keyArrowDown].includes(code)) {
2959
+ focus(nextElementSibling);
2960
+ }
2961
+
2962
+ if ([keyEnter, keySpace].includes(code)) {
2963
+ this.menuClickHandler({ target });
2964
+ }
2965
+ }
2966
+
2967
+ /**
2968
+ * The `ColorPicker` click event listener for the colour menu presets / defaults.
2969
+ * @param {Partial<Event>} e
2970
+ * @this {ColorPicker}
2971
+ */
2972
+ menuClickHandler(e) {
2973
+ const self = this;
2974
+ /** @type {*} */
2975
+ const { target } = e;
2976
+ const { colorMenu } = self;
2977
+ const newOption = (getAttribute(target, 'data-value') || '').trim();
2978
+ // invalidate for targets other than color options
2979
+ if (!newOption.length) return;
2980
+ const currentActive = querySelector('li.active', colorMenu);
2981
+ let newColor = nonColors.includes(newOption) ? 'white' : newOption;
2982
+ newColor = newOption === 'transparent' ? 'rgba(0,0,0,0)' : newOption;
2983
+
2984
+ const {
2985
+ r, g, b, a,
2986
+ } = new Color(newColor);
2987
+
2988
+ ObjectAssign(self.color, {
2989
+ r, g, b, a,
2990
+ });
2991
+
2992
+ self.update();
2993
+
2994
+ if (currentActive) {
2995
+ removeClass(currentActive, 'active');
2996
+ removeAttribute(currentActive, ariaSelected);
2997
+ }
2998
+
2999
+ if (currentActive !== target) {
3000
+ addClass(target, 'active');
3001
+ setAttribute(target, ariaSelected, 'true');
3002
+
3003
+ if (nonColors.includes(newOption)) {
3004
+ self.value = newOption;
3005
+ }
3006
+ firePickerChange(self);
3007
+ }
3008
+ }
3009
+
3010
+ /**
3011
+ * The `ColorPicker` *touchstart* / *mousedown* events listener for control knobs.
3012
+ * @param {TouchEvent} e
3013
+ * @this {ColorPicker}
3014
+ */
3015
+ pointerDown(e) {
3016
+ const self = this;
3017
+ /** @type {*} */
3018
+ const {
3019
+ type, target, touches, pageX, pageY,
3020
+ } = e;
3021
+ const { colorMenu, visuals, controlKnobs } = self;
3022
+ const [v1, v2, v3] = visuals;
3023
+ const [c1, c2, c3] = controlKnobs;
3024
+ /** @type {HTMLElement} */
3025
+ const visual = hasClass(target, 'visual-control')
3026
+ ? target : querySelector('.visual-control', target.parentElement);
3027
+ const visualRect = getBoundingClientRect(visual);
3028
+ const X = type === 'touchstart' ? touches[0].pageX : pageX;
3029
+ const Y = type === 'touchstart' ? touches[0].pageY : pageY;
3030
+ const offsetX = X - window.pageXOffset - visualRect.left;
3031
+ const offsetY = Y - window.pageYOffset - visualRect.top;
3032
+
3033
+ if (target === v1 || target === c1) {
3034
+ self.dragElement = visual;
3035
+ self.changeControl1(offsetX, offsetY);
3036
+ } else if (target === v2 || target === c2) {
3037
+ self.dragElement = visual;
3038
+ self.changeControl2(offsetY);
3039
+ } else if (target === v3 || target === c3) {
3040
+ self.dragElement = visual;
3041
+ self.changeAlpha(offsetY);
3042
+ }
3043
+
3044
+ if (colorMenu) {
3045
+ const currentActive = querySelector('li.active', colorMenu);
3046
+ if (currentActive) {
3047
+ removeClass(currentActive, 'active');
3048
+ removeAttribute(currentActive, ariaSelected);
3049
+ }
3050
+ }
3051
+ e.preventDefault();
3052
+ }
3053
+
3054
+ /**
3055
+ * The `ColorPicker` *touchend* / *mouseup* events listener for control knobs.
3056
+ * @param {TouchEvent} e
3057
+ * @this {ColorPicker}
3058
+ */
3059
+ pointerUp({ target }) {
3060
+ const self = this;
3061
+ const { parent } = self;
3062
+ const doc = getDocument(parent);
3063
+ const currentOpen = querySelector(`${colorPickerParentSelector}.open`, doc) !== null;
3064
+ const selection = doc.getSelection();
3065
+ // @ts-ignore
3066
+ if (!self.dragElement && !selection.toString().length
3067
+ // @ts-ignore
3068
+ && !parent.contains(target)) {
3069
+ self.hide(currentOpen);
3070
+ }
3071
+
3072
+ self.dragElement = null;
3073
+ }
3074
+
3075
+ /**
3076
+ * The `ColorPicker` *touchmove* / *mousemove* events listener for control knobs.
3077
+ * @param {TouchEvent} e
3078
+ */
3079
+ pointerMove(e) {
3080
+ const self = this;
3081
+ const { dragElement, visuals } = self;
3082
+ const [v1, v2, v3] = visuals;
3083
+ const {
3084
+ // @ts-ignore
3085
+ type, touches, pageX, pageY,
3086
+ } = e;
3087
+
3088
+ if (!dragElement) return;
3089
+
3090
+ const controlRect = getBoundingClientRect(dragElement);
3091
+ const X = type === 'touchmove' ? touches[0].pageX : pageX;
3092
+ const Y = type === 'touchmove' ? touches[0].pageY : pageY;
3093
+ const offsetX = X - window.pageXOffset - controlRect.left;
3094
+ const offsetY = Y - window.pageYOffset - controlRect.top;
3095
+
3096
+ if (dragElement === v1) {
3097
+ self.changeControl1(offsetX, offsetY);
3098
+ }
3099
+
3100
+ if (dragElement === v2) {
3101
+ self.changeControl2(offsetY);
3102
+ }
3103
+
3104
+ if (dragElement === v3) {
3105
+ self.changeAlpha(offsetY);
3106
+ }
3107
+ }
3108
+
3109
+ /**
3110
+ * The `ColorPicker` *keydown* event listener for control knobs.
3111
+ * @param {KeyboardEvent} e
3112
+ */
3113
+ handleKnobs(e) {
3114
+ const { target, code } = e;
3115
+ const self = this;
3116
+
3117
+ // only react to arrow buttons
3118
+ if (![keyArrowUp, keyArrowDown, keyArrowLeft, keyArrowRight].includes(code)) return;
3119
+ e.preventDefault();
3120
+
3121
+ const { controlKnobs } = self;
3122
+ const [c1, c2, c3] = controlKnobs;
3123
+ const { activeElement } = getDocument(c1);
3124
+ const currentKnob = controlKnobs.find((x) => x === activeElement);
3125
+
3126
+ if (currentKnob) {
3127
+ let offsetX = 0;
3128
+ let offsetY = 0;
3129
+ if (target === c1) {
3130
+ if ([keyArrowLeft, keyArrowRight].includes(code)) {
3131
+ self.controlPositions.c1x += code === keyArrowRight ? +1 : -1;
3132
+ } else if ([keyArrowUp, keyArrowDown].includes(code)) {
3133
+ self.controlPositions.c1y += code === keyArrowDown ? +1 : -1;
3134
+ }
3135
+
3136
+ offsetX = self.controlPositions.c1x;
3137
+ offsetY = self.controlPositions.c1y;
3138
+ self.changeControl1(offsetX, offsetY);
3139
+ } else if (target === c2) {
3140
+ self.controlPositions.c2y += [keyArrowDown, keyArrowRight].includes(code) ? +1 : -1;
3141
+ offsetY = self.controlPositions.c2y;
3142
+ self.changeControl2(offsetY);
3143
+ } else if (target === c3) {
3144
+ self.controlPositions.c3y += [keyArrowDown, keyArrowRight].includes(code) ? +1 : -1;
3145
+ offsetY = self.controlPositions.c3y;
3146
+ self.changeAlpha(offsetY);
3147
+ }
3148
+ self.handleScroll(e);
3149
+ }
3150
+ }
3151
+
3152
+ /** The event listener of the colour form inputs. */
3153
+ changeHandler() {
3154
+ const self = this;
3155
+ let colorSource;
3156
+ const {
3157
+ inputs, format, value: currentValue, input, controlPositions, visuals,
3158
+ } = self;
3159
+ /** @type {*} */
3160
+ const { activeElement } = getDocument(input);
3161
+ const { offsetHeight } = visuals[0];
3162
+ const [i1,,, i4] = inputs;
3163
+ const [v1, v2, v3, v4] = format === 'rgb'
3164
+ ? inputs.map((i) => parseFloat(i.value) / (i === i4 ? 100 : 1))
3165
+ : inputs.map((i) => parseFloat(i.value) / (i !== i1 ? 100 : 360));
3166
+ const isNonColorValue = self.includeNonColor && nonColors.includes(currentValue);
3167
+ const alpha = i4 ? v4 : (1 - controlPositions.c3y / offsetHeight);
3168
+
3169
+ if (activeElement === input || (activeElement && inputs.includes(activeElement))) {
3170
+ if (activeElement === input) {
3171
+ if (isNonColorValue) {
3172
+ colorSource = 'white';
3173
+ } else {
3174
+ colorSource = currentValue;
3175
+ }
3176
+ } else if (format === 'hex') {
3177
+ colorSource = i1.value;
3178
+ } else if (format === 'hsl') {
3179
+ colorSource = {
3180
+ h: v1, s: v2, l: v3, a: alpha,
3181
+ };
3182
+ } else if (format === 'hwb') {
3183
+ colorSource = {
3184
+ h: v1, w: v2, b: v3, a: alpha,
3185
+ };
3186
+ } else {
3187
+ colorSource = {
3188
+ r: v1, g: v2, b: v3, a: alpha,
3189
+ };
3190
+ }
3191
+
3192
+ const {
3193
+ r, g, b, a,
3194
+ } = new Color(colorSource);
3195
+
3196
+ ObjectAssign(self.color, {
3197
+ r, g, b, a,
3198
+ });
3199
+ self.setControlPositions();
3200
+ self.updateAppearance();
3201
+ self.updateInputs();
3202
+ self.updateControls();
3203
+ self.updateVisuals();
3204
+
3205
+ // set non-color keyword
3206
+ if (activeElement === input && isNonColorValue) {
3207
+ self.value = currentValue;
3208
+ }
3209
+ }
3210
+ }
3211
+
3212
+ /**
3213
+ * Updates `ColorPicker` first control:
3214
+ * * `lightness` and `saturation` for HEX/RGB;
3215
+ * * `lightness` and `hue` for HSL.
3216
+ *
3217
+ * @param {number} X the X component of the offset
3218
+ * @param {number} Y the Y component of the offset
3219
+ */
3220
+ changeControl1(X, Y) {
3221
+ const self = this;
3222
+ let [offsetX, offsetY] = [0, 0];
3223
+ const {
3224
+ format, controlPositions, visuals,
3225
+ } = self;
3226
+ const { offsetHeight, offsetWidth } = visuals[0];
3227
+
3228
+ if (X > offsetWidth) offsetX = offsetWidth;
3229
+ else if (X >= 0) offsetX = X;
3230
+
3231
+ if (Y > offsetHeight) offsetY = offsetHeight;
3232
+ else if (Y >= 0) offsetY = Y;
3233
+
3234
+ const hue = format === 'hsl'
3235
+ ? offsetX / offsetWidth
3236
+ : controlPositions.c2y / offsetHeight;
3237
+
3238
+ const saturation = format === 'hsl'
3239
+ ? 1 - controlPositions.c2y / offsetHeight
3240
+ : offsetX / offsetWidth;
3241
+
3242
+ const lightness = 1 - offsetY / offsetHeight;
3243
+ const alpha = 1 - controlPositions.c3y / offsetHeight;
3244
+
3245
+ const colorObject = format === 'hsl'
3246
+ ? {
3247
+ h: hue, s: saturation, l: lightness, a: alpha,
3248
+ }
3249
+ : {
3250
+ h: hue, s: saturation, v: lightness, a: alpha,
3251
+ };
3252
+
3253
+ // new color
3254
+ const {
3255
+ r, g, b, a,
3256
+ } = new Color(colorObject);
3257
+
3258
+ ObjectAssign(self.color, {
3259
+ r, g, b, a,
3260
+ });
3261
+
3262
+ // new positions
3263
+ self.controlPositions.c1x = offsetX;
3264
+ self.controlPositions.c1y = offsetY;
3265
+
3266
+ // update color picker
3267
+ self.updateAppearance();
3268
+ self.updateInputs();
3269
+ self.updateControls();
3270
+ self.updateVisuals();
3271
+ }
3272
+
3273
+ /**
3274
+ * Updates `ColorPicker` second control:
3275
+ * * `hue` for HEX/RGB/HWB;
3276
+ * * `saturation` for HSL.
3277
+ *
3278
+ * @param {number} Y the Y offset
3279
+ */
3280
+ changeControl2(Y) {
3281
+ const self = this;
3282
+ const {
3283
+ format, controlPositions, visuals,
3284
+ } = self;
3285
+ const { offsetHeight, offsetWidth } = visuals[0];
3286
+
3287
+ let offsetY = 0;
3288
+
3289
+ if (Y > offsetHeight) offsetY = offsetHeight;
3290
+ else if (Y >= 0) offsetY = Y;
3291
+
3292
+ const hue = format === 'hsl'
3293
+ ? controlPositions.c1x / offsetWidth
3294
+ : offsetY / offsetHeight;
3295
+ const saturation = format === 'hsl'
3296
+ ? 1 - offsetY / offsetHeight
3297
+ : controlPositions.c1x / offsetWidth;
3298
+ const lightness = 1 - controlPositions.c1y / offsetHeight;
3299
+ const alpha = 1 - controlPositions.c3y / offsetHeight;
3300
+ const colorObject = format === 'hsl'
3301
+ ? {
3302
+ h: hue, s: saturation, l: lightness, a: alpha,
3303
+ }
3304
+ : {
3305
+ h: hue, s: saturation, v: lightness, a: alpha,
3306
+ };
3307
+
3308
+ // new color
3309
+ const {
3310
+ r, g, b, a,
3311
+ } = new Color(colorObject);
3312
+
3313
+ ObjectAssign(self.color, {
3314
+ r, g, b, a,
3315
+ });
3316
+
3317
+ // new position
3318
+ self.controlPositions.c2y = offsetY;
3319
+ // update color picker
3320
+ self.updateAppearance();
3321
+ self.updateInputs();
3322
+ self.updateControls();
3323
+ self.updateVisuals();
3324
+ }
3325
+
3326
+ /**
3327
+ * Updates `ColorPicker` last control,
3328
+ * the `alpha` channel.
3329
+ *
3330
+ * @param {number} Y
3331
+ */
3332
+ changeAlpha(Y) {
3333
+ const self = this;
3334
+ const { visuals } = self;
3335
+ const { offsetHeight } = visuals[0];
3336
+ let offsetY = 0;
3337
+
3338
+ if (Y > offsetHeight) offsetY = offsetHeight;
3339
+ else if (Y >= 0) offsetY = Y;
3340
+
3341
+ // update color alpha
3342
+ const alpha = 1 - offsetY / offsetHeight;
3343
+ self.color.setAlpha(alpha);
3344
+ // update position
3345
+ self.controlPositions.c3y = offsetY;
3346
+ // update color picker
3347
+ self.updateAppearance();
3348
+ self.updateInputs();
3349
+ self.updateControls();
3350
+ self.updateVisuals();
3351
+ }
3352
+
3353
+ /**
3354
+ * Updates `ColorPicker` control positions on:
3355
+ * * initialization
3356
+ * * window resize
3357
+ */
3358
+ update() {
3359
+ const self = this;
3360
+ self.updateDropdownPosition();
3361
+ self.updateAppearance();
3362
+ self.setControlPositions();
3363
+ self.updateInputs(true);
3364
+ self.updateControls();
3365
+ self.updateVisuals();
3366
+ }
3367
+
3368
+ /** Updates the open dropdown position on *scroll* event. */
3369
+ updateDropdownPosition() {
3370
+ const self = this;
3371
+ const { input, colorPicker, colorMenu } = self;
3372
+ const elRect = getBoundingClientRect(input);
3373
+ const { top, bottom } = elRect;
3374
+ const { offsetHeight: elHeight } = input;
3375
+ const windowHeight = getDocumentElement(input).clientHeight;
3376
+ const isPicker = hasClass(colorPicker, 'show');
3377
+ const dropdown = isPicker ? colorPicker : colorMenu;
3378
+ if (!dropdown) return;
3379
+ const { offsetHeight: dropHeight } = dropdown;
3380
+ const distanceBottom = windowHeight - bottom;
3381
+ const distanceTop = top;
3382
+ const bottomExceed = top + dropHeight + elHeight > windowHeight; // show
3383
+ const topExceed = top - dropHeight < 0; // show-top
3384
+
3385
+ if ((hasClass(dropdown, 'bottom') || !topExceed) && distanceBottom < distanceTop && bottomExceed) {
3386
+ removeClass(dropdown, 'bottom');
3387
+ addClass(dropdown, 'top');
3388
+ } else {
3389
+ removeClass(dropdown, 'top');
3390
+ addClass(dropdown, 'bottom');
3391
+ }
3392
+ }
3393
+
3394
+ /** Updates control knobs' positions. */
3395
+ setControlPositions() {
3396
+ const self = this;
3397
+ const {
3398
+ format, visuals, color, hsl, hsv,
3399
+ } = self;
3400
+ const { offsetHeight, offsetWidth } = visuals[0];
3401
+ const alpha = color.a;
3402
+ const hue = hsl.h;
3403
+
3404
+ const saturation = format !== 'hsl' ? hsv.s : hsl.s;
3405
+ const lightness = format !== 'hsl' ? hsv.v : hsl.l;
3406
+
3407
+ self.controlPositions.c1x = format !== 'hsl' ? saturation * offsetWidth : hue * offsetWidth;
3408
+ self.controlPositions.c1y = (1 - lightness) * offsetHeight;
3409
+ self.controlPositions.c2y = format !== 'hsl' ? hue * offsetHeight : (1 - saturation) * offsetHeight;
3410
+ self.controlPositions.c3y = (1 - alpha) * offsetHeight;
3411
+ }
3412
+
3413
+ /** Update the visual appearance label and control knob labels. */
3414
+ updateAppearance() {
3415
+ const self = this;
3416
+ const {
3417
+ componentLabels, colorLabels, color, parent,
3418
+ hsl, hsv, hex, format, controlKnobs,
3419
+ } = self;
3420
+ const {
3421
+ appearanceLabel, hexLabel, valueLabel,
3422
+ } = componentLabels;
3423
+ const { r, g, b } = color.toRgb();
3424
+ const [knob1, knob2, knob3] = controlKnobs;
3425
+ const hue = Math.round(hsl.h * 360);
3426
+ const alpha = color.a;
3427
+ const saturationSource = format === 'hsl' ? hsl.s : hsv.s;
3428
+ const saturation = Math.round(saturationSource * 100);
3429
+ const lightness = Math.round(hsl.l * 100);
3430
+ const hsvl = hsv.v * 100;
3431
+ let colorName;
3432
+
3433
+ // determine color appearance
3434
+ if (lightness === 100 && saturation === 0) {
3435
+ colorName = colorLabels.white;
3436
+ } else if (lightness === 0) {
3437
+ colorName = colorLabels.black;
3438
+ } else if (saturation === 0) {
3439
+ colorName = colorLabels.grey;
3440
+ } else if (hue < 15 || hue >= 345) {
3441
+ colorName = colorLabels.red;
3442
+ } else if (hue >= 15 && hue < 45) {
3443
+ colorName = hsvl > 80 && saturation > 80 ? colorLabels.orange : colorLabels.brown;
3444
+ } else if (hue >= 45 && hue < 75) {
3445
+ const isGold = hue > 46 && hue < 54 && hsvl < 80 && saturation > 90;
3446
+ const isOlive = hue >= 54 && hue < 75 && hsvl < 80;
3447
+ colorName = isGold ? colorLabels.gold : colorLabels.yellow;
3448
+ colorName = isOlive ? colorLabels.olive : colorName;
3449
+ } else if (hue >= 75 && hue < 155) {
3450
+ colorName = hsvl < 68 ? colorLabels.green : colorLabels.lime;
3451
+ } else if (hue >= 155 && hue < 175) {
3452
+ colorName = colorLabels.teal;
3453
+ } else if (hue >= 175 && hue < 195) {
3454
+ colorName = colorLabels.cyan;
3455
+ } else if (hue >= 195 && hue < 255) {
3456
+ colorName = colorLabels.blue;
3457
+ } else if (hue >= 255 && hue < 270) {
3458
+ colorName = colorLabels.violet;
3459
+ } else if (hue >= 270 && hue < 295) {
3460
+ colorName = colorLabels.magenta;
3461
+ } else if (hue >= 295 && hue < 345) {
3462
+ colorName = colorLabels.pink;
3463
+ }
3464
+
3465
+ let colorLabel = `${hexLabel} ${hex.split('').join(' ')}`;
3466
+
3467
+ if (format === 'hsl') {
3468
+ colorLabel = `HSL: ${hue}°, ${saturation}%, ${lightness}%`;
3469
+ setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3470
+ setAttribute(knob1, ariaValueText, `${hue}° & ${lightness}%`);
3471
+ setAttribute(knob1, ariaValueNow, `${hue}`);
3472
+ setAttribute(knob2, ariaValueText, `${saturation}%`);
3473
+ setAttribute(knob2, ariaValueNow, `${saturation}`);
3474
+ } else if (format === 'hwb') {
3475
+ const { hwb } = self;
3476
+ const whiteness = Math.round(hwb.w * 100);
3477
+ const blackness = Math.round(hwb.b * 100);
3478
+ colorLabel = `HWB: ${hue}°, ${whiteness}%, ${blackness}%`;
3479
+ setAttribute(knob1, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3480
+ setAttribute(knob1, ariaValueText, `${whiteness}% & ${blackness}%`);
3481
+ setAttribute(knob1, ariaValueNow, `${whiteness}`);
3482
+ setAttribute(knob2, ariaValueText, `${hue}%`);
3483
+ setAttribute(knob2, ariaValueNow, `${hue}`);
3484
+ } else {
3485
+ colorLabel = format === 'rgb' ? `RGB: ${r}, ${g}, ${b}` : colorLabel;
3486
+ setAttribute(knob2, ariaDescription, `${valueLabel}: ${colorLabel}. ${appearanceLabel}: ${colorName}.`);
3487
+ setAttribute(knob1, ariaValueText, `${lightness}% & ${saturation}%`);
3488
+ setAttribute(knob1, ariaValueNow, `${lightness}`);
3489
+ setAttribute(knob2, ariaValueText, `${hue}°`);
3490
+ setAttribute(knob2, ariaValueNow, `${hue}`);
3491
+ }
3492
+
3493
+ const alphaValue = Math.round(alpha * 100);
3494
+ setAttribute(knob3, ariaValueText, `${alphaValue}%`);
3495
+ setAttribute(knob3, ariaValueNow, `${alphaValue}`);
3496
+
3497
+ // update the input backgroundColor
3498
+ const newColor = color.toString();
3499
+ setElementStyle(self.input, { backgroundColor: newColor });
3500
+
3501
+ // toggle dark/light classes will also style the placeholder
3502
+ // dark sets color white, light sets color black
3503
+ // isDark ? '#000' : '#fff'
3504
+ if (!self.isDark) {
3505
+ if (hasClass(parent, 'txt-dark')) removeClass(parent, 'txt-dark');
3506
+ if (!hasClass(parent, 'txt-light')) addClass(parent, 'txt-light');
3507
+ } else {
3508
+ if (hasClass(parent, 'txt-light')) removeClass(parent, 'txt-light');
3509
+ if (!hasClass(parent, 'txt-dark')) addClass(parent, 'txt-dark');
3510
+ }
3511
+ }
3512
+
3513
+ /** Updates the control knobs actual positions. */
3514
+ updateControls() {
3515
+ const { controlKnobs, controlPositions } = this;
3516
+ const [control1, control2, control3] = controlKnobs;
3517
+ setElementStyle(control1, { transform: `translate3d(${controlPositions.c1x - 4}px,${controlPositions.c1y - 4}px,0)` });
3518
+ setElementStyle(control2, { transform: `translate3d(0,${controlPositions.c2y - 4}px,0)` });
3519
+ setElementStyle(control3, { transform: `translate3d(0,${controlPositions.c3y - 4}px,0)` });
3520
+ }
3521
+
3522
+ /**
3523
+ * Updates all color form inputs.
3524
+ * @param {boolean=} isPrevented when `true`, the component original event is prevented
3525
+ */
3526
+ updateInputs(isPrevented) {
3527
+ const self = this;
3528
+ const {
3529
+ value: oldColor, format, inputs, color, hsl,
3530
+ } = self;
3531
+ const [i1, i2, i3, i4] = inputs;
3532
+ const alpha = Math.round(color.a * 100);
3533
+ const hue = Math.round(hsl.h * 360);
3534
+ let newColor;
3535
+
3536
+ if (format === 'hex') {
3537
+ newColor = self.color.toHexString(true);
3538
+ i1.value = self.hex;
3539
+ } else if (format === 'hsl') {
3540
+ const lightness = Math.round(hsl.l * 100);
3541
+ const saturation = Math.round(hsl.s * 100);
3542
+ newColor = self.color.toHslString();
3543
+ i1.value = `${hue}`;
3544
+ i2.value = `${saturation}`;
3545
+ i3.value = `${lightness}`;
3546
+ i4.value = `${alpha}`;
3547
+ } else if (format === 'hwb') {
3548
+ const { w, b } = self.hwb;
3549
+ const whiteness = Math.round(w * 100);
3550
+ const blackness = Math.round(b * 100);
3551
+
3552
+ newColor = self.color.toHwbString();
3553
+ i1.value = `${hue}`;
3554
+ i2.value = `${whiteness}`;
3555
+ i3.value = `${blackness}`;
3556
+ i4.value = `${alpha}`;
3557
+ } else if (format === 'rgb') {
3558
+ const { r, g, b } = self.rgb;
3559
+
3560
+ newColor = self.color.toRgbString();
3561
+ i1.value = `${r}`;
3562
+ i2.value = `${g}`;
3563
+ i3.value = `${b}`;
3564
+ i4.value = `${alpha}`;
3565
+ }
3566
+
3567
+ // update the color value
3568
+ self.value = `${newColor}`;
3569
+
3570
+ // don't trigger the custom event unless it's really changed
3571
+ if (!isPrevented && newColor !== oldColor) {
3572
+ firePickerChange(self);
3573
+ }
3574
+ }
3575
+
3576
+ /**
3577
+ * The `Space` & `Enter` keys specific event listener.
3578
+ * Toggle visibility of the `ColorPicker` / the presets menu, showing one will hide the other.
3579
+ * @param {KeyboardEvent} e
3580
+ * @this {ColorPicker}
3581
+ */
3582
+ keyToggle(e) {
3583
+ const self = this;
3584
+ const { menuToggle } = self;
3585
+ const { activeElement } = getDocument(menuToggle);
3586
+ const { code } = e;
3587
+
3588
+ if ([keyEnter, keySpace].includes(code)) {
3589
+ if ((menuToggle && activeElement === menuToggle) || !activeElement) {
3590
+ e.preventDefault();
3591
+ if (!activeElement) {
3592
+ self.togglePicker(e);
3593
+ } else {
3594
+ self.toggleMenu();
3595
+ }
3596
+ }
3597
+ }
3598
+ }
3599
+
3600
+ /**
3601
+ * Toggle the `ColorPicker` dropdown visibility.
3602
+ * @param {Event} e
3603
+ * @this {ColorPicker}
3604
+ */
3605
+ togglePicker(e) {
3606
+ e.preventDefault();
3607
+ const self = this;
3608
+ const { colorPicker } = self;
3609
+
3610
+ if (self.isOpen && hasClass(colorPicker, 'show')) {
3611
+ self.hide(true);
3612
+ } else {
3613
+ showDropdown(self, colorPicker);
3614
+ }
3615
+ }
3616
+
3617
+ /** Shows the `ColorPicker` dropdown. */
3618
+ showPicker() {
3619
+ const self = this;
3620
+ const { colorPicker } = self;
3621
+
3622
+ if (!hasClass(colorPicker, 'show')) {
3623
+ showDropdown(self, colorPicker);
3624
+ }
3625
+ }
3626
+
3627
+ /** Toggles the visibility of the `ColorPicker` presets menu. */
3628
+ toggleMenu() {
3629
+ const self = this;
3630
+ const { colorMenu } = self;
3631
+
3632
+ if (self.isOpen && hasClass(colorMenu, 'show')) {
3633
+ self.hide(true);
3634
+ } else {
3635
+ showDropdown(self, colorMenu);
3636
+ }
3637
+ }
3638
+
3639
+ /** Shows the `ColorPicker` dropdown or the presets menu. */
3640
+ show() {
3641
+ const self = this;
3642
+ const { menuToggle } = self;
3643
+ if (!self.isOpen) {
3644
+ toggleEventsOnShown(self, true);
3645
+ self.updateDropdownPosition();
3646
+ self.isOpen = true;
3647
+ setAttribute(self.input, 'tabindex', '0');
3648
+ if (menuToggle) {
3649
+ setAttribute(menuToggle, 'tabindex', '0');
3650
+ }
3651
+ }
3652
+ }
3653
+
3654
+ /**
3655
+ * Hides the currently open `ColorPicker` dropdown.
3656
+ * @param {boolean=} focusPrevented
3657
+ */
3658
+ hide(focusPrevented) {
3659
+ const self = this;
3660
+ if (self.isOpen) {
3661
+ const {
3662
+ pickerToggle, menuToggle, colorPicker, colorMenu, parent, input,
3663
+ } = self;
3664
+ const openPicker = hasClass(colorPicker, 'show');
3665
+ const openDropdown = openPicker ? colorPicker : colorMenu;
3666
+ const relatedBtn = openPicker ? pickerToggle : menuToggle;
3667
+ const animationDuration = openDropdown && getElementTransitionDuration(openDropdown);
3668
+
3669
+ if (openDropdown) {
3670
+ removeClass(openDropdown, 'show');
3671
+ setAttribute(relatedBtn, ariaExpanded, 'false');
3672
+ setTimeout(() => {
3673
+ removePosition(openDropdown);
3674
+ if (!querySelector('.show', parent)) {
3675
+ removeClass(parent, 'open');
3676
+ toggleEventsOnShown(self);
3677
+ self.isOpen = false;
3678
+ }
3679
+ }, animationDuration);
3680
+ }
3681
+
3682
+ if (!self.isValid) {
3683
+ self.value = self.color.toString();
3684
+ }
3685
+ if (!focusPrevented) {
3686
+ focus(pickerToggle);
3687
+ }
3688
+ setAttribute(input, 'tabindex', '-1');
3689
+ if (menuToggle) {
3690
+ setAttribute(menuToggle, 'tabindex', '-1');
3691
+ }
3692
+ }
3693
+ }
3694
+
3695
+ /** Removes `ColorPicker` from target `<input>`. */
3696
+ dispose() {
3697
+ const self = this;
3698
+ const { input, parent } = self;
3699
+ self.hide(true);
3700
+ toggleEvents(self);
3701
+ [...parent.children].forEach((el) => {
3702
+ if (el !== input) el.remove();
3703
+ });
3704
+ setElementStyle(input, { backgroundColor: '' });
3705
+ ['txt-light', 'txt-dark'].forEach((c) => removeClass(parent, c));
3706
+ Data.remove(input, colorPickerString);
3707
+ }
3708
+ }
3709
+
3710
+ ObjectAssign(ColorPicker, {
3711
+ Color,
3712
+ Version,
3713
+ getInstance: getColorPickerInstance,
3714
+ init: initColorPicker,
3715
+ selector: colorPickerSelector,
3716
+ });
3717
+
3718
+ let CPID = 0;
3719
+
3720
+ /**
3721
+ * `ColorPickerElement` Web Component.
3722
+ * @example
3723
+ * <color-picker>
3724
+ * <input type="text">
3725
+ * </color-picker>
3726
+ */
3727
+ class ColorPickerElement extends HTMLElement {
3728
+ constructor() {
3729
+ super();
3730
+ /** @type {boolean} */
3731
+ this.isDisconnected = true;
3732
+ this.attachShadow({ mode: 'open' });
3733
+ }
3734
+
3735
+ /**
3736
+ * Returns the current color value.
3737
+ * @returns {string?}
3738
+ */
3739
+ get value() { return this.input ? this.input.value : null; }
3740
+
3741
+ connectedCallback() {
3742
+ if (this.colorPicker) {
3743
+ if (this.isDisconnected) {
3744
+ this.isDisconnected = false;
3745
+ }
3746
+ return;
3747
+ }
3748
+
3749
+ const inputs = getElementsByTagName('input', this);
3750
+
3751
+ if (!inputs.length) {
3752
+ const label = getAttribute(this, 'data-label');
3753
+ const value = getAttribute(this, 'data-value') || '#069';
3754
+ const format = getAttribute(this, 'data-format') || 'rgb';
3755
+ const newInput = createElement({
3756
+ tagName: 'input',
3757
+ type: 'text',
3758
+ className: 'color-preview btn-appearance',
3759
+ });
3760
+ let id = getAttribute(this, 'data-id');
3761
+ if (!id) {
3762
+ id = `color-picker-${format}-${CPID}`;
3763
+ CPID += 1;
3764
+ }
3765
+
3766
+ const labelElement = createElement({ tagName: 'label', innerText: label || 'Color Picker' });
3767
+ this.before(labelElement);
3768
+ setAttribute(labelElement, 'for', id);
3769
+ setAttribute(newInput, 'id', id);
3770
+ setAttribute(newInput, 'name', id);
3771
+ setAttribute(newInput, 'autocomplete', 'off');
3772
+ setAttribute(newInput, 'spellcheck', 'false');
3773
+ setAttribute(newInput, 'value', value);
3774
+ this.append(newInput);
3775
+ }
3776
+
3777
+ const [input] = inputs;
3778
+
3779
+ if (input) {
3780
+ /** @type {HTMLInputElement} */
3781
+ // @ts-ignore - `HTMLInputElement` is `HTMLElement`
3782
+ this.input = input;
3783
+
3784
+ // @ts-ignore - `HTMLInputElement` is `HTMLElement`
3785
+ this.colorPicker = new ColorPicker(input);
3786
+ this.color = this.colorPicker.color;
3787
+
3788
+ if (this.shadowRoot) {
3789
+ this.shadowRoot.append(createElement('slot'));
3790
+ }
3791
+
3792
+ this.isDisconnected = false;
3793
+ }
3794
+ }
3795
+
3796
+ disconnectedCallback() {
3797
+ if (this.colorPicker) this.colorPicker.dispose();
3798
+ this.isDisconnected = true;
3799
+ }
3800
+ }
3801
+
3802
+ ObjectAssign(ColorPickerElement, {
3803
+ Color,
3804
+ ColorPicker,
3805
+ Version,
3806
+ });
3807
+
3808
+ customElements.define('color-picker', ColorPickerElement);
3809
+
3810
+ export default ColorPickerElement;