@stats-organization/github-readme-stats-core 2.1.4 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/build/api/gist.d.ts +2 -7
  2. package/build/api/gist.d.ts.map +1 -1
  3. package/build/api/gist.js +28 -26
  4. package/build/api/index.d.ts +3 -8
  5. package/build/api/index.d.ts.map +1 -1
  6. package/build/api/index.js +32 -36
  7. package/build/api/pin.d.ts +2 -7
  8. package/build/api/pin.d.ts.map +1 -1
  9. package/build/api/pin.js +18 -33
  10. package/build/api/top-langs.d.ts +2 -7
  11. package/build/api/top-langs.d.ts.map +1 -1
  12. package/build/api/top-langs.js +32 -44
  13. package/build/api/wakatime.d.ts +2 -7
  14. package/build/api/wakatime.d.ts.map +1 -1
  15. package/build/api/wakatime.js +28 -26
  16. package/build/calculateRank.d.ts +12 -11
  17. package/build/calculateRank.d.ts.map +1 -1
  18. package/build/calculateRank.js +18 -18
  19. package/build/cards/common-options.d.ts +13 -0
  20. package/build/cards/common-options.d.ts.map +1 -0
  21. package/build/cards/common-options.js +1 -0
  22. package/build/cards/gist.d.ts +11 -16
  23. package/build/cards/gist.d.ts.map +1 -1
  24. package/build/cards/gist.js +26 -31
  25. package/build/cards/repo.d.ts +19 -16
  26. package/build/cards/repo.d.ts.map +1 -1
  27. package/build/cards/repo.js +79 -72
  28. package/build/cards/stats.d.ts +29 -44
  29. package/build/cards/stats.d.ts.map +1 -1
  30. package/build/cards/stats.js +118 -173
  31. package/build/cards/top-languages.d.ts +81 -77
  32. package/build/cards/top-languages.d.ts.map +1 -1
  33. package/build/cards/top-languages.js +186 -184
  34. package/build/cards/wakatime.d.ts +21 -11
  35. package/build/cards/wakatime.d.ts.map +1 -1
  36. package/build/cards/wakatime.js +92 -109
  37. package/build/common/Card.d.ts +41 -14
  38. package/build/common/Card.d.ts.map +1 -1
  39. package/build/common/Card.js +92 -31
  40. package/build/common/I18n.d.ts +4 -2
  41. package/build/common/I18n.d.ts.map +1 -1
  42. package/build/common/color.d.ts +91 -15
  43. package/build/common/color.d.ts.map +1 -1
  44. package/build/common/color.js +163 -17
  45. package/build/common/config.d.ts +2 -1
  46. package/build/common/config.d.ts.map +1 -1
  47. package/build/common/config.js +16 -26
  48. package/build/common/constants.d.ts +2 -1
  49. package/build/common/constants.d.ts.map +1 -1
  50. package/build/common/date.d.ts +37 -0
  51. package/build/common/date.d.ts.map +1 -0
  52. package/build/common/date.js +31 -0
  53. package/build/common/html.js +1 -1
  54. package/build/common/http.d.ts +37 -5
  55. package/build/common/http.d.ts.map +1 -1
  56. package/build/common/http.js +29 -6
  57. package/build/common/icons.d.ts +1 -0
  58. package/build/common/icons.d.ts.map +1 -1
  59. package/build/common/icons.js +1 -0
  60. package/build/common/languageColors.d.ts +11 -0
  61. package/build/common/languageColors.d.ts.map +1 -0
  62. package/build/common/languageColors.js +15 -0
  63. package/build/common/languageColors.json +10 -1
  64. package/build/common/ops.d.ts +27 -33
  65. package/build/common/ops.d.ts.map +1 -1
  66. package/build/common/ops.js +31 -48
  67. package/build/common/render.d.ts +154 -113
  68. package/build/common/render.d.ts.map +1 -1
  69. package/build/common/render.js +209 -92
  70. package/build/common/retryer.d.ts +15 -6
  71. package/build/common/retryer.d.ts.map +1 -1
  72. package/build/common/retryer.js +2 -0
  73. package/build/fetchers/gist.d.ts +6 -21
  74. package/build/fetchers/gist.d.ts.map +1 -1
  75. package/build/fetchers/gist.js +28 -64
  76. package/build/fetchers/repo.d.ts +12 -11
  77. package/build/fetchers/repo.d.ts.map +1 -1
  78. package/build/fetchers/repo.js +27 -68
  79. package/build/fetchers/stats.d.ts +24 -17
  80. package/build/fetchers/stats.d.ts.map +1 -1
  81. package/build/fetchers/stats.js +302 -184
  82. package/build/fetchers/top-languages.d.ts +10 -15
  83. package/build/fetchers/top-languages.d.ts.map +1 -1
  84. package/build/fetchers/top-languages.js +52 -103
  85. package/build/fetchers/types.d.ts +130 -0
  86. package/build/fetchers/types.d.ts.map +1 -0
  87. package/build/fetchers/types.js +1 -0
  88. package/build/fetchers/wakatime.d.ts +9 -5
  89. package/build/fetchers/wakatime.d.ts.map +1 -1
  90. package/build/fetchers/wakatime.js +8 -4
  91. package/build/graphql/contributionsDocument.d.ts +18 -0
  92. package/build/graphql/contributionsDocument.d.ts.map +1 -0
  93. package/build/graphql/contributionsDocument.js +30 -0
  94. package/build/graphql/generated/common.d.ts +42 -0
  95. package/build/graphql/generated/common.d.ts.map +1 -0
  96. package/build/graphql/generated/common.js +2 -0
  97. package/build/graphql/generated/gist.d.ts +44 -0
  98. package/build/graphql/generated/gist.d.ts.map +1 -0
  99. package/build/graphql/generated/gist.js +27 -0
  100. package/build/graphql/generated/repo.d.ts +67 -0
  101. package/build/graphql/generated/repo.d.ts.map +1 -0
  102. package/build/graphql/generated/repo.js +30 -0
  103. package/build/graphql/generated/stats.d.ts +150 -0
  104. package/build/graphql/generated/stats.d.ts.map +1 -0
  105. package/build/graphql/generated/stats.js +95 -0
  106. package/build/graphql/generated/top-languages.d.ts +54 -0
  107. package/build/graphql/generated/top-languages.d.ts.map +1 -0
  108. package/build/graphql/generated/top-languages.js +27 -0
  109. package/build/graphql/graphqlDocument.d.ts +22 -0
  110. package/build/graphql/graphqlDocument.d.ts.map +1 -0
  111. package/build/graphql/graphqlDocument.js +8 -0
  112. package/build/graphql/reposContributedToDocument.d.ts +27 -0
  113. package/build/graphql/reposContributedToDocument.d.ts.map +1 -0
  114. package/build/graphql/reposContributedToDocument.js +59 -0
  115. package/build/index.d.ts +3 -1
  116. package/build/index.d.ts.map +1 -1
  117. package/build/index.js +2 -1
  118. package/build/themes/index.d.ts +32 -0
  119. package/build/themes/index.d.ts.map +1 -1
  120. package/build/themes/index.js +28 -0
  121. package/build/translations.d.ts +17 -0
  122. package/build/translations.d.ts.map +1 -1
  123. package/build/translations.js +17 -0
  124. package/package.json +14 -8
  125. package/src/_emoji-name-map.d.ts +10 -0
  126. package/src/_github-username-regex.d.ts +4 -0
  127. package/src/api/gist.js +31 -31
  128. package/src/api/index.js +40 -42
  129. package/src/api/pin.js +20 -38
  130. package/src/api/top-langs.js +36 -49
  131. package/src/api/wakatime.js +31 -31
  132. package/src/{calculateRank.js → calculateRank.ts} +29 -19
  133. package/src/cards/common-options.ts +13 -0
  134. package/src/cards/{gist.js → gist.ts} +37 -37
  135. package/src/cards/{repo.js → repo.ts} +107 -79
  136. package/src/cards/{stats.js → stats.ts} +173 -210
  137. package/src/cards/{top-languages.js → top-languages.ts} +325 -243
  138. package/src/cards/{wakatime.js → wakatime.ts} +158 -127
  139. package/src/common/Card.ts +123 -43
  140. package/src/common/I18n.ts +6 -2
  141. package/src/common/color.ts +240 -28
  142. package/src/common/config.ts +22 -29
  143. package/src/common/constants.ts +3 -1
  144. package/src/common/date.ts +45 -0
  145. package/src/common/html.ts +1 -1
  146. package/src/common/http.ts +71 -0
  147. package/src/common/icons.ts +1 -0
  148. package/src/common/languageColors.json +10 -1
  149. package/src/common/languageColors.ts +19 -0
  150. package/src/common/ops.ts +158 -0
  151. package/src/common/render.ts +652 -0
  152. package/src/common/retryer.ts +21 -12
  153. package/src/fetchers/gist.ts +79 -0
  154. package/src/fetchers/repo.ts +114 -0
  155. package/src/fetchers/stats.ts +743 -0
  156. package/src/fetchers/top-languages.ts +135 -0
  157. package/src/fetchers/{types.d.ts → types.ts} +28 -20
  158. package/src/fetchers/{wakatime.js → wakatime.ts} +19 -5
  159. package/src/graphql/contributionsDocument.ts +44 -0
  160. package/src/graphql/generated/common.ts +26 -0
  161. package/src/graphql/generated/gist.ts +59 -0
  162. package/src/graphql/generated/repo.ts +90 -0
  163. package/src/graphql/generated/stats.ts +194 -0
  164. package/src/graphql/generated/top-languages.ts +75 -0
  165. package/src/graphql/graphqlDocument.ts +25 -0
  166. package/src/graphql/queries/gist.graphql +25 -0
  167. package/src/graphql/queries/repo.graphql +28 -0
  168. package/src/graphql/queries/stats.graphql +120 -0
  169. package/src/graphql/queries/top-languages.graphql +34 -0
  170. package/src/graphql/reposContributedToDocument.ts +86 -0
  171. package/src/index.ts +3 -1
  172. package/src/themes/index.ts +33 -0
  173. package/src/translations.ts +17 -0
  174. package/src/cards/types.d.ts +0 -78
  175. package/src/common/http.js +0 -19
  176. package/src/common/ops.js +0 -170
  177. package/src/common/render.js +0 -440
  178. package/src/fetchers/gist.js +0 -112
  179. package/src/fetchers/repo.js +0 -161
  180. package/src/fetchers/stats.js +0 -474
  181. package/src/fetchers/top-languages.js +0 -175
  182. package/src/themes/README.md +0 -229
