|
1 | 1 | // ==UserScript==
|
2 | 2 | // @name GitHub Label Color Picker
|
3 |
| -// @version 1.0.7 |
| 3 | +// @version 1.0.8 |
4 | 4 | // @description A userscript that adds a color picker to the label color input
|
5 | 5 | // @license MIT
|
6 | 6 | // @author Rob Garrison
|
|
13 | 13 | // @grant GM_setValue
|
14 | 14 | // @grant GM_registerMenuCommand
|
15 | 15 | // @require https://greasyfork.org/scripts/23181-colorpicker/code/colorPicker.js?version=147862
|
| 16 | +// @require https://greasyfork.org/scripts/398877-utils-js/code/utilsjs.js?version=952600 |
16 | 17 | // @icon https://github.githubassets.com/pinned-octocat.svg
|
17 | 18 | // @updateURL https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-label-color-picker.user.js
|
18 | 19 | // @downloadURL https://raw.githubusercontent.com/Mottie/GitHub-userscripts/master/github-label-color-picker.user.js
|
19 | 20 | // @supportURL https://github.com/Mottie/GitHub-userscripts/issues
|
20 | 21 | // ==/UserScript==
|
21 |
| -/* global jsColorPicker */ |
| 22 | +/* global jsColorPicker $ on */ |
22 | 23 | (() => {
|
23 | 24 | "use strict";
|
24 | 25 |
|
25 |
| - // GitHub-Dark changes "text-black" to #c0c0c0 |
26 | 26 | GM_addStyle(`
|
27 |
| - div.cp-app { margin:0; z-index:10; } |
| 27 | + div.cp-app { margin:100px 0 0 -7px; z-index:10; } |
28 | 28 | .js-new-label-color-icon { pointer-events:none; }
|
29 |
| - .js-new-label-color-icon.text-black { color:#000 !important; } |
| 29 | + .js-new-label-color-icon.color-scale-black { color:#000 !important; } |
30 | 30 | `);
|
31 | 31 |
|
32 | 32 | function addPicker() {
|
|
35 | 35 | customBG: "#222",
|
36 | 36 | noAlpha: true,
|
37 | 37 | renderCallback: function(colors) {
|
38 |
| - let input = this && this.input; |
| 38 | + const input = this && this.input; |
39 | 39 | if (input) {
|
40 | 40 | updateSwatch(input, colors);
|
41 | 41 | }
|
|
45 | 45 | }
|
46 | 46 |
|
47 | 47 | function updateSwatch(input, colors) {
|
48 |
| - let background = "#" + colors.HEX; |
49 |
| - input.value = background; |
50 |
| - let textColor = calcContrast(colors.HEX); |
| 48 | + input.value = colors.HEX; |
| 49 | + const colorStyle = calcStyle(colors.rgb, colors.hsl); |
| 50 | + |
51 | 51 | // Update color swatch next to input
|
52 |
| - let swatch = $(".js-new-label-color", input.closest("dd")); |
53 |
| - updateIcon(swatch, textColor); |
54 |
| - updateColors(swatch, background, textColor); |
| 52 | + const inputSwatch = $(".js-new-label-color", input.closest("dd")); |
| 53 | + inputSwatch.style = colorStyle; |
| 54 | + |
55 | 55 | // Update label preview
|
56 |
| - swatch = $( |
| 56 | + const labelSwatch = $( |
57 | 57 | ".js-label-preview .IssueLabel--big",
|
58 |
| - input.closest(".table-list-item") |
| 58 | + input.closest(".Box-row") |
59 | 59 | );
|
60 |
| - updateColors(swatch, background, textColor); |
61 |
| - } |
62 |
| - |
63 |
| - function updateIcon(swatch, textColor) { |
64 |
| - let icon = $(".octicon", swatch); |
65 |
| - // !important set on these GitHub primer color definitions |
66 |
| - icon.classList.remove("text-white", "text-black"); |
67 |
| - icon.classList.add("text-" + textColor); |
| 60 | + labelSwatch.style = colorStyle; |
68 | 61 | }
|
69 | 62 |
|
70 |
| - function updateColors(el, background, color) { |
71 |
| - el.style.backgroundColor = background; |
72 |
| - el.style.color = color; |
| 63 | + function calcStyle(rgb, hsl) { |
| 64 | + // GitHub adds CSS variables to the wrapper |
| 65 | + // rgb is used as the foreground (text) color |
| 66 | + // hsl is used to calculate a color variant for the background |
| 67 | + const multiplier = { h: 360, s: 100, l: 100 }; |
| 68 | + const fg = Object.entries(rgb).map( |
| 69 | + ([c, v]) => `--label-${c}:${(v * 255).toFixed(0)}` |
| 70 | + ); |
| 71 | + const bg = Object.entries(hsl).map( |
| 72 | + ([c, v]) => `--label-${c}:${(v * multiplier[c]).toFixed(0)}` |
| 73 | + ); |
| 74 | + // --label-r:255; --label-g:255; --label-b:255; --label-h:15; --label-s:0; --label-l:100; |
| 75 | + return `${fg.join("; ")}; ${bg.join("; ")}`; |
73 | 76 | }
|
74 | 77 |
|
75 | 78 | /* replace colorPicker storage */
|
|
84 | 87 | "'rgba(83,25,231,1)','rgba(86,66,66,1)','rgba(22,20,223,1)'"
|
85 | 88 | */
|
86 | 89 | function convertColorsToRgba(values) {
|
87 |
| - let result = []; |
88 | 90 | // see http://stackoverflow.com/a/26196012/145346
|
89 |
| - values |
| 91 | + return values |
90 | 92 | .replace(/['"]/g, "")
|
91 | 93 | .split(/\s*,(?![^()]*(?:\([^()]*\))?\))\s*/g)
|
92 |
| - .forEach(val => { |
93 |
| - let rgb = hexToRgb(val); |
| 94 | + .map(val => { |
| 95 | + const rgb = hexToRgb(val); |
94 | 96 | if (rgb) {
|
95 |
| - result.push(`'rgba(${rgb.r},${rgb.g},${rgb.b},1)'`); |
| 97 | + return `'rgba(${rgb.r},${rgb.g},${rgb.b},1)'`; |
96 | 98 | } else if (rgb === null && val.indexOf("rgba(") > -1) {
|
97 | 99 | // allow adding rgba() definitions
|
98 |
| - result.push(`'${val}'`); |
| 100 | + return`'${val}'`; |
99 | 101 | }
|
100 |
| - }); |
101 |
| - return result.join(","); |
| 102 | + }) |
| 103 | + .filter(Boolean) |
| 104 | + .join(","); |
102 | 105 | }
|
103 | 106 |
|
104 | 107 | // Modified code from http://stackoverflow.com/a/5624139/145346
|
| 108 | + // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") |
| 109 | + const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; |
105 | 110 | function hexToRgb(hex) {
|
106 |
| - let result, |
107 |
| - // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF") |
108 |
| - shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i; |
109 |
| - hex = hex.replace(shorthandRegex, (m, r, g, b) => { |
| 111 | + const modHex = hex.replace(shorthandRegex, (_, r, g, b) => { |
110 | 112 | return r + r + g + g + b + b;
|
111 | 113 | });
|
112 |
| - result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); |
| 114 | + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(modHex); |
113 | 115 | return result ? {
|
114 | 116 | r: parseInt(result[1], 16),
|
115 | 117 | g: parseInt(result[2], 16),
|
116 | 118 | b: parseInt(result[3], 16)
|
117 | 119 | } : null;
|
118 | 120 | }
|
119 | 121 |
|
120 |
| - // Calculate contrasting text color for the given background color |
121 |
| - // https://24ways.org/2010/calculating-color-contrast/ |
122 |
| - function calcContrast(hex) { |
123 |
| - const r = parseInt(hex.substr(0, 2), 16), |
124 |
| - g = parseInt(hex.substr(2, 2), 16), |
125 |
| - b = parseInt(hex.substr(4, 2), 16), |
126 |
| - yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000; |
127 |
| - return yiq >= 128 ? "black" : "white"; |
128 |
| - } |
129 |
| - |
130 |
| - function $(selector, el) { |
131 |
| - return (el || document).querySelector(selector); |
132 |
| - } |
133 |
| - |
134 | 122 | // Add GM options
|
135 | 123 | GM_registerMenuCommand(
|
136 | 124 | "Set label ColorPicker swatches (8 HEX or RGBA Max)",
|
|
143 | 131 | }
|
144 | 132 | );
|
145 | 133 |
|
146 |
| - document.body.addEventListener("click", event => { |
| 134 | + on(document.body, "click", event => { |
147 | 135 | // initialize if "Edit" or "New label" button clicked
|
148 | 136 | // because "Save changes" updates the entire item
|
149 | 137 | if (
|
150 |
| - event.target && event.target.matches(".js-edit-label, .js-details-target") |
| 138 | + event.target?.matches(".js-edit-label, .js-details-target") |
151 | 139 | ) {
|
152 | 140 | addPicker();
|
153 | 141 | }
|
154 | 142 | });
|
155 | 143 | addPicker();
|
156 |
| - |
157 | 144 | })();
|
0 commit comments