@stats-forge/github-stats-forge-core 0.0.2 → 0.1.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 (208) hide show
  1. package/README.md +0 -10
  2. package/build/api/api-result.d.ts +6 -7
  3. package/build/api/api-result.d.ts.map +1 -1
  4. package/build/api/api-result.js +4 -5
  5. package/build/api/gist.d.ts +3 -10
  6. package/build/api/gist.d.ts.map +1 -1
  7. package/build/api/gist.js +4 -11
  8. package/build/api/index.d.ts +12 -12
  9. package/build/api/params.d.ts +11 -8
  10. package/build/api/params.d.ts.map +1 -1
  11. package/build/api/params.js +16 -14
  12. package/build/api/pin.d.ts +3 -19
  13. package/build/api/pin.d.ts.map +1 -1
  14. package/build/api/pin.js +6 -22
  15. package/build/api/stats.d.ts +10 -4
  16. package/build/api/stats.d.ts.map +1 -1
  17. package/build/api/stats.js +6 -32
  18. package/build/api/top-langs.d.ts +12 -6
  19. package/build/api/top-langs.d.ts.map +1 -1
  20. package/build/api/top-langs.js +6 -26
  21. package/build/api/wakatime.d.ts +13 -5
  22. package/build/api/wakatime.d.ts.map +1 -1
  23. package/build/api/wakatime.js +4 -21
  24. package/build/calculateRank.d.ts +0 -9
  25. package/build/calculateRank.d.ts.map +1 -1
  26. package/build/calculateRank.js +12 -17
  27. package/build/cards/gist.d.ts +2 -4
  28. package/build/cards/gist.d.ts.map +1 -1
  29. package/build/cards/gist.js +27 -36
  30. package/build/cards/options.d.ts +1 -1
  31. package/build/cards/repo.d.ts +2 -4
  32. package/build/cards/repo.d.ts.map +1 -1
  33. package/build/cards/repo.js +61 -93
  34. package/build/cards/stats.d.ts +3 -8
  35. package/build/cards/stats.d.ts.map +1 -1
  36. package/build/cards/stats.js +119 -165
  37. package/build/cards/top-languages.d.ts +5 -31
  38. package/build/cards/top-languages.d.ts.map +1 -1
  39. package/build/cards/top-languages.js +176 -384
  40. package/build/cards/wakatime.d.ts +4 -6
  41. package/build/cards/wakatime.d.ts.map +1 -1
  42. package/build/cards/wakatime.js +118 -185
  43. package/build/common/Card.d.ts +14 -62
  44. package/build/common/Card.d.ts.map +1 -1
  45. package/build/common/Card.js +97 -199
  46. package/build/common/I18n.d.ts +0 -6
  47. package/build/common/I18n.d.ts.map +1 -1
  48. package/build/common/I18n.js +3 -6
  49. package/build/common/color.d.ts +4 -17
  50. package/build/common/color.d.ts.map +1 -1
  51. package/build/common/color.js +67 -49
  52. package/build/common/config.d.ts +4 -14
  53. package/build/common/config.d.ts.map +1 -1
  54. package/build/common/config.js +3 -16
  55. package/build/common/constants.d.ts +4 -2
  56. package/build/common/constants.d.ts.map +1 -1
  57. package/build/common/constants.js +3 -1
  58. package/build/common/date.d.ts +0 -2
  59. package/build/common/date.d.ts.map +1 -1
  60. package/build/common/date.js +0 -2
  61. package/build/common/emojiMap.d.ts +8 -0
  62. package/build/common/emojiMap.d.ts.map +1 -0
  63. package/build/common/emojiMap.js +10 -0
  64. package/build/common/emojiMap.json +1915 -0
  65. package/build/common/error.d.ts +1 -10
  66. package/build/common/error.d.ts.map +1 -1
  67. package/build/common/error.js +1 -10
  68. package/build/common/fmt.d.ts +3 -15
  69. package/build/common/fmt.d.ts.map +1 -1
  70. package/build/common/fmt.js +7 -35
  71. package/build/common/http.d.ts +2 -7
  72. package/build/common/http.d.ts.map +1 -1
  73. package/build/common/http.js +8 -17
  74. package/build/common/icons.d.ts +18 -20
  75. package/build/common/icons.d.ts.map +1 -1
  76. package/build/common/icons.js +103 -67
  77. package/build/common/languageColors.d.ts +0 -1
  78. package/build/common/languageColors.d.ts.map +1 -1
  79. package/build/common/languageColors.js +1 -4
  80. package/build/common/languageColors.json +18 -2
  81. package/build/common/ops.d.ts +3 -14
  82. package/build/common/ops.d.ts.map +1 -1
  83. package/build/common/ops.js +19 -30
  84. package/build/common/render.d.ts +28 -82
  85. package/build/common/render.d.ts.map +1 -1
  86. package/build/common/render.js +175 -211
  87. package/build/common/retryer.d.ts +2 -7
  88. package/build/common/retryer.d.ts.map +1 -1
  89. package/build/common/retryer.js +4 -11
  90. package/build/fetchers/gist.d.ts +2 -5
  91. package/build/fetchers/gist.d.ts.map +1 -1
  92. package/build/fetchers/gist.js +1 -5
  93. package/build/fetchers/index.d.ts +12 -12
  94. package/build/fetchers/repo.d.ts +2 -11
  95. package/build/fetchers/repo.d.ts.map +1 -1
  96. package/build/fetchers/repo.js +20 -33
  97. package/build/fetchers/stats.d.ts +2 -32
  98. package/build/fetchers/stats.d.ts.map +1 -1
  99. package/build/fetchers/stats.js +27 -86
  100. package/build/fetchers/top-languages.d.ts +2 -9
  101. package/build/fetchers/top-languages.d.ts.map +1 -1
  102. package/build/fetchers/top-languages.js +15 -31
  103. package/build/fetchers/types.d.ts +1 -1
  104. package/build/fetchers/wakatime.d.ts +2 -6
  105. package/build/fetchers/wakatime.d.ts.map +1 -1
  106. package/build/fetchers/wakatime.js +1 -5
  107. package/build/graphql/contributionsDocument.d.ts +3 -3
  108. package/build/graphql/contributionsDocument.d.ts.map +1 -1
  109. package/build/graphql/contributionsDocument.js +0 -1
  110. package/build/graphql/generated/gist.d.ts +1 -1
  111. package/build/graphql/generated/repo.d.ts +1 -1
  112. package/build/graphql/generated/stats.d.ts +3 -3
  113. package/build/graphql/generated/top-languages.d.ts +2 -2
  114. package/build/graphql/graphqlDocument.d.ts +0 -1
  115. package/build/graphql/graphqlDocument.d.ts.map +1 -1
  116. package/build/graphql/graphqlDocument.js +0 -1
  117. package/build/graphql/reposContributedToDocument.d.ts +4 -5
  118. package/build/graphql/reposContributedToDocument.d.ts.map +1 -1
  119. package/build/graphql/reposContributedToDocument.js +0 -2
  120. package/build/markup/css.d.ts +35 -0
  121. package/build/markup/css.d.ts.map +1 -0
  122. package/build/markup/css.js +64 -0
  123. package/build/markup/element.d.ts +41 -0
  124. package/build/markup/element.d.ts.map +1 -0
  125. package/build/markup/element.js +127 -0
  126. package/build/markup/html.d.ts +13 -0
  127. package/build/markup/html.d.ts.map +1 -0
  128. package/build/markup/html.js +27 -0
  129. package/build/markup/index.d.ts +9 -0
  130. package/build/markup/index.d.ts.map +1 -0
  131. package/build/markup/index.js +6 -0
  132. package/build/markup/serialize.d.ts +16 -0
  133. package/build/markup/serialize.d.ts.map +1 -0
  134. package/build/markup/serialize.js +17 -0
  135. package/build/themes/index.d.ts +332 -333
  136. package/build/themes/index.d.ts.map +1 -1
  137. package/build/themes/index.js +1 -1
  138. package/build/translations.d.ts +2 -6
  139. package/build/translations.d.ts.map +1 -1
  140. package/build/translations.js +621 -628
  141. package/package.json +17 -26
  142. package/src/api/api-result.ts +10 -9
  143. package/src/api/gist.ts +11 -18
  144. package/src/api/index.ts +12 -12
  145. package/src/api/params.ts +27 -21
  146. package/src/api/pin.ts +13 -28
  147. package/src/api/stats.ts +13 -39
  148. package/src/api/top-langs.ts +13 -33
  149. package/src/api/wakatime.ts +11 -28
  150. package/src/calculateRank.ts +12 -23
  151. package/src/cards/gist.ts +40 -43
  152. package/src/cards/options.ts +1 -1
  153. package/src/cards/repo.ts +97 -107
  154. package/src/cards/stats.ts +165 -184
  155. package/src/cards/top-languages.ts +297 -413
  156. package/src/cards/wakatime.ts +193 -221
  157. package/src/common/Card.ts +165 -217
  158. package/src/common/I18n.ts +3 -6
  159. package/src/common/color.ts +80 -57
  160. package/src/common/config.ts +5 -18
  161. package/src/common/constants.ts +5 -2
  162. package/src/common/date.ts +0 -2
  163. package/src/common/emojiMap.json +1915 -0
  164. package/src/common/emojiMap.ts +13 -0
  165. package/src/common/error.ts +2 -11
  166. package/src/common/fmt.ts +7 -44
  167. package/src/common/http.ts +9 -15
  168. package/src/common/icons.ts +117 -42
  169. package/src/common/languageColors.json +18 -2
  170. package/src/common/languageColors.ts +1 -4
  171. package/src/common/ops.ts +26 -34
  172. package/src/common/render.ts +279 -232
  173. package/src/common/retryer.ts +8 -15
  174. package/src/fetchers/gist.ts +8 -12
  175. package/src/fetchers/index.ts +12 -12
  176. package/src/fetchers/repo.ts +27 -40
  177. package/src/fetchers/stats.ts +44 -102
  178. package/src/fetchers/top-languages.ts +26 -40
  179. package/src/fetchers/types.ts +1 -1
  180. package/src/fetchers/wakatime.ts +5 -9
  181. package/src/graphql/contributionsDocument.ts +7 -5
  182. package/src/graphql/generated/gist.ts +1 -1
  183. package/src/graphql/generated/repo.ts +1 -1
  184. package/src/graphql/generated/stats.ts +2 -2
  185. package/src/graphql/generated/top-languages.ts +2 -2
  186. package/src/graphql/graphqlDocument.ts +0 -1
  187. package/src/graphql/reposContributedToDocument.ts +6 -7
  188. package/src/markup/css.ts +118 -0
  189. package/src/markup/element.ts +246 -0
  190. package/src/markup/html.ts +34 -0
  191. package/src/markup/index.ts +9 -0
  192. package/src/markup/serialize.ts +27 -0
  193. package/src/themes/index.ts +1 -1
  194. package/src/translations.ts +622 -629
  195. package/build/cards/index.d.ts +0 -11
  196. package/build/cards/index.d.ts.map +0 -1
  197. package/build/cards/index.js +0 -7
  198. package/build/common/html.d.ts +0 -8
  199. package/build/common/html.d.ts.map +0 -1
  200. package/build/common/html.js +0 -14
  201. package/build/index.d.ts +0 -15
  202. package/build/index.d.ts.map +0 -1
  203. package/build/index.js +0 -10
  204. package/src/_emoji-name-map.d.ts +0 -10
  205. package/src/_github-username-regex.d.ts +0 -4
  206. package/src/cards/index.ts +0 -21
  207. package/src/common/html.ts +0 -17
  208. package/src/index.ts +0 -18
