generates.ts 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import { strings } from './constants';
  2. // type ColorType = 'Hex' | 'Hsl' | 'Hsla' | 'Rgb' | 'Rgba' | 'Semi Design Tokens';
  3. type Generate = {
  4. startColor: string;
  5. endColor: string;
  6. size: number
  7. };
  8. type StrokeSet = { percent: number; color: string };
  9. type StrokeArr = Array<StrokeSet>;
  10. function generateColor(s: StrokeArr, percent: number, gradient: boolean): string | undefined {
  11. try {
  12. const gradientColorArr = generate(s, percent, gradient);
  13. if (gradientColorArr.length !== 0) return gradientColorArr;
  14. } catch (e) {
  15. return undefined;
  16. }
  17. return undefined;
  18. }
  19. function generate(s: StrokeArr, percent: number, gradient: boolean): string | undefined {
  20. s.sort((a, b) => a.percent - b.percent);
  21. if (s[0].percent > percent) {
  22. return strings.STROKE_DEFAULT;
  23. }
  24. const endS = s[s.length - 1];
  25. if (endS.percent < percent) {
  26. return formatToHex(endS.color);
  27. }
  28. for (const [index, item] of s.entries()) {
  29. if (item.percent === percent) {
  30. return formatToHex(item.color);
  31. }
  32. if (percent > item.percent) continue;
  33. const oldItem = s[index - 1];
  34. if (!gradient) {
  35. return formatToHex(oldItem.color);
  36. }
  37. return generateGradients(
  38. {
  39. startColor: formatToHex(oldItem.color),
  40. endColor: formatToHex(item.color),
  41. size: item.percent - oldItem.percent - 1,
  42. },
  43. percent - oldItem.percent - 1
  44. ) as string;
  45. }
  46. return undefined;
  47. }
  48. function generateGradients(g: Generate, index: number | undefined): Array<string> | string {
  49. const { startColor, endColor, size } = g;
  50. const sA = startColor.split('');
  51. const eA = endColor.split('');
  52. const rC = [parseInt(`${sA[1]}${sA[2]}`, 16), parseInt(`${eA[1]}${eA[2]}`, 16)];
  53. const gC = [parseInt(`${sA[3]}${sA[4]}`, 16), parseInt(`${eA[3]}${eA[4]}`, 16)];
  54. const bC = [parseInt(`${sA[5]}${sA[6]}`, 16), parseInt(`${eA[5]}${eA[6]}`, 16)];
  55. const aC = [parseInt(`${sA[7]}${sA[8]}`, 16), parseInt(`${eA[7]}${eA[8]}`, 16)];
  56. const rStep = (rC[0] - rC[1]) / (size + 1);
  57. const gStep = (gC[0] - gC[1]) / (size + 1);
  58. const bStep = (bC[0] - bC[1]) / (size + 1);
  59. const aStep = (aC[0] - aC[1]) / (size + 1);
  60. function tHex(i: number) {
  61. const rS = Math.round(rC[0] - rStep * (i + 1)).toString(16);
  62. const gS = Math.round(gC[0] - gStep * (i + 1)).toString(16);
  63. const bS = Math.round(bC[0] - bStep * (i + 1)).toString(16);
  64. const h = `${padTwo(rS)}${padTwo(gS)}${padTwo(bS)}`;
  65. const t = Math.floor(aStep * (i + 1) + aC[1]).toString(16);
  66. return toHex.Hex(`#${h}`, t);
  67. }
  68. function padTwo(s: string) {
  69. if (s.length === 1) {
  70. return `0${s}`;
  71. }
  72. if (s.length === 0) {
  73. return '00';
  74. }
  75. return s;
  76. }
  77. if (typeof index === 'undefined') {
  78. const gradientColorArr = [startColor];
  79. for (let i = 0; i < size; i += 1) {
  80. gradientColorArr.push(tHex(i));
  81. }
  82. return gradientColorArr;
  83. }
  84. return tHex(index);
  85. }
  86. // Resolve the colour type contained within `ColorType` to Hex
  87. function formatToHex(color: string): string | undefined {
  88. color = color.trim().toLowerCase();
  89. // Hex
  90. if (REG_S.hex.test(color)) {
  91. return toHex.Hex(color, undefined);
  92. }
  93. // Hsl or Hsla
  94. if (REG_S.hslA.test(color)) {
  95. return toHex.Hex(toHex.HslA(color), undefined);
  96. }
  97. // Rgb or Rgba
  98. if (REG_S.rgbA.test(color)) {
  99. return toHex.Hex(toHex.RgbA(color), undefined);
  100. }
  101. // Semi Design Tokens
  102. if (REG_S.semiDesignTokens.test(color)) {
  103. if (SEMI_DESIGN_TOKENS.ALONG.indexOf(color) !== -1) {
  104. return toHex.SemiDesignToken(color);
  105. }
  106. if (SEMI_DESIGN_TOKENS.SEQUENCE.indexOf(color) !== -1) {
  107. return toHex.SemiDesignToken(`${color}-5`);
  108. }
  109. return toHex.SemiDesignToken(`${color}`);
  110. }
  111. return undefined;
  112. }
  113. const toHex = {
  114. Hex(color: string, transparency: string | undefined): string {
  115. color = color.replace('#', '');
  116. if (color.length === 8) return `#${color}`;
  117. if (color.length === 6) return `#${color}${transparency || 'ff'}`;
  118. if (color.length === 3) {
  119. color = color
  120. .split('')
  121. .map(c => c + c)
  122. .join('');
  123. }
  124. return `#${color}${transparency || 'ff'}`;
  125. },
  126. SemiDesignToken(color: string): string | undefined {
  127. // ! Only produces effects when used, the conditions for running need to occur after the real DOM is rendered
  128. if (typeof window === 'undefined') {
  129. return undefined;
  130. }
  131. const variable = getComputedStyle(document.body).getPropertyValue(`--semi-${color}`);
  132. if (variable === '') return undefined;
  133. const rgba = `rgba(${variable}, 1)`;
  134. return toHex.RgbA(rgba);
  135. },
  136. HslA(color: string): string {
  137. const hsla = REG_S.hslA.exec(color);
  138. const h = parseInt(hsla[2]);
  139. const s = parseInt(hsla[3]) / 100;
  140. const l = parseInt(hsla[4]) / 100;
  141. const a = hsla[5];
  142. const c = (1 - Math.abs(2 * l - 1)) * s,
  143. x = c * (1 - Math.abs(((h / 60) % 2) - 1)),
  144. m = l - c / 2;
  145. let r: string | number = 0,
  146. g: string | number = 0,
  147. b: string | number = 0;
  148. if (0 <= h && h < 60) {
  149. r = c;
  150. g = x;
  151. b = 0;
  152. } else if (60 <= h && h < 120) {
  153. r = x;
  154. g = c;
  155. b = 0;
  156. } else if (120 <= h && h < 180) {
  157. r = 0;
  158. g = c;
  159. b = x;
  160. } else if (180 <= h && h < 240) {
  161. r = 0;
  162. g = x;
  163. b = c;
  164. } else if (240 <= h && h < 300) {
  165. r = x;
  166. g = 0;
  167. b = c;
  168. } else if (300 <= h && h < 360) {
  169. r = c;
  170. g = 0;
  171. b = x;
  172. }
  173. r = Math.round((r + m) * 255).toString(16);
  174. g = Math.round((g + m) * 255).toString(16);
  175. b = Math.round((b + m) * 255).toString(16);
  176. return toHex.utils.pAL(r, g, b, a);
  177. },
  178. RgbA(color: string): string {
  179. const rgba = REG_S.rgbA.exec(color);
  180. const r = parseInt(rgba[2], 10).toString(16),
  181. g = parseInt(rgba[3], 10).toString(16),
  182. b = parseInt(rgba[4], 10).toString(16),
  183. a = rgba[5];
  184. return toHex.utils.pAL(r, g, b, a);
  185. },
  186. utils: {
  187. pAL(r: string, g: string, b: string, a: string) {
  188. if (r.length == 1) r = '0' + r;
  189. if (g.length == 1) g = '0' + g;
  190. if (b.length == 1) b = '0' + b;
  191. if (typeof a !== 'undefined') {
  192. a = Math.round(parseInt(a) * 255).toString(16);
  193. if (a.length == 1) a = '0' + a;
  194. return '#' + r + g + b + a;
  195. }
  196. return '#' + r + g + b;
  197. },
  198. },
  199. };
  200. const REG_S = {
  201. hex: /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/,
  202. hslA: /(hsl)a?\(\s*?(\d+),?\s*?(\d+)%,?\s*?(\d+)%,?\s*?\/?(\s*?[\d.]+)?\s*?\)/,
  203. rgbA: /(rgb)a?\(\s*?(\d+),?\s*?(\d+),?\s*?(\d+),?\s*?\/?(\s*?[\d.]+)?\s*?\)/,
  204. semiDesignTokens: /(\w+)?-?(\w+)-?(\d)?/,
  205. };
  206. // From src/components/palette.js
  207. const SEMI_DESIGN_TOKENS = {
  208. // No sequence
  209. ALONG: ["black", "white"],
  210. // Sequence: 0-9
  211. SEQUENCE: [
  212. "amber",
  213. "blue",
  214. "cyan",
  215. "green",
  216. "grey",
  217. "indigo",
  218. "light-blue",
  219. "light-green",
  220. "lime",
  221. "orange",
  222. "pink",
  223. "purple",
  224. "red",
  225. "teal",
  226. "violet",
  227. "yellow"
  228. ]
  229. };
  230. export { generateColor, StrokeArr };