@@ -1,18 +1,22 @@
1
1
  import { Card } from "../common/Card.js";
2
2
  import { I18n } from "../common/I18n.js";
3
- import { fallbackColor, getCardColors } from "../common/color.js";
3
+ import { getLightDarkColors, isPrefixedHexColor } from "../common/color.js";
4
4
  import { formatBytes } from "../common/fmt.js";
5
+ import { encodeHTML } from "../common/html.js";
6
+ import { DEFAULT_LANG_COLOR } from "../common/languageColors.js";
5
7
  import { chunkArray, clampValue, lowercaseTrim } from "../common/ops.js";
6
8
  import {
7
9
  createProgressNode,
8
10
  flexLayout,
9
11
  measureText,
10
12
  } from "../common/render.js";
13
+ import type { Lang, TopLangData } from "../fetchers/types.js";
11
14
  import { langCardLocales } from "../translations.js";
12
15
 
16
+ import type { CommonOptions } from "./common-options.js";
17
+
13
18
  const DEFAULT_CARD_WIDTH = 300;
14
19
  const MIN_CARD_WIDTH = 280;
15
- const DEFAULT_LANG_COLOR = "#858585";
16
20
  const CARD_PADDING = 25;
17
21
  const COMPACT_LAYOUT_BASE_HEIGHT = 90;
18
22
  const MAXIMUM_LANGS_COUNT = 20;
@@ -23,18 +27,32 @@ const DONUT_LAYOUT_DEFAULT_LANGS_COUNT = 5;
23
27
  const PIE_LAYOUT_DEFAULT_LANGS_COUNT = 6;
24
28
  const DONUT_VERTICAL_LAYOUT_DEFAULT_LANGS_COUNT = 6;
25
29
 
26
- /**
27
- * @typedef {import("../fetchers/types").Lang} Lang
28
- */
30
+ type TopLangLayout = "compact" | "normal" | "donut" | "donut-vertical" | "pie";
31
+
32
+ interface TopLangOptions extends CommonOptions {
33
+ hide_title: boolean;
34
+ card_width: number;
35
+ hide: Array<string>;
36
+ layout: TopLangLayout;
37
+ custom_title: string;
38
+ langs_count: number;
39
+ disable_animations: boolean;
40
+ hide_progress: boolean;
41
+ hide_values: boolean;
42
+ prog_bar_bg_color: string;
43
+ stats_format: "percentages" | "bytes";
44
+ }
29
45
 
30
46
  /**
31
47
  * Retrieves the programming language whose name is the longest.
32
48
  *
33
- * @param {Lang[]} arr Array of programming languages.
34
- * @returns {{ name: string, size: number, color: string }} Longest programming language object.
49
+ * @param arr Array of programming languages.
50
+ * @returns Longest programming language object.
35
51
  */
36
- const getLongestLang = (arr) =>
37
- arr.reduce(
52
+ const getLongestLang = (
53
+ arr: Array<Lang>,
54
+ ): Pick<Lang, "name" | "size" | "color"> =>
55
+ arr.reduce<Pick<Lang, "name" | "size" | "color">>(
38
56
  (savedLang, lang) =>
39
57
  lang.name.length > savedLang.name.length ? lang : savedLang,
40
58
  { name: "", size: 0, color: "" },
@@ -43,29 +61,36 @@ const getLongestLang = (arr) =>
43
61
  /**
44
62
  * Convert degrees to radians.
45
63
  *
46
- * @param {number} angleInDegrees Angle in degrees.
47
- * @returns {number} Angle in radians.
64
+ * @param angleInDegrees Angle in degrees.
65
+ * @returns Angle in radians.
48
66
  */
49
- const degreesToRadians = (angleInDegrees) => angleInDegrees * (Math.PI / 180.0);
67
+ const degreesToRadians = (angleInDegrees: number): number =>
68
+ angleInDegrees * (Math.PI / 180.0);
50
69
 
51
70
  /**
52
71
  * Convert radians to degrees.
53
72
  *
54
- * @param {number} angleInRadians Angle in radians.
55
- * @returns {number} Angle in degrees.
73
+ * @param angleInRadians Angle in radians.
74
+ * @returns Angle in degrees.
56
75
  */
57
- const radiansToDegrees = (angleInRadians) => angleInRadians / (Math.PI / 180.0);
76
+ const radiansToDegrees = (angleInRadians: number): number =>
77
+ angleInRadians / (Math.PI / 180.0);
58
78
 
59
79
  /**
60
80
  * Convert polar coordinates to cartesian coordinates.
61
81
  *
62
- * @param {number} centerX Center x coordinate.
63
- * @param {number} centerY Center y coordinate.
64
- * @param {number} radius Radius of the circle.
65
- * @param {number} angleInDegrees Angle in degrees.
66
- * @returns {{x: number, y: number}} Cartesian coordinates.
82
+ * @param centerX Center x coordinate.
83
+ * @param centerY Center y coordinate.
84
+ * @param radius Radius of the circle.
85
+ * @param angleInDegrees Angle in degrees.
86
+ * @returns Cartesian coordinates.
67
87
  */
68
- const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
88
+ const polarToCartesian = (
89
+ centerX: number,
90
+ centerY: number,
91
+ radius: number,
92
+ angleInDegrees: number,
93
+ ): { x: number; y: number } => {
69
94
  const rads = degreesToRadians(angleInDegrees);
70
95
  return {
71
96
  x: centerX + radius * Math.cos(rads),
@@ -76,13 +101,18 @@ const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
76
101
  /**
77
102
  * Convert cartesian coordinates to polar coordinates.
78
103
  *
79
- * @param {number} centerX Center x coordinate.
80
- * @param {number} centerY Center y coordinate.
81
- * @param {number} x Point x coordinate.
82
- * @param {number} y Point y coordinate.
83
- * @returns {{radius: number, angleInDegrees: number}} Polar coordinates.
104
+ * @param centerX Center x coordinate.
105
+ * @param centerY Center y coordinate.
106
+ * @param x Point x coordinate.
107
+ * @param y Point y coordinate.
108
+ * @returns Polar coordinates.
84
109
  */
85
- const cartesianToPolar = (centerX, centerY, x, y) => {
110
+ const cartesianToPolar = (
111
+ centerX: number,
112
+ centerY: number,
113
+ x: number,
114
+ y: number,
115
+ ): { radius: number; angleInDegrees: number } => {
86
116
  const radius = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
87
117
  let angleInDegrees = radiansToDegrees(Math.atan2(y - centerY, x - centerX));
88
118
  if (angleInDegrees < 0) {
@@ -94,90 +124,93 @@ const cartesianToPolar = (centerX, centerY, x, y) => {
94
124
  /**
95
125
  * Calculates length of circle.
96
126
  *
97
- * @param {number} radius Radius of the circle.
98
- * @returns {number} The length of the circle.
127
+ * @param radius Radius of the circle.
128
+ * @returns The length of the circle.
99
129
  */
100
- const getCircleLength = (radius) => {
130
+ const getCircleLength = (radius: number): number => {
101
131
  return 2 * Math.PI * radius;
102
132
  };
103
133
 
104
134
  /**
105
135
  * Calculates height for the compact layout.
106
136
  *
107
- * @param {number} totalLangs Total number of languages.
108
- * @returns {number} Card height.
137
+ * @param totalLangs Total number of languages.
138
+ * @returns Card height.
109
139
  */
110
- const calculateCompactLayoutHeight = (totalLangs) => {
140
+ const calculateCompactLayoutHeight = (totalLangs: number): number => {
111
141
  return COMPACT_LAYOUT_BASE_HEIGHT + Math.round(totalLangs / 2) * 25;
112
142
  };
113
143
 
114
144
  /**
115
145
  * Calculates height for the normal layout.
116
146
  *
117
- * @param {number} totalLangs Total number of languages.
118
- * @returns {number} Card height.
147
+ * @param totalLangs Total number of languages.
148
+ * @returns Card height.
119
149
  */
120
- const calculateNormalLayoutHeight = (totalLangs) => {
150
+ const calculateNormalLayoutHeight = (totalLangs: number): number => {
121
151
  return 45 + (totalLangs + 1) * 40;
122
152
  };
123
153
 
124
154
  /**
125
155
  * Calculates height for the donut layout.
126
156
  *
127
- * @param {number} totalLangs Total number of languages.
128
- * @returns {number} Card height.
157
+ * @param totalLangs Total number of languages.
158
+ * @returns Card height.
129
159
  */
130
- const calculateDonutLayoutHeight = (totalLangs) => {
160
+ const calculateDonutLayoutHeight = (totalLangs: number): number => {
131
161
  return 215 + Math.max(totalLangs - 5, 0) * 32;
132
162
  };
133
163
 
134
164
  /**
135
165
  * Calculates height for the donut vertical layout.
136
166
  *
137
- * @param {number} totalLangs Total number of languages.
138
- * @returns {number} Card height.
167
+ * @param totalLangs Total number of languages.
168
+ * @returns Card height.
139
169
  */
140
- const calculateDonutVerticalLayoutHeight = (totalLangs) => {
170
+ const calculateDonutVerticalLayoutHeight = (totalLangs: number): number => {
141
171
  return 300 + Math.round(totalLangs / 2) * 25;
142
172
  };
143
173
 
144
174
  /**
145
175
  * Calculates height for the pie layout.
146
176
  *
147
- * @param {number} totalLangs Total number of languages.
148
- * @returns {number} Card height.
177
+ * @param totalLangs Total number of languages.
178
+ * @returns Card height.
149
179
  */
150
- const calculatePieLayoutHeight = (totalLangs) => {
180
+ const calculatePieLayoutHeight = (totalLangs: number): number => {
151
181
  return 300 + Math.round(totalLangs / 2) * 25;
152
182
  };
153
183
 
154
184
  /**
155
185
  * Calculates the center translation needed to keep the donut chart centred.
156
- * @param {number} totalLangs Total number of languages.
157
- * @returns {number} Donut center translation.
186
+ *
187
+ * @param totalLangs Total number of languages.
188
+ * @returns Donut center translation.
158
189
  */
159
- const donutCenterTranslation = (totalLangs) => {
190
+ const donutCenterTranslation = (totalLangs: number): number => {
160
191
  return -45 + Math.max(totalLangs - 5, 0) * 16;
161
192
  };
162
193
 
163
194
  /**
164
195
  * Trim top languages to lang_count while also hiding certain languages.
165
196
  *
166
- * @param {Record<string, Lang>} topLangs Top languages.
167
- * @param {number} langs_count Number of languages to show.
168
- * @param {string[]=} hide Languages to hide.
169
- * @returns {{ langs: Lang[], totalLanguageSize: number }} Trimmed top languages and total size.
197
+ * @param topLangs Top languages.
198
+ * @param langs_count Number of languages to show.
199
+ * @param hide Languages to hide.
200
+ * @returns Trimmed top languages and total size.
170
201
  */
171
- const trimTopLanguages = (topLangs, langs_count, hide) => {
202
+ const trimTopLanguages = (
203
+ topLangs: TopLangData,
204
+ langs_count: number,
205
+ hide?: Array<string>,
206
+ ): { langs: Array<Lang>; totalLanguageSize: number } => {
172
207
  let langs = Object.values(topLangs);
173
- let langsToHide = {};
174
- let langsCount = clampValue(langs_count, 1, MAXIMUM_LANGS_COUNT);
208
+ const langsToHide: Record<string, boolean> = {};
209
+ const langsCount = clampValue(langs_count, 1, MAXIMUM_LANGS_COUNT);
175
210
 
176
- // populate langsToHide map for quick lookup
177
- // while filtering out
211
+ // populate langsToHide map for quick lookup while filtering out
178
212
  if (hide) {
179
213
  hide.forEach((langName) => {
180
- // @ts-ignore
181
214
  langsToHide[lowercaseTrim(langName)] = true;
182
215
  });
183
216
  }
@@ -185,10 +218,7 @@ const trimTopLanguages = (topLangs, langs_count, hide) => {
185
218
  // filter out languages to be hidden
186
219
  langs = langs
187
220
  .sort((a, b) => b.size - a.size)
188
- .filter((lang) => {
189
- // @ts-ignore
190
- return !langsToHide[lowercaseTrim(lang.name)];
191
- })
221
+ .filter((lang) => !langsToHide[lowercaseTrim(lang.name)])
192
222
  .slice(0, langsCount);
193
223
 
194
224
  const totalLanguageSize = langs.reduce((acc, curr) => acc + curr.size, 0);
@@ -199,41 +229,67 @@ const trimTopLanguages = (topLangs, langs_count, hide) => {
199
229
  /**
200
230
  * Get display value corresponding to the format.
201
231
  *
202
- * @param {number} size Bytes size.
203
- * @param {number} percentages Percentage value.
204
- * @param {string} format Format of the stats.
205
- * @returns {string} Display value.
232
+ * @param size Bytes size.
233
+ * @param percentages Percentage value.
234
+ * @param format Format of the stats.
235
+ * @returns Display value.
206
236
  */
207
- const getDisplayValue = (size, percentages, format) => {
237
+ const getDisplayValue = (
238
+ size: number,
239
+ percentages: number,
240
+ format: string,
241
+ ): string => {
208
242
  return format === "bytes" ? formatBytes(size) : `${percentages.toFixed(2)}%`;
209
243
  };
210
244
 
245
+ /**
246
+ * Resolves a language's display color, falling back to the default, and
247
+ * validates it is a prefixed hex color.
248
+ *
249
+ * @param lang Programming language object.
250
+ * @returns Validated language color.
251
+ */
252
+ const resolveLangColor = (lang: Lang): string => {
253
+ const color = lang.color || DEFAULT_LANG_COLOR;
254
+ if (!isPrefixedHexColor(color)) {
255
+ throw new Error(`Invalid language color: "${color}"`);
256
+ }
257
+ return color;
258
+ };
259
+
211
260
  /**
212
261
  * Create progress bar text item for a programming language.
213
262
  *
214
- * @param {object} props Function properties.
215
- * @param {number} props.width The card width
216
- * @param {string} props.color Color of the programming language.
217
- * @param {string} props.progBarBgColor Color of the background of progress bar.
218
- * @param {string} props.name Name of the programming language.
219
- * @param {number} props.size Size of the programming language.
220
- * @param {number} props.totalSize Total size of all languages.
221
- * @param {string} props.statsFormat Stats format.
222
- * @param {boolean=} props.hideValues Whether to hide stats values.
223
- * @param {number} props.index Index of the programming language.
224
- * @returns {string} Programming language SVG node.
263
+ * @param props Function properties.
264
+ * @param props.width The card width
265
+ * @param props.color Color of the programming language.
266
+ * @param props.name Name of the programming language.
267
+ * @param props.size Size of the programming language.
268
+ * @param props.totalSize Total size of all languages.
269
+ * @param props.statsFormat Stats format.
270
+ * @param props.hideValues Whether to hide stats values.
271
+ * @param props.index Index of the programming language.
272
+ * @returns Programming language SVG node.
225
273
  */
226
274
  const createProgressTextNode = ({
227
275
  width,
228
276
  color,
229
- progBarBgColor,
230
277
  name,
231
278
  size,
232
279
  totalSize,
233
280
  statsFormat,
234
281
  hideValues,
235
282
  index,
236
- }) => {
283
+ }: {
284
+ width: number;
285
+ color: string;
286
+ name: string;
287
+ size: number;
288
+ totalSize: number;
289
+ statsFormat: string;
290
+ hideValues?: boolean | undefined;
291
+ index: number;
292
+ }): string => {
237
293
  const staggerDelay = (index + 3) * 150;
238
294
  const paddingRight = hideValues ? CARD_PADDING * 2 : 95;
239
295
  const progressTextX = width - paddingRight + 10;
@@ -244,15 +300,14 @@ const createProgressTextNode = ({
244
300
 
245
301
  return `
246
302
  <g class="stagger" style="animation-delay: ${staggerDelay}ms">
247
- <text data-testid="lang-name" x="2" y="15" class="lang-name">${name}</text>
248
- ${hideValues ? "" : `<text x="${progressTextX}" y="34" class="lang-name">${displayValue}</text>`}
303
+ <text data-testid="lang-name" x="2" y="15" class="lang-name">${encodeHTML(name)}</text>
304
+ ${hideValues ? "" : `<text x="${progressTextX}" y="34" class="lang-name">${encodeHTML(displayValue)}</text>`}
249
305
  ${createProgressNode({
250
306
  x: 0,
251
307
  y: 25,
252
308
  color,
253
309
  width: progressWidth,
254
310
  progress,
255
- progressBarBackgroundColor: progBarBgColor,
256
311
  delay: staggerDelay + 300,
257
312
  })}
258
313
  </g>
@@ -262,14 +317,14 @@ const createProgressTextNode = ({
262
317
  /**
263
318
  * Creates compact text item for a programming language.
264
319
  *
265
- * @param {object} props Function properties.
266
- * @param {Lang} props.lang Programming language object.
267
- * @param {number} props.totalSize Total size of all languages.
268
- * @param {boolean=} props.hideProgress Whether to hide percentage.
269
- * @param {boolean=} props.hideValues Whether to hide stats values (percentages/bytes).
270
- * @param {string=} props.statsFormat Stats format
271
- * @param {number} props.index Index of the programming language.
272
- * @returns {string} Compact layout programming language SVG node.
320
+ * @param props Function properties.
321
+ * @param props.lang Programming language object.
322
+ * @param props.totalSize Total size of all languages.
323
+ * @param props.hideProgress Whether to hide percentage.
324
+ * @param props.hideValues Whether to hide stats values (percentages/bytes).
325
+ * @param props.statsFormat Stats format
326
+ * @param props.index Index of the programming language.
327
+ * @returns Compact layout programming language SVG node.
273
328
  */
274
329
  const createCompactLangNode = ({
275
330
  lang,
@@ -278,18 +333,25 @@ const createCompactLangNode = ({
278
333
  hideValues,
279
334
  statsFormat = "percentages",
280
335
  index,
281
- }) => {
336
+ }: {
337
+ lang: Lang;
338
+ totalSize: number;
339
+ hideProgress?: boolean | undefined;
340
+ hideValues?: boolean | undefined;
341
+ statsFormat?: string | undefined;
342
+ index: number;
343
+ }): string => {
282
344
  const percentages = (lang.size / totalSize) * 100;
283
345
  const displayValue = getDisplayValue(lang.size, percentages, statsFormat);
284
346
 
285
347
  const staggerDelay = (index + 3) * 150;
286
- const color = lang.color || "#858585";
348
+ const color = resolveLangColor(lang);
287
349
 
288
350
  return `
289
351
  <g class="stagger" style="animation-delay: ${staggerDelay}ms">
290
352
  <circle cx="5" cy="6" r="5" fill="${color}" />
291
353
  <text data-testid="lang-name" x="15" y="10" class='lang-name'>
292
- ${lang.name} ${hideProgress || hideValues ? "" : displayValue}
354
+ ${encodeHTML(lang.name)} ${hideProgress || hideValues ? "" : encodeHTML(displayValue)}
293
355
  </text>
294
356
  </g>
295
357
  `;
@@ -298,13 +360,13 @@ const createCompactLangNode = ({
298
360
  /**
299
361
  * Create compact languages text items for all programming languages.
300
362
  *
301
- * @param {object} props Function properties.
302
- * @param {Lang[]} props.langs Array of programming languages.
303
- * @param {number} props.totalSize Total size of all languages.
304
- * @param {boolean=} props.hideProgress Whether to hide percentage.
305
- * @param {boolean=} props.hideValues Whether to hide stats values.
306
- * @param {string=} props.statsFormat Stats format
307
- * @returns {string} Programming languages SVG node.
363
+ * @param props Function properties.
364
+ * @param props.langs Array of programming languages.
365
+ * @param props.totalSize Total size of all languages.
366
+ * @param props.hideProgress Whether to hide percentage.
367
+ * @param props.hideValues Whether to hide stats values.
368
+ * @param props.statsFormat Stats format
369
+ * @returns Programming languages SVG node.
308
370
  */
309
371
  const createLanguageTextNode = ({
310
372
  langs,
@@ -312,11 +374,16 @@ const createLanguageTextNode = ({
312
374
  hideProgress,
313
375
  hideValues,
314
376
  statsFormat,
315
- }) => {
377
+ }: {
378
+ langs: Array<Lang>;
379
+ totalSize: number;
380
+ hideProgress?: boolean | undefined;
381
+ hideValues?: boolean | undefined;
382
+ statsFormat?: string | undefined;
383
+ }): string => {
316
384
  const longestLang = getLongestLang(langs);
317
385
  const chunked = chunkArray(langs, langs.length / 2);
318
386
  const layouts = chunked.map((array) => {
319
- // @ts-ignore
320
387
  const items = array.map((lang, index) =>
321
388
  createCompactLangNode({
322
389
  lang,
@@ -346,19 +413,24 @@ const createLanguageTextNode = ({
346
413
  /**
347
414
  * Create donut languages text items for all programming languages.
348
415
  *
349
- * @param {object} props Function properties.
350
- * @param {Lang[]} props.langs Array of programming languages.
351
- * @param {number} props.totalSize Total size of all languages.
352
- * @param {boolean=} props.hideValues Whether to hide stats values.
353
- * @param {string} props.statsFormat Stats format
354
- * @returns {string} Donut layout programming language SVG node.
416
+ * @param props Function properties.
417
+ * @param props.langs Array of programming languages.
418
+ * @param props.totalSize Total size of all languages.
419
+ * @param props.hideValues Whether to hide stats values.
420
+ * @param props.statsFormat Stats format
421
+ * @returns Donut layout programming language SVG node.
355
422
  */
356
423
  const createDonutLanguagesNode = ({
357
424
  langs,
358
425
  totalSize,
359
426
  hideValues,
360
427
  statsFormat,
361
- }) => {
428
+ }: {
429
+ langs: Array<Lang>;
430
+ totalSize: number;
431
+ hideValues?: boolean | undefined;
432
+ statsFormat?: string | undefined;
433
+ }): string => {
362
434
  return flexLayout({
363
435
  items: langs.map((lang, index) => {
364
436
  return createCompactLangNode({
@@ -378,22 +450,20 @@ const createDonutLanguagesNode = ({
378
450
  /**
379
451
  * Renders the default language card layout.
380
452
  *
381
- * @param {Lang[]} langs Array of programming languages.
382
- * @param {number} width Card width.
383
- * @param {number} totalLanguageSize Total size of all languages.
384
- * @param progBarBgColor Color of the background of progress bar.
385
- * @param {string} statsFormat Stats format.
386
- * @param {boolean=} hideValues Whether to hide stats values.
387
- * @returns {string} Normal layout card SVG object.
453
+ * @param langs Array of programming languages.
454
+ * @param width Card width.
455
+ * @param totalLanguageSize Total size of all languages.
456
+ * @param statsFormat Stats format.
457
+ * @param hideValues Whether to hide stats values.
458
+ * @returns Normal layout card SVG object.
388
459
  */
389
460
  const renderNormalLayout = (
390
- langs,
391
- width,
392
- totalLanguageSize,
393
- progBarBgColor,
394
- statsFormat,
395
- hideValues,
396
- ) => {
461
+ langs: Array<Lang>,
462
+ width: number,
463
+ totalLanguageSize: number,
464
+ statsFormat: string,
465
+ hideValues?: boolean,
466
+ ): string => {
397
467
  return flexLayout({
398
468
  items: langs.map((lang, index) => {
399
469
  return createProgressTextNode({
@@ -405,7 +475,6 @@ const renderNormalLayout = (
405
475
  statsFormat,
406
476
  hideValues,
407
477
  index,
408
- progBarBgColor,
409
478
  });
410
479
  }),
411
480
  gap: 40,
@@ -416,22 +485,22 @@ const renderNormalLayout = (
416
485
  /**
417
486
  * Renders the compact language card layout.
418
487
  *
419
- * @param {Lang[]} langs Array of programming languages.
420
- * @param {number} width Card width.
421
- * @param {number} totalLanguageSize Total size of all languages.
422
- * @param {boolean=} hideProgress Whether to hide progress bar.
423
- * @param {string} statsFormat Stats format.
424
- * @param {boolean=} hideValues Whether to hide stats values.
425
- * @returns {string} Compact layout card SVG object.
488
+ * @param langs Array of programming languages.
489
+ * @param width Card width.
490
+ * @param totalLanguageSize Total size of all languages.
491
+ * @param hideProgress Whether to hide progress bar.
492
+ * @param statsFormat Stats format.
493
+ * @param hideValues Whether to hide stats values.
494
+ * @returns Compact layout card SVG object.
426
495
  */
427
496
  const renderCompactLayout = (
428
- langs,
429
- width,
430
- totalLanguageSize,
431
- hideProgress,
497
+ langs: Array<Lang>,
498
+ width: number,
499
+ totalLanguageSize: number,
500
+ hideProgress?: boolean,
432
501
  statsFormat = "percentages",
433
- hideValues,
434
- ) => {
502
+ hideValues?: boolean,
503
+ ): string => {
435
504
  const paddingRight = 50;
436
505
  const offsetWidth = width - paddingRight;
437
506
  // progressOffset holds the previous language's width and used to offset the next language
@@ -439,6 +508,8 @@ const renderCompactLayout = (
439
508
  let progressOffset = 0;
440
509
  const compactProgressBar = langs
441
510
  .map((lang) => {
511
+ const langColor = resolveLangColor(lang);
512
+
442
513
  const percentage = parseFloat(
443
514
  ((lang.size / totalLanguageSize) * offsetWidth).toFixed(2),
444
515
  );
@@ -453,7 +524,7 @@ const renderCompactLayout = (
453
524
  y="0"
454
525
  width="${progress}"
455
526
  height="8"
456
- fill="${lang.color || "#858585"}"
527
+ fill="${langColor}"
457
528
  />
458
529
  `;
459
530
  progressOffset += percentage;
@@ -487,24 +558,24 @@ const renderCompactLayout = (
487
558
  /**
488
559
  * Renders donut vertical layout to display user's most frequently used programming languages.
489
560
  *
490
- * @param {Lang[]} langs Array of programming languages.
491
- * @param {number} totalLanguageSize Total size of all languages.
492
- * @param {string} statsFormat Stats format.
493
- * @param {boolean=} hideValues Whether to hide stats values.
494
- * @returns {string} Compact layout card SVG object.
561
+ * @param langs Array of programming languages.
562
+ * @param totalLanguageSize Total size of all languages.
563
+ * @param statsFormat Stats format.
564
+ * @param hideValues Whether to hide stats values.
565
+ * @returns Compact layout card SVG object.
495
566
  */
496
567
  const renderDonutVerticalLayout = (
497
- langs,
498
- totalLanguageSize,
499
- statsFormat,
500
- hideValues,
501
- ) => {
568
+ langs: Array<Lang>,
569
+ totalLanguageSize: number,
570
+ statsFormat: string,
571
+ hideValues?: boolean,
572
+ ): string => {
502
573
  // Donut vertical chart radius and total length
503
574
  const radius = 80;
504
575
  const totalCircleLength = getCircleLength(radius);
505
576
 
506
577
  // SVG circles
507
- let circles = [];
578
+ const circles = [];
508
579
 
509
580
  // Start indent for donut vertical chart parts
510
581
  let indent = 0;
@@ -514,18 +585,20 @@ const renderDonutVerticalLayout = (
514
585
 
515
586
  // Generate each donut vertical chart part
516
587
  for (const lang of langs) {
588
+ const langColor = resolveLangColor(lang);
589
+
517
590
  const percentage = (lang.size / totalLanguageSize) * 100;
518
591
  const circleLength = totalCircleLength * (percentage / 100);
519
592
  const delay = startDelayCoefficient * 100;
520
593
 
521
594
  circles.push(`
522
595
  <g class="stagger" style="animation-delay: ${delay}ms">
523
- <circle
596
+ <circle
524
597
  cx="150"
525
598
  cy="100"
526
599
  r="${radius}"
527
600
  fill="transparent"
528
- stroke="${lang.color}"
601
+ stroke="${langColor}"
529
602
  stroke-width="25"
530
603
  stroke-dasharray="${totalCircleLength}"
531
604
  stroke-dashoffset="${indent}"
@@ -566,13 +639,18 @@ const renderDonutVerticalLayout = (
566
639
  /**
567
640
  * Renders pie layout to display user's most frequently used programming languages.
568
641
  *
569
- * @param {Lang[]} langs Array of programming languages.
570
- * @param {number} totalLanguageSize Total size of all languages.
571
- * @param {string} statsFormat Stats format.
572
- * @param {boolean=} hideValues Whether to hide stats values.
573
- * @returns {string} Compact layout card SVG object.
642
+ * @param langs Array of programming languages.
643
+ * @param totalLanguageSize Total size of all languages.
644
+ * @param statsFormat Stats format.
645
+ * @param hideValues Whether to hide stats values.
646
+ * @returns Compact layout card SVG object.
574
647
  */
575
- const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
648
+ const renderPieLayout = (
649
+ langs: Array<Lang>,
650
+ totalLanguageSize: number,
651
+ statsFormat: string,
652
+ hideValues?: boolean,
653
+ ): string => {
576
654
  // Pie chart radius and center coordinates
577
655
  const radius = 90;
578
656
  const centerX = 150;
@@ -589,6 +667,8 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
589
667
 
590
668
  // Generate each pie chart part
591
669
  for (const lang of langs) {
670
+ const langColor = resolveLangColor(lang);
671
+
592
672
  if (langs.length === 1) {
593
673
  paths.push(`
594
674
  <circle
@@ -596,7 +676,7 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
596
676
  cy="${centerY}"
597
677
  r="${radius}"
598
678
  stroke="none"
599
- fill="${lang.color}"
679
+ fill="${langColor}"
600
680
  data-testid="lang-pie"
601
681
  size="100"
602
682
  />
@@ -629,7 +709,7 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
629
709
  data-testid="lang-pie"
630
710
  size="${percentage}"
631
711
  d="M ${centerX} ${centerY} L ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endPoint.x} ${endPoint.y} Z"
632
- fill="${lang.color}"
712
+ fill="${langColor}"
633
713
  />
634
714
  </g>
635
715
  `);
@@ -665,34 +745,34 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
665
745
  /**
666
746
  * Creates the SVG paths for the language donut chart.
667
747
  *
668
- * @param {number} cx Donut center x-position.
669
- * @param {number} cy Donut center y-position.
670
- * @param {number} radius Donut arc Radius.
671
- * @param {number[]} percentages Array with donut section percentages.
672
- * @returns {{d: string, percent: number}[]} Array of svg path elements
748
+ * @param cx Donut center x-position.
749
+ * @param cy Donut center y-position.
750
+ * @param radius Donut arc Radius.
751
+ * @param percentages Array with donut section percentages.
752
+ * @returns Array of svg path elements
673
753
  */
674
- const createDonutPaths = (cx, cy, radius, percentages) => {
675
- const paths = [];
754
+ const createDonutPaths = (
755
+ cx: number,
756
+ cy: number,
757
+ radius: number,
758
+ percentages: Array<number>,
759
+ ): Array<{ d: string; percent: number }> => {
760
+ const paths: Array<{ d: string; percent: number }> = [];
676
761
  let startAngle = 0;
677
- let endAngle;
678
762
 
679
763
  const totalPercent = percentages.reduce((acc, curr) => acc + curr, 0);
680
- for (let i = 0; i < percentages.length; i++) {
681
- const tmpPath = {};
764
+ for (const rawPercent of percentages) {
765
+ const percent = parseFloat(((rawPercent / totalPercent) * 100).toFixed(2));
682
766
 
683
- let percent = parseFloat(
684
- ((percentages[i] / totalPercent) * 100).toFixed(2),
685
- );
686
-
687
- endAngle = 3.6 * percent + startAngle;
767
+ const endAngle = 3.6 * percent + startAngle;
688
768
  const startPoint = polarToCartesian(cx, cy, radius, endAngle - 90); // rotate donut 90 degrees counter-clockwise.
689
769
  const endPoint = polarToCartesian(cx, cy, radius, startAngle - 90); // rotate donut 90 degrees counter-clockwise.
690
770
  const largeArc = endAngle - startAngle <= 180 ? 0 : 1;
691
771
 
692
- tmpPath.percent = percent;
693
- tmpPath.d = `M ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArc} 0 ${endPoint.x} ${endPoint.y}`;
694
-
695
- paths.push(tmpPath);
772
+ paths.push({
773
+ percent,
774
+ d: `M ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArc} 0 ${endPoint.x} ${endPoint.y}`,
775
+ });
696
776
  startAngle = endAngle;
697
777
  }
698
778
 
@@ -702,26 +782,30 @@ const createDonutPaths = (cx, cy, radius, percentages) => {
702
782
  /**
703
783
  * Renders the donut language card layout.
704
784
  *
705
- * @param {Lang[]} langs Array of programming languages.
706
- * @param {number} width Card width.
707
- * @param {number} totalLanguageSize Total size of all languages.
708
- * @param {string} statsFormat Stats format.
709
- * @param {boolean=} hideValues Whether to hide stats values.
710
- * @returns {string} Donut layout card SVG object.
785
+ * @param langs Array of programming languages.
786
+ * @param width Card width.
787
+ * @param totalLanguageSize Total size of all languages.
788
+ * @param statsFormat Stats format.
789
+ * @param hideValues Whether to hide stats values.
790
+ * @returns Donut layout card SVG object.
711
791
  */
712
792
  const renderDonutLayout = (
713
- langs,
714
- width,
715
- totalLanguageSize,
716
- statsFormat,
717
- hideValues,
718
- ) => {
793
+ langs: Array<Lang>,
794
+ width: number,
795
+ totalLanguageSize: number,
796
+ statsFormat: string,
797
+ hideValues?: boolean,
798
+ ): string => {
799
+ if (!Number.isFinite(width)) {
800
+ throw new Error(`Invalid width: "${width}"`);
801
+ }
802
+
719
803
  const centerX = width / 3;
720
804
  const centerY = width / 3;
721
805
  const radius = centerX - 60;
722
806
  const strokeWidth = 12;
723
807
 
724
- const colors = langs.map((lang) => lang.color);
808
+ const colors = langs.map(resolveLangColor);
725
809
  const langsPercents = langs.map((lang) =>
726
810
  parseFloat(((lang.size / totalLanguageSize) * 100).toFixed(2)),
727
811
  );
@@ -730,7 +814,7 @@ const renderDonutLayout = (
730
814
 
731
815
  const donutPaths =
732
816
  langs.length === 1
733
- ? `<circle cx="${centerX}" cy="${centerY}" r="${radius}" stroke="${colors[0]}" fill="none" stroke-width="${strokeWidth}" data-testid="lang-donut" size="100"/>`
817
+ ? `<circle cx="${centerX}" cy="${centerY}" r="${radius}" stroke="${colors[0] ?? DEFAULT_LANG_COLOR}" fill="none" stroke-width="${strokeWidth}" data-testid="lang-donut" size="100"/>`
734
818
  : langPaths
735
819
  .map((section, index) => {
736
820
  const staggerDelay = (index + 3) * 100;
@@ -742,7 +826,7 @@ const renderDonutLayout = (
742
826
  data-testid="lang-donut"
743
827
  size="${section.percent}"
744
828
  d="${section.d}"
745
- stroke="${colors[index]}"
829
+ stroke="${colors[index] ?? DEFAULT_LANG_COLOR}"
746
830
  fill="none"
747
831
  stroke-width="${strokeWidth}">
748
832
  </path>
@@ -768,37 +852,43 @@ const renderDonutLayout = (
768
852
  `;
769
853
  };
770
854
 
771
- /**
772
- * @typedef {import("./types").TopLangOptions} TopLangOptions
773
- * @typedef {TopLangOptions["layout"]} Layout
774
- */
775
-
776
855
  /**
777
856
  * Creates the no languages data SVG node.
778
857
  *
779
- * @param {object} props Object with function properties.
780
- * @param {string} props.color No languages data text color.
781
- * @param {string} props.text No languages data translated text.
782
- * @param {Layout | undefined} props.layout Card layout.
783
- * @returns {string} No languages data SVG node string.
858
+ * @param props Object with function properties.
859
+ * @param props.text No languages data translated text.
860
+ * @param props.layout Card layout.
861
+ * @returns No languages data SVG node string.
784
862
  */
785
- const noLanguagesDataNode = ({ color, text, layout }) => {
863
+ const noLanguagesDataNode = ({
864
+ text,
865
+ layout,
866
+ }: {
867
+ text: string;
868
+ layout: TopLangLayout | undefined;
869
+ }): string => {
786
870
  return `
787
871
  <text x="${
788
872
  layout === "pie" || layout === "donut-vertical" ? CARD_PADDING : 0
789
- }" y="11" class="stat bold" fill="${color}">${text}</text>
873
+ }" y="11" class="stat bold">${encodeHTML(text)}</text>
790
874
  `;
791
875
  };
792
876
 
793
877
  /**
794
878
  * Get default languages count for provided card layout.
795
879
  *
796
- * @param {object} props Function properties.
797
- * @param {Layout=} props.layout Input layout string.
798
- * @param {boolean=} props.hide_progress Input hide_progress parameter value.
799
- * @returns {number} Default languages count for input layout.
880
+ * @param props Function properties.
881
+ * @param props.layout Input layout string.
882
+ * @param props.hide_progress Input hide_progress parameter value.
883
+ * @returns Default languages count for input layout.
800
884
  */
801
- const getDefaultLanguagesCountByLayout = ({ layout, hide_progress }) => {
885
+ const getDefaultLanguagesCountByLayout = ({
886
+ layout,
887
+ hide_progress,
888
+ }: {
889
+ layout?: TopLangLayout | undefined;
890
+ hide_progress?: boolean | undefined;
891
+ }): number => {
802
892
  if (layout === "compact" || hide_progress === true) {
803
893
  return COMPACT_LAYOUT_DEFAULT_LANGS_COUNT;
804
894
  } else if (layout === "donut") {
@@ -812,36 +902,29 @@ const getDefaultLanguagesCountByLayout = ({ layout, hide_progress }) => {
812
902
  }
813
903
  };
814
904
 
815
- /**
816
- * @typedef {import('../fetchers/types').TopLangData} TopLangData
817
- */
818
-
819
905
  /**
820
906
  * Renders card that display user's most frequently used programming languages.
821
907
  *
822
- * @param {TopLangData} topLangs User's most frequently used programming languages.
823
- * @param {Partial<TopLangOptions>} options Card options.
824
- * @returns {string} Language card SVG object.
908
+ * @param topLangs User's most frequently used programming languages.
909
+ * @param options Card options.
910
+ * @returns Language card SVG object.
825
911
  */
826
- const renderTopLanguages = (topLangs, options = {}) => {
912
+ const renderTopLanguages = (
913
+ topLangs: TopLangData,
914
+ options: Partial<TopLangOptions> = {},
915
+ ): string => {
827
916
  const {
828
917
  hide_title = false,
829
918
  hide_border = false,
830
919
  card_width,
831
- title_color,
832
- text_color,
833
- bg_color,
834
- prog_bar_bg_color,
835
920
  hide,
836
921
  hide_progress,
837
922
  hide_values,
838
- theme,
839
923
  layout,
840
924
  custom_title,
841
925
  locale,
842
926
  langs_count = getDefaultLanguagesCountByLayout({ layout, hide_progress }),
843
927
  border_radius,
844
- border_color,
845
928
  disable_animations,
846
929
  stats_format = "percentages",
847
930
  } = options;
@@ -866,20 +949,12 @@ const renderTopLanguages = (topLangs, options = {}) => {
866
949
  : DEFAULT_CARD_WIDTH;
867
950
  let height = calculateNormalLayoutHeight(langs.length);
868
951
 
869
- // returns theme based colors with proper overrides and defaults
870
- const colors = getCardColors({
871
- title_color,
872
- text_color,
873
- bg_color,
874
- border_color,
875
- theme,
876
- });
952
+ const { lightColors, darkColors } = getLightDarkColors(options);
877
953
 
878
- let finalLayout;
954
+ let finalLayout: string;
879
955
  if (langs.length === 0) {
880
956
  height = COMPACT_LAYOUT_BASE_HEIGHT;
881
957
  finalLayout = noLanguagesDataNode({
882
- color: colors.textColor,
883
958
  text: i18n.t("langcard.nodata"),
884
959
  layout,
885
960
  });
@@ -899,7 +974,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
899
974
  stats_format,
900
975
  hide_values,
901
976
  );
902
- } else if (layout === "compact" || hide_progress == true) {
977
+ } else if (layout === "compact" || hide_progress === true) {
903
978
  height =
904
979
  calculateCompactLayoutHeight(langs.length) + (hide_progress ? -25 : 0);
905
980
 
@@ -926,7 +1001,6 @@ const renderTopLanguages = (topLangs, options = {}) => {
926
1001
  langs,
927
1002
  width,
928
1003
  totalLanguageSize,
929
- fallbackColor(prog_bar_bg_color, "#ddd"),
930
1004
  stats_format,
931
1005
  hide_values,
932
1006
  );
@@ -938,7 +1012,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
938
1012
  width,
939
1013
  height,
940
1014
  border_radius,
941
- colors,
1015
+ colors: { light: lightColors, dark: darkColors },
942
1016
  });
943
1017
 
944
1018
  if (disable_animations) {
@@ -947,8 +1021,8 @@ const renderTopLanguages = (topLangs, options = {}) => {
947
1021
 
948
1022
  card.setHideBorder(hide_border);
949
1023
  card.setHideTitle(hide_title);
950
- card.setCSS(
951
- `
1024
+ card.setCSS({
1025
+ light: `
952
1026
  @keyframes slideInAnimation {
953
1027
  from {
954
1028
  width: 0;
@@ -966,7 +1040,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
966
1040
  }
967
1041
  }
968
1042
  .stat {
969
- font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: ${colors.textColor};
1043
+ font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: ${lightColors.textColor};
970
1044
  }
971
1045
  @supports(-moz-appearance: auto) {
972
1046
  /* Selector detects Firefox */
@@ -975,7 +1049,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
975
1049
  .bold { font-weight: 700 }
976
1050
  .lang-name {
977
1051
  font: 400 11px "Segoe UI", Ubuntu, Sans-Serif;
978
- fill: ${colors.textColor};
1052
+ fill: ${lightColors.textColor};
979
1053
  }
980
1054
  .stagger {
981
1055
  opacity: 0;
@@ -987,8 +1061,16 @@ const renderTopLanguages = (topLangs, options = {}) => {
987
1061
  .lang-progress{
988
1062
  animation: growWidthAnimation 0.6s ease-in-out forwards;
989
1063
  }
1064
+ .progress-background { fill: ${lightColors.progBarBgColor}; }
990
1065
  `,
991
- );
1066
+ dark: darkColors
1067
+ ? `
1068
+ .stat { fill: ${darkColors.textColor}; }
1069
+ .lang-name { fill: ${darkColors.textColor}; }
1070
+ .progress-background { fill: ${darkColors.progBarBgColor}; }
1071
+ `
1072
+ : null,
1073
+ });
992
1074
 
993
1075
  if (layout === "pie" || layout === "donut-vertical") {
994
1076
  return card.render(finalLayout);