@@ -1,11 +1,11 @@
1
1
  import { Card } from '../common/Card.js';
2
2
  import { getLightDarkColors, isPrefixedHexColor } from '../common/color.js';
3
3
  import { formatBytes } from '../common/fmt.js';
4
- import { encodeHTML } from '../common/html.js';
5
4
  import { I18n } from '../common/I18n.js';
6
5
  import { DEFAULT_LANG_COLOR } from '../common/languageColors.js';
7
6
  import { chunkArray, clampValue, lowercaseTrim } from '../common/ops.js';
8
7
  import { createProgressNode, flexLayout, measureText } from '../common/render.js';
8
+ import { atRule, cssComment, el, rule } from '../markup/index.js';
9
9
  import { langCardLocales } from '../translations.js';
10
10
  const DEFAULT_CARD_WIDTH = 300;
11
11
  const MIN_CARD_WIDTH = 280;
@@ -24,31 +24,32 @@ const TOP_LANG_STATS_FORMATS = ['percentages', 'bytes'];
24
24
  /**
25
25
  * Retrieves the programming language whose name is the longest.
26
26
  *
27
- * @param arr Array of programming languages.
28
27
  * @returns Longest programming language object.
29
28
  */
30
- const getLongestLang = (arr) => arr.reduce((savedLang, lang) => (lang.name.length > savedLang.name.length ? lang : savedLang), { name: '', size: 0, color: '' });
29
+ const getLongestLang = (arr) => {
30
+ let longest = { name: '', size: 0, color: '' };
31
+ for (const lang of arr) {
32
+ if (lang.name.length > longest.name.length) {
33
+ longest = lang;
34
+ }
35
+ }
36
+ return longest;
37
+ };
31
38
  /**
32
39
  * Convert degrees to radians.
33
40
  *
34
- * @param angleInDegrees Angle in degrees.
35
41
  * @returns Angle in radians.
36
42
  */
37
- const degreesToRadians = (angleInDegrees) => angleInDegrees * (Math.PI / 180.0);
43
+ const degreesToRadians = (angleInDegrees) => angleInDegrees * (Math.PI / 180);
38
44
  /**
39
45
  * Convert radians to degrees.
40
46
  *
41
- * @param angleInRadians Angle in radians.
42
47
  * @returns Angle in degrees.
43
48
  */
44
- const radiansToDegrees = (angleInRadians) => angleInRadians / (Math.PI / 180.0);
49
+ const radiansToDegrees = (angleInRadians) => angleInRadians / (Math.PI / 180);
45
50
  /**
46
51
  * Convert polar coordinates to cartesian coordinates.
47
52
  *
48
- * @param centerX Center x coordinate.
49
- * @param centerY Center y coordinate.
50
- * @param radius Radius of the circle.
51
- * @param angleInDegrees Angle in degrees.
52
53
  * @returns Cartesian coordinates.
53
54
  */
54
55
  const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
@@ -61,14 +62,10 @@ const polarToCartesian = (centerX, centerY, radius, angleInDegrees) => {
61
62
  /**
62
63
  * Convert cartesian coordinates to polar coordinates.
63
64
  *
64
- * @param centerX Center x coordinate.
65
- * @param centerY Center y coordinate.
66
- * @param x Point x coordinate.
67
- * @param y Point y coordinate.
68
65
  * @returns Polar coordinates.
69
66
  */
70
67
  const cartesianToPolar = (centerX, centerY, x, y) => {
71
- const radius = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
68
+ const radius = Math.hypot(x - centerX, y - centerY);
72
69
  let angleInDegrees = radiansToDegrees(Math.atan2(y - centerY, x - centerX));
73
70
  if (angleInDegrees < 0) {
74
71
  angleInDegrees += 360;
@@ -78,72 +75,48 @@ const cartesianToPolar = (centerX, centerY, x, y) => {
78
75
  /**
79
76
  * Calculates length of circle.
80
77
  *
81
- * @param radius Radius of the circle.
82
78
  * @returns The length of the circle.
83
79
  */
84
- const getCircleLength = (radius) => {
85
- return 2 * Math.PI * radius;
86
- };
80
+ const getCircleLength = (radius) => 2 * Math.PI * radius;
87
81
  /**
88
82
  * Calculates height for the compact layout.
89
83
  *
90
- * @param totalLangs Total number of languages.
91
84
  * @returns Card height.
92
85
  */
93
- const calculateCompactLayoutHeight = (totalLangs) => {
94
- return COMPACT_LAYOUT_BASE_HEIGHT + Math.round(totalLangs / 2) * 25;
95
- };
86
+ const calculateCompactLayoutHeight = (totalLangs) => COMPACT_LAYOUT_BASE_HEIGHT + Math.round(totalLangs / 2) * 25;
96
87
  /**
97
88
  * Calculates height for the normal layout.
98
89
  *
99
- * @param totalLangs Total number of languages.
100
90
  * @returns Card height.
101
91
  */
102
- const calculateNormalLayoutHeight = (totalLangs) => {
103
- return 45 + (totalLangs + 1) * 40;
104
- };
92
+ const calculateNormalLayoutHeight = (totalLangs) => 45 + (totalLangs + 1) * 40;
105
93
  /**
106
94
  * Calculates height for the donut layout.
107
95
  *
108
- * @param totalLangs Total number of languages.
109
96
  * @returns Card height.
110
97
  */
111
- const calculateDonutLayoutHeight = (totalLangs) => {
112
- return 215 + Math.max(totalLangs - 5, 0) * 32;
113
- };
98
+ const calculateDonutLayoutHeight = (totalLangs) => 215 + Math.max(totalLangs - 5, 0) * 32;
114
99
  /**
115
100
  * Calculates height for the donut vertical layout.
116
101
  *
117
- * @param totalLangs Total number of languages.
118
102
  * @returns Card height.
119
103
  */
120
- const calculateDonutVerticalLayoutHeight = (totalLangs) => {
121
- return 300 + Math.round(totalLangs / 2) * 25;
122
- };
104
+ const calculateDonutVerticalLayoutHeight = (totalLangs) => 300 + Math.round(totalLangs / 2) * 25;
123
105
  /**
124
106
  * Calculates height for the pie layout.
125
107
  *
126
- * @param totalLangs Total number of languages.
127
108
  * @returns Card height.
128
109
  */
129
- const calculatePieLayoutHeight = (totalLangs) => {
130
- return 300 + Math.round(totalLangs / 2) * 25;
131
- };
110
+ const calculatePieLayoutHeight = (totalLangs) => 300 + Math.round(totalLangs / 2) * 25;
132
111
  /**
133
112
  * Calculates the center translation needed to keep the donut chart centred.
134
113
  *
135
- * @param totalLangs Total number of languages.
136
114
  * @returns Donut center translation.
137
115
  */
138
- const donutCenterTranslation = (totalLangs) => {
139
- return -45 + Math.max(totalLangs - 5, 0) * 16;
140
- };
116
+ const donutCenterTranslation = (totalLangs) => -45 + Math.max(totalLangs - 5, 0) * 16;
141
117
  /**
142
118
  * Trim top languages to lang_count while also hiding certain languages.
143
119
  *
144
- * @param topLangs Top languages.
145
- * @param langs_count Number of languages to show.
146
- * @param hide Languages to hide.
147
120
  * @returns Trimmed top languages and total size.
148
121
  */
149
122
  const trimTopLanguages = (topLangs, langs_count, hide) => {
@@ -152,13 +125,13 @@ const trimTopLanguages = (topLangs, langs_count, hide) => {
152
125
  const langsCount = clampValue(langs_count, 1, MAXIMUM_LANGS_COUNT);
153
126
  // populate langsToHide map for quick lookup while filtering out
154
127
  if (hide) {
155
- hide.forEach((langName) => {
128
+ for (const langName of hide) {
156
129
  langsToHide[lowercaseTrim(langName)] = true;
157
- });
130
+ }
158
131
  }
159
132
  // filter out languages to be hidden
160
133
  langs = langs
161
- .sort((a, b) => b.size - a.size)
134
+ .toSorted((a, b) => b.size - a.size)
162
135
  .filter((lang) => !langsToHide[lowercaseTrim(lang.name)])
163
136
  .slice(0, langsCount);
164
137
  const totalLanguageSize = langs.reduce((acc, curr) => acc + curr.size, 0);
@@ -167,19 +140,13 @@ const trimTopLanguages = (topLangs, langs_count, hide) => {
167
140
  /**
168
141
  * Get display value corresponding to the format.
169
142
  *
170
- * @param size Bytes size.
171
- * @param percentages Percentage value.
172
- * @param format Format of the stats.
173
143
  * @returns Display value.
174
144
  */
175
- const getDisplayValue = (size, percentages, format) => {
176
- return format === 'bytes' ? formatBytes(size) : `${percentages.toFixed(2)}%`;
177
- };
145
+ const getDisplayValue = (size, percentages, format) => format === 'bytes' ? formatBytes(size) : `${percentages.toFixed(2)}%`;
178
146
  /**
179
147
  * Resolves a language's display color, falling back to the default, and
180
148
  * validates it is a prefixed hex color.
181
149
  *
182
- * @param lang Programming language object.
183
150
  * @returns Validated language color.
184
151
  */
185
152
  const resolveLangColor = (lang) => {
@@ -192,15 +159,6 @@ const resolveLangColor = (lang) => {
192
159
  /**
193
160
  * Create progress bar text item for a programming language.
194
161
  *
195
- * @param props Function properties.
196
- * @param props.width The card width
197
- * @param props.color Color of the programming language.
198
- * @param props.name Name of the programming language.
199
- * @param props.size Size of the programming language.
200
- * @param props.totalSize Total size of all languages.
201
- * @param props.statsFormat Stats format.
202
- * @param props.hideValues Whether to hide stats values.
203
- * @param props.index Index of the programming language.
204
162
  * @returns Programming language SVG node.
205
163
  */
206
164
  const createProgressTextNode = ({ width, color, name, size, totalSize, statsFormat, hideValues, index, }) => {
@@ -210,31 +168,18 @@ const createProgressTextNode = ({ width, color, name, size, totalSize, statsForm
210
168
  const progressWidth = width - paddingRight;
211
169
  const progress = (size / totalSize) * 100;
212
170
  const displayValue = getDisplayValue(size, progress, statsFormat);
213
- return `
214
- <g class="stagger" style="animation-delay: ${staggerDelay}ms">
215
- <text data-testid="lang-name" x="2" y="15" class="lang-name">${encodeHTML(name)}</text>
216
- ${hideValues ? '' : `<text x="${progressTextX}" y="34" class="lang-name">${encodeHTML(displayValue)}</text>`}
217
- ${createProgressNode({
171
+ return el('g', { class: 'stagger', style: `animation-delay: ${staggerDelay}ms` }, el('text', { 'data-testid': 'lang-name', x: 2, y: 15, class: 'lang-name' }, name), !hideValues && el('text', { x: progressTextX, y: 34, class: 'lang-name' }, displayValue), createProgressNode({
218
172
  x: 0,
219
173
  y: 25,
220
174
  color,
221
175
  width: progressWidth,
222
176
  progress,
223
177
  delay: staggerDelay + 300,
224
- })}
225
- </g>
226
- `;
178
+ }));
227
179
  };
228
180
  /**
229
181
  * Creates compact text item for a programming language.
230
182
  *
231
- * @param props Function properties.
232
- * @param props.lang Programming language object.
233
- * @param props.totalSize Total size of all languages.
234
- * @param props.hideProgress Whether to hide percentage.
235
- * @param props.hideValues Whether to hide stats values (percentages/bytes).
236
- * @param props.statsFormat Stats format
237
- * @param props.index Index of the programming language.
238
183
  * @returns Compact layout programming language SVG node.
239
184
  */
240
185
  const createCompactLangNode = ({ lang, totalSize, hideProgress, hideValues, statsFormat = 'percentages', index, }) => {
@@ -242,24 +187,12 @@ const createCompactLangNode = ({ lang, totalSize, hideProgress, hideValues, stat
242
187
  const displayValue = getDisplayValue(lang.size, percentages, statsFormat);
243
188
  const staggerDelay = (index + 3) * 150;
244
189
  const color = resolveLangColor(lang);
245
- return `
246
- <g class="stagger" style="animation-delay: ${staggerDelay}ms">
247
- <circle cx="5" cy="6" r="5" fill="${color}" />
248
- <text data-testid="lang-name" x="15" y="10" class='lang-name'>
249
- ${encodeHTML(lang.name)} ${hideProgress || hideValues ? '' : encodeHTML(displayValue)}
250
- </text>
251
- </g>
252
- `;
190
+ const label = hideProgress || hideValues ? lang.name : `${lang.name} ${displayValue}`;
191
+ return el('g', { class: 'stagger', style: `animation-delay: ${staggerDelay}ms` }, el('circle', { cx: 5, cy: 6, r: 5, fill: color }), el('text', { 'data-testid': 'lang-name', x: 15, y: 10, class: 'lang-name' }, label));
253
192
  };
254
193
  /**
255
194
  * Create compact languages text items for all programming languages.
256
195
  *
257
- * @param props Function properties.
258
- * @param props.langs Array of programming languages.
259
- * @param props.totalSize Total size of all languages.
260
- * @param props.hideProgress Whether to hide percentage.
261
- * @param props.hideValues Whether to hide stats values.
262
- * @param props.statsFormat Stats format
263
196
  * @returns Programming languages SVG node.
264
197
  */
265
198
  const createLanguageTextNode = ({ langs, totalSize, hideProgress, hideValues, statsFormat, }) => {
@@ -274,83 +207,52 @@ const createLanguageTextNode = ({ langs, totalSize, hideProgress, hideValues, st
274
207
  statsFormat,
275
208
  index,
276
209
  }));
277
- return flexLayout({
278
- items,
279
- gap: 25,
280
- direction: 'column',
281
- }).join('');
210
+ return flexLayout({ items, gap: 25, direction: 'column' });
282
211
  });
283
212
  const percent = ((longestLang.size / totalSize) * 100).toFixed(2);
284
213
  const minGap = 150;
285
214
  const maxGap = 20 + measureText(`${longestLang.name} ${percent}%`, 11);
286
- return flexLayout({
287
- items: layouts,
288
- gap: maxGap < minGap ? minGap : maxGap,
289
- }).join('');
215
+ return flexLayout({ items: layouts, gap: maxGap < minGap ? minGap : maxGap });
290
216
  };
291
217
  /**
292
218
  * Create donut languages text items for all programming languages.
293
219
  *
294
- * @param props Function properties.
295
- * @param props.langs Array of programming languages.
296
- * @param props.totalSize Total size of all languages.
297
- * @param props.hideValues Whether to hide stats values.
298
- * @param props.statsFormat Stats format
299
220
  * @returns Donut layout programming language SVG node.
300
221
  */
301
- const createDonutLanguagesNode = ({ langs, totalSize, hideValues, statsFormat, }) => {
302
- return flexLayout({
303
- items: langs.map((lang, index) => {
304
- return createCompactLangNode({
305
- lang,
306
- totalSize,
307
- hideProgress: false,
308
- hideValues,
309
- statsFormat,
310
- index,
311
- });
312
- }),
313
- gap: 32,
314
- direction: 'column',
315
- }).join('');
316
- };
222
+ const createDonutLanguagesNode = ({ langs, totalSize, hideValues, statsFormat, }) => flexLayout({
223
+ items: langs.map((lang, index) => createCompactLangNode({
224
+ lang,
225
+ totalSize,
226
+ hideProgress: false,
227
+ hideValues,
228
+ statsFormat,
229
+ index,
230
+ })),
231
+ gap: 32,
232
+ direction: 'column',
233
+ });
317
234
  /**
318
235
  * Renders the default language card layout.
319
236
  *
320
- * @param langs Array of programming languages.
321
- * @param width Card width.
322
- * @param totalLanguageSize Total size of all languages.
323
- * @param statsFormat Stats format.
324
- * @param hideValues Whether to hide stats values.
325
237
  * @returns Normal layout card SVG object.
326
238
  */
327
- const renderNormalLayout = (langs, width, totalLanguageSize, statsFormat, hideValues) => {
328
- return flexLayout({
329
- items: langs.map((lang, index) => {
330
- return createProgressTextNode({
331
- width,
332
- name: lang.name,
333
- color: lang.color || DEFAULT_LANG_COLOR,
334
- size: lang.size,
335
- totalSize: totalLanguageSize,
336
- statsFormat,
337
- hideValues,
338
- index,
339
- });
340
- }),
341
- gap: 40,
342
- direction: 'column',
343
- }).join('');
344
- };
239
+ const renderNormalLayout = (langs, width, totalLanguageSize, statsFormat, hideValues) => flexLayout({
240
+ items: langs.map((lang, index) => createProgressTextNode({
241
+ width,
242
+ name: lang.name,
243
+ color: lang.color || DEFAULT_LANG_COLOR,
244
+ size: lang.size,
245
+ totalSize: totalLanguageSize,
246
+ statsFormat,
247
+ hideValues,
248
+ index,
249
+ })),
250
+ gap: 40,
251
+ direction: 'column',
252
+ });
345
253
  /**
346
254
  * Renders the compact language card layout.
347
255
  *
348
- * @param langs Array of programming languages.
349
- * @param width Card width.
350
- * @param totalLanguageSize Total size of all languages.
351
- * @param hideProgress Whether to hide progress bar.
352
- * @param statsFormat Stats format.
353
- * @param hideValues Whether to hide stats values.
354
256
  * @returns Compact layout card SVG object.
355
257
  */
356
258
  const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress, statsFormat = 'percentages', hideValues) => {
@@ -359,53 +261,37 @@ const renderCompactLayout = (langs, width, totalLanguageSize, hideProgress, stat
359
261
  // progressOffset holds the previous language's width and used to offset the next language
360
262
  // so that we can stack them one after another, like this: [--][----][---]
361
263
  let progressOffset = 0;
362
- const compactProgressBar = langs
363
- .map((lang) => {
364
- const langColor = resolveLangColor(lang);
365
- const percentage = parseFloat(((lang.size / totalLanguageSize) * offsetWidth).toFixed(2));
366
- const progress = percentage < 10 ? percentage + 10 : percentage;
367
- const output = `
368
- <rect
369
- mask="url(#rect-mask)"
370
- data-testid="lang-progress"
371
- x="${progressOffset}"
372
- y="0"
373
- width="${progress}"
374
- height="8"
375
- fill="${langColor}"
376
- />
377
- `;
264
+ const compactProgressBar = langs.map((lang) => {
265
+ const percentage = Number.parseFloat(((lang.size / totalLanguageSize) * offsetWidth).toFixed(2));
266
+ const x = progressOffset;
378
267
  progressOffset += percentage;
379
- return output;
380
- })
381
- .join('');
382
- return `
383
- ${hideProgress
384
- ? ''
385
- : `
386
- <mask id="rect-mask">
387
- <rect x="0" y="0" width="${offsetWidth}" height="8" fill="white" rx="5"/>
388
- </mask>
389
- ${compactProgressBar}
390
- `}
391
- <g transform="translate(0, ${hideProgress ? '0' : '25'})">
392
- ${createLanguageTextNode({
393
- langs,
394
- totalSize: totalLanguageSize,
395
- hideProgress,
396
- statsFormat,
397
- hideValues,
398
- })}
399
- </g>
400
- `;
268
+ return el('rect', {
269
+ mask: 'url(#rect-mask)',
270
+ 'data-testid': 'lang-progress',
271
+ x,
272
+ y: 0,
273
+ width: percentage < 10 ? percentage + 10 : percentage,
274
+ height: 8,
275
+ fill: resolveLangColor(lang),
276
+ });
277
+ });
278
+ return [
279
+ !hideProgress && [
280
+ el('mask', { id: 'rect-mask' }, el('rect', { x: 0, y: 0, width: offsetWidth, height: 8, fill: 'white', rx: 5 })),
281
+ compactProgressBar,
282
+ ],
283
+ el('g', { transform: `translate(0, ${hideProgress ? 0 : 25})` }, createLanguageTextNode({
284
+ langs,
285
+ totalSize: totalLanguageSize,
286
+ hideProgress,
287
+ statsFormat,
288
+ hideValues,
289
+ })),
290
+ ];
401
291
  };
402
292
  /**
403
293
  * Renders donut vertical layout to display user's most frequently used programming languages.
404
294
  *
405
- * @param langs Array of programming languages.
406
- * @param totalLanguageSize Total size of all languages.
407
- * @param statsFormat Stats format.
408
- * @param hideValues Whether to hide stats values.
409
295
  * @returns Compact layout card SVG object.
410
296
  */
411
297
  const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
@@ -424,55 +310,34 @@ const renderDonutVerticalLayout = (langs, totalLanguageSize, statsFormat, hideVa
424
310
  const percentage = (lang.size / totalLanguageSize) * 100;
425
311
  const circleLength = totalCircleLength * (percentage / 100);
426
312
  const delay = startDelayCoefficient * 100;
427
- circles.push(`
428
- <g class="stagger" style="animation-delay: ${delay}ms">
429
- <circle
430
- cx="150"
431
- cy="100"
432
- r="${radius}"
433
- fill="transparent"
434
- stroke="${langColor}"
435
- stroke-width="25"
436
- stroke-dasharray="${totalCircleLength}"
437
- stroke-dashoffset="${indent}"
438
- size="${percentage}"
439
- data-testid="lang-donut"
440
- />
441
- </g>
442
- `);
313
+ circles.push(el('g', { class: 'stagger', style: `animation-delay: ${delay}ms` }, el('circle', {
314
+ cx: 150,
315
+ cy: 100,
316
+ r: radius,
317
+ fill: 'transparent',
318
+ stroke: langColor,
319
+ 'stroke-width': 25,
320
+ 'stroke-dasharray': totalCircleLength,
321
+ 'stroke-dashoffset': indent,
322
+ size: percentage,
323
+ 'data-testid': 'lang-donut',
324
+ })));
443
325
  // Update the indent for the next part
444
326
  indent += circleLength;
445
327
  // Update the start delay coefficient for the next part
446
328
  startDelayCoefficient += 1;
447
329
  }
448
- return `
449
- <svg data-testid="lang-items">
450
- <g transform="translate(0, 0)">
451
- <svg data-testid="donut">
452
- ${circles.join('')}
453
- </svg>
454
- </g>
455
- <g transform="translate(0, 220)">
456
- <svg data-testid="lang-names" x="${CARD_PADDING}">
457
- ${createLanguageTextNode({
330
+ return el('svg', { 'data-testid': 'lang-items' }, el('svg', { 'data-testid': 'donut' }, circles), el('g', { transform: 'translate(0, 220)' }, el('svg', { 'data-testid': 'lang-names', x: CARD_PADDING }, createLanguageTextNode({
458
331
  langs,
459
332
  totalSize: totalLanguageSize,
460
333
  hideProgress: false,
461
334
  statsFormat,
462
335
  hideValues,
463
- })}
464
- </svg>
465
- </g>
466
- </svg>
467
- `;
336
+ }))));
468
337
  };
469
338
  /**
470
339
  * Renders pie layout to display user's most frequently used programming languages.
471
340
  *
472
- * @param langs Array of programming languages.
473
- * @param totalLanguageSize Total size of all languages.
474
- * @param statsFormat Stats format.
475
- * @param hideValues Whether to hide stats values.
476
341
  * @returns Compact layout card SVG object.
477
342
  */
478
343
  const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
@@ -490,17 +355,15 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
490
355
  for (const lang of langs) {
491
356
  const langColor = resolveLangColor(lang);
492
357
  if (langs.length === 1) {
493
- paths.push(`
494
- <circle
495
- cx="${centerX}"
496
- cy="${centerY}"
497
- r="${radius}"
498
- stroke="none"
499
- fill="${langColor}"
500
- data-testid="lang-pie"
501
- size="100"
502
- />
503
- `);
358
+ paths.push(el('circle', {
359
+ cx: centerX,
360
+ cy: centerY,
361
+ r: radius,
362
+ stroke: 'none',
363
+ fill: langColor,
364
+ 'data-testid': 'lang-pie',
365
+ size: 100,
366
+ }));
504
367
  break;
505
368
  }
506
369
  const langSizePart = lang.size / totalLanguageSize;
@@ -517,49 +380,28 @@ const renderPieLayout = (langs, totalLanguageSize, statsFormat, hideValues) => {
517
380
  // Calculate delay
518
381
  const delay = startDelayCoefficient * 100;
519
382
  // SVG arc markup
520
- paths.push(`
521
- <g class="stagger" style="animation-delay: ${delay}ms">
522
- <path
523
- data-testid="lang-pie"
524
- size="${percentage}"
525
- d="M ${centerX} ${centerY} L ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endPoint.x} ${endPoint.y} Z"
526
- fill="${langColor}"
527
- />
528
- </g>
529
- `);
383
+ paths.push(el('g', { class: 'stagger', style: `animation-delay: ${delay}ms` }, el('path', {
384
+ 'data-testid': 'lang-pie',
385
+ size: percentage,
386
+ d: `M ${centerX} ${centerY} L ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endPoint.x} ${endPoint.y} Z`,
387
+ fill: langColor,
388
+ })));
530
389
  // Update the start angle for the next part
531
390
  startAngle = endAngle;
532
391
  // Update the start delay coefficient for the next part
533
392
  startDelayCoefficient += 1;
534
393
  }
535
- return `
536
- <svg data-testid="lang-items">
537
- <g transform="translate(0, 0)">
538
- <svg data-testid="pie">
539
- ${paths.join('')}
540
- </svg>
541
- </g>
542
- <g transform="translate(0, 220)">
543
- <svg data-testid="lang-names" x="${CARD_PADDING}">
544
- ${createLanguageTextNode({
394
+ return el('svg', { 'data-testid': 'lang-items' }, el('svg', { 'data-testid': 'pie' }, paths), el('g', { transform: 'translate(0, 220)' }, el('svg', { 'data-testid': 'lang-names', x: CARD_PADDING }, createLanguageTextNode({
545
395
  langs,
546
396
  totalSize: totalLanguageSize,
547
397
  hideProgress: false,
548
398
  statsFormat,
549
399
  hideValues,
550
- })}
551
- </svg>
552
- </g>
553
- </svg>
554
- `;
400
+ }))));
555
401
  };
556
402
  /**
557
403
  * Creates the SVG paths for the language donut chart.
558
404
  *
559
- * @param cx Donut center x-position.
560
- * @param cy Donut center y-position.
561
- * @param radius Donut arc Radius.
562
- * @param percentages Array with donut section percentages.
563
405
  * @returns Array of svg path elements
564
406
  */
565
407
  const createDonutPaths = (cx, cy, radius, percentages) => {
@@ -567,7 +409,7 @@ const createDonutPaths = (cx, cy, radius, percentages) => {
567
409
  let startAngle = 0;
568
410
  const totalPercent = percentages.reduce((acc, curr) => acc + curr, 0);
569
411
  for (const rawPercent of percentages) {
570
- const percent = parseFloat(((rawPercent / totalPercent) * 100).toFixed(2));
412
+ const percent = Number.parseFloat(((rawPercent / totalPercent) * 100).toFixed(2));
571
413
  const endAngle = 3.6 * percent + startAngle;
572
414
  const startPoint = polarToCartesian(cx, cy, radius, endAngle - 90); // rotate donut 90 degrees counter-clockwise.
573
415
  const endPoint = polarToCartesian(cx, cy, radius, startAngle - 90); // rotate donut 90 degrees counter-clockwise.
@@ -583,101 +425,78 @@ const createDonutPaths = (cx, cy, radius, percentages) => {
583
425
  /**
584
426
  * Renders the donut language card layout.
585
427
  *
586
- * @param langs Array of programming languages.
587
- * @param width Card width.
588
- * @param totalLanguageSize Total size of all languages.
589
- * @param statsFormat Stats format.
590
- * @param hideValues Whether to hide stats values.
591
428
  * @returns Donut layout card SVG object.
592
429
  */
593
430
  const renderDonutLayout = (langs, width, totalLanguageSize, statsFormat, hideValues) => {
594
431
  if (!Number.isFinite(width)) {
595
- throw new Error(`Invalid width: "${width}"`);
432
+ throw new TypeError(`Invalid width: "${width}"`);
596
433
  }
597
434
  const centerX = width / 3;
598
435
  const centerY = width / 3;
599
436
  const radius = centerX - 60;
600
437
  const strokeWidth = 12;
601
- const colors = langs.map(resolveLangColor);
602
- const langsPercents = langs.map((lang) => parseFloat(((lang.size / totalLanguageSize) * 100).toFixed(2)));
438
+ const colors = langs.map((lang) => resolveLangColor(lang));
439
+ const langsPercents = langs.map((lang) => Number.parseFloat(((lang.size / totalLanguageSize) * 100).toFixed(2)));
603
440
  const langPaths = createDonutPaths(centerX, centerY, radius, langsPercents);
604
441
  const donutPaths = langs.length === 1
605
- ? `<circle cx="${centerX}" cy="${centerY}" r="${radius}" stroke="${colors[0] ?? DEFAULT_LANG_COLOR}" fill="none" stroke-width="${strokeWidth}" data-testid="lang-donut" size="100"/>`
606
- : langPaths
607
- .map((section, index) => {
608
- const staggerDelay = (index + 3) * 100;
609
- const delay = staggerDelay + 300;
610
- const output = `
611
- <g class="stagger" style="animation-delay: ${delay}ms">
612
- <path
613
- data-testid="lang-donut"
614
- size="${section.percent}"
615
- d="${section.d}"
616
- stroke="${colors[index] ?? DEFAULT_LANG_COLOR}"
617
- fill="none"
618
- stroke-width="${strokeWidth}">
619
- </path>
620
- </g>
621
- `;
622
- return output;
623
- })
624
- .join('');
625
- const donut = `<svg width="${width}" height="${width}">${donutPaths}</svg>`;
626
- return `
627
- <g transform="translate(0, 0)">
628
- <g transform="translate(0, 0)">
629
- ${createDonutLanguagesNode({ langs, totalSize: totalLanguageSize, hideValues, statsFormat })}
630
- </g>
631
-
632
- <g transform="translate(125, ${donutCenterTranslation(langs.length)})">
633
- ${donut}
634
- </g>
635
- </g>
636
- `;
442
+ ? [
443
+ el('circle', {
444
+ cx: centerX,
445
+ cy: centerY,
446
+ r: radius,
447
+ stroke: colors[0] ?? DEFAULT_LANG_COLOR,
448
+ fill: 'none',
449
+ 'stroke-width': strokeWidth,
450
+ 'data-testid': 'lang-donut',
451
+ size: 100,
452
+ }),
453
+ ]
454
+ : langPaths.map((section, index) => el('g', { class: 'stagger', style: `animation-delay: ${(index + 3) * 100 + 300}ms` }, el('path', {
455
+ 'data-testid': 'lang-donut',
456
+ size: section.percent,
457
+ d: section.d,
458
+ stroke: colors[index] ?? DEFAULT_LANG_COLOR,
459
+ fill: 'none',
460
+ 'stroke-width': strokeWidth,
461
+ })));
462
+ return [
463
+ createDonutLanguagesNode({ langs, totalSize: totalLanguageSize, hideValues, statsFormat }),
464
+ el('g', { transform: `translate(125, ${donutCenterTranslation(langs.length)})` }, el('svg', { width, height: width }, donutPaths)),
465
+ ];
637
466
  };
638
467
  /**
639
468
  * Creates the no languages data SVG node.
640
469
  *
641
- * @param props Object with function properties.
642
- * @param props.text No languages data translated text.
643
- * @param props.layout Card layout.
644
470
  * @returns No languages data SVG node string.
645
471
  */
646
- const noLanguagesDataNode = ({ text, layout, }) => {
647
- return `
648
- <text x="${layout === 'pie' || layout === 'donut-vertical' ? CARD_PADDING : 0}" y="11" class="stat bold">${encodeHTML(text)}</text>
649
- `;
650
- };
472
+ const noLanguagesDataNode = ({ text, layout, }) => el('text', {
473
+ x: layout === 'pie' || layout === 'donut-vertical' ? CARD_PADDING : 0,
474
+ y: 11,
475
+ class: 'stat bold',
476
+ }, text);
651
477
  /**
652
478
  * Get default languages count for provided card layout.
653
479
  *
654
- * @param props Function properties.
655
- * @param props.layout Input layout string.
656
- * @param props.hide_progress Input hide_progress parameter value.
657
480
  * @returns Default languages count for input layout.
658
481
  */
659
482
  const getDefaultLanguagesCountByLayout = ({ layout, hide_progress, }) => {
660
483
  if (layout === 'compact' || hide_progress === true) {
661
484
  return COMPACT_LAYOUT_DEFAULT_LANGS_COUNT;
662
485
  }
663
- else if (layout === 'donut') {
486
+ if (layout === 'donut') {
664
487
  return DONUT_LAYOUT_DEFAULT_LANGS_COUNT;
665
488
  }
666
- else if (layout === 'donut-vertical') {
489
+ if (layout === 'donut-vertical') {
667
490
  return DONUT_VERTICAL_LAYOUT_DEFAULT_LANGS_COUNT;
668
491
  }
669
- else if (layout === 'pie') {
492
+ if (layout === 'pie') {
670
493
  return PIE_LAYOUT_DEFAULT_LANGS_COUNT;
671
494
  }
672
- else {
673
- return NORMAL_LAYOUT_DEFAULT_LANGS_COUNT;
674
- }
495
+ return NORMAL_LAYOUT_DEFAULT_LANGS_COUNT;
675
496
  };
676
497
  /**
677
498
  * Renders card that display user's most frequently used programming languages.
678
499
  *
679
- * @param topLangs User's most frequently used programming languages.
680
- * @param options Card options.
681
500
  * @returns Language card SVG object.
682
501
  */
683
502
  const renderTopLanguages = (topLangs, options = {}) => {
@@ -688,7 +507,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
688
507
  });
689
508
  const { langs, totalLanguageSize } = trimTopLanguages(topLangs, langs_count, hide);
690
509
  let width = card_width
691
- ? isNaN(card_width)
510
+ ? Number.isNaN(card_width)
692
511
  ? DEFAULT_CARD_WIDTH
693
512
  : card_width < MIN_CARD_WIDTH
694
513
  ? MIN_CARD_WIDTH
@@ -718,7 +537,7 @@ const renderTopLanguages = (topLangs, options = {}) => {
718
537
  }
719
538
  else if (layout === 'donut') {
720
539
  height = calculateDonutLayoutHeight(langs.length);
721
- width = width + 50; // padding
540
+ width += 50; // padding
722
541
  finalLayout = renderDonutLayout(langs, width, totalLanguageSize, stats_format, hide_values);
723
542
  }
724
543
  else {
@@ -738,52 +557,29 @@ const renderTopLanguages = (topLangs, options = {}) => {
738
557
  card.setHideBorder(hide_border);
739
558
  card.setHideTitle(hide_title);
740
559
  card.setCSS({
741
- light: ({ textColor, progBarBgColor }) => `
742
- @keyframes slideInAnimation {
743
- from {
744
- width: 0;
745
- }
746
- to {
747
- width: calc(100%-100px);
748
- }
749
- }
750
- @keyframes growWidthAnimation {
751
- from {
752
- width: 0;
753
- }
754
- to {
755
- width: 100%;
756
- }
757
- }
758
- .stat {
759
- font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif; fill: ${textColor};
760
- }
761
- @supports(-moz-appearance: auto) {
762
- /* Selector detects Firefox */
763
- .stat { font-size:12px; }
764
- }
765
- .bold { font-weight: 700 }
766
- .lang-name {
767
- font: 400 11px "Segoe UI", Ubuntu, Sans-Serif;
768
- fill: ${textColor};
769
- }
770
- .stagger {
771
- opacity: 0;
772
- animation: fadeInAnimation 0.3s ease-in-out forwards;
773
- }
774
- #rect-mask rect{
775
- animation: slideInAnimation 1s ease-in-out forwards;
776
- }
777
- .lang-progress{
778
- animation: growWidthAnimation 0.6s ease-in-out forwards;
779
- }
780
- .progress-background { fill: ${progBarBgColor}; }
781
- `,
782
- dark: ({ textColor, progBarBgColor }) => `
783
- .stat { fill: ${textColor}; }
784
- .lang-name { fill: ${textColor}; }
785
- .progress-background { fill: ${progBarBgColor}; }
786
- `,
560
+ light: ({ textColor, progBarBgColor }) => [
561
+ atRule('@keyframes slideInAnimation', rule('from', { width: 0 }),
562
+ // Invalid on purpose — `calc` needs spaces around its `-`. Browsers drop the
563
+ // declaration and animate to the rect's own width, which is the reveal the mask wants.
564
+ rule('to', { width: 'calc(100%-100px)' })),
565
+ atRule('@keyframes growWidthAnimation', rule('from', { width: 0 }), rule('to', { width: '100%' })),
566
+ rule('.stat', {
567
+ font: `600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif`,
568
+ fill: textColor,
569
+ }),
570
+ atRule('@supports(-moz-appearance: auto)', cssComment('Selector detects Firefox'), rule('.stat', { 'font-size': '12px' })),
571
+ rule('.bold', { 'font-weight': 700 }),
572
+ rule('.lang-name', { font: '400 11px "Segoe UI", Ubuntu, Sans-Serif', fill: textColor }),
573
+ rule('.stagger', { opacity: 0, animation: 'fadeInAnimation 0.3s ease-in-out forwards' }),
574
+ rule('#rect-mask rect', { animation: 'slideInAnimation 1s ease-in-out forwards' }),
575
+ rule('.lang-progress', { animation: 'growWidthAnimation 0.6s ease-in-out forwards' }),
576
+ rule('.progress-background', { fill: progBarBgColor }),
577
+ ],
578
+ dark: ({ textColor, progBarBgColor }) => [
579
+ rule('.stat', { fill: textColor }),
580
+ rule('.lang-name', { fill: textColor }),
581
+ rule('.progress-background', { fill: progBarBgColor }),
582
+ ],
787
583
  });
788
584
  // `role="img"` hides the inner text from assistive tech, so everything the card
789
585
  // shows has to be repeated here.
@@ -796,10 +592,6 @@ const renderTopLanguages = (topLangs, options = {}) => {
796
592
  if (layout === 'pie' || layout === 'donut-vertical') {
797
593
  return card.render(finalLayout);
798
594
  }
799
- return card.render(`
800
- <svg data-testid="lang-items" x="${CARD_PADDING}">
801
- ${finalLayout}
802
- </svg>
803
- `);
595
+ return card.render(el('svg', { 'data-testid': 'lang-items', x: CARD_PADDING }, finalLayout));
804
596
  };
805
597
  export { getLongestLang, degreesToRadians, radiansToDegrees, polarToCartesian, cartesianToPolar, getCircleLength, calculateCompactLayoutHeight, calculateNormalLayoutHeight, calculateDonutLayoutHeight, calculateDonutVerticalLayoutHeight, calculatePieLayoutHeight, donutCenterTranslation, trimTopLanguages, renderTopLanguages, MIN_CARD_WIDTH, getDefaultLanguagesCountByLayout, TOP_LANG_LAYOUTS, TOP_LANG_STATS_FORMATS, };