@stats-forge/github-stats-forge-core 0.0.1 → 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.
- package/README.md +0 -10
- package/build/api/api-result.d.ts +6 -7
- package/build/api/api-result.d.ts.map +1 -1
- package/build/api/api-result.js +4 -5
- package/build/api/gist.d.ts +3 -10
- package/build/api/gist.d.ts.map +1 -1
- package/build/api/gist.js +4 -11
- package/build/api/index.d.ts +12 -12
- package/build/api/params.d.ts +11 -8
- package/build/api/params.d.ts.map +1 -1
- package/build/api/params.js +16 -14
- package/build/api/pin.d.ts +3 -19
- package/build/api/pin.d.ts.map +1 -1
- package/build/api/pin.js +6 -22
- package/build/api/stats.d.ts +10 -4
- package/build/api/stats.d.ts.map +1 -1
- package/build/api/stats.js +6 -32
- package/build/api/top-langs.d.ts +12 -6
- package/build/api/top-langs.d.ts.map +1 -1
- package/build/api/top-langs.js +6 -26
- package/build/api/wakatime.d.ts +13 -5
- package/build/api/wakatime.d.ts.map +1 -1
- package/build/api/wakatime.js +4 -21
- package/build/calculateRank.d.ts +0 -9
- package/build/calculateRank.d.ts.map +1 -1
- package/build/calculateRank.js +12 -17
- package/build/cards/gist.d.ts +2 -4
- package/build/cards/gist.d.ts.map +1 -1
- package/build/cards/gist.js +27 -36
- package/build/cards/options.d.ts +1 -1
- package/build/cards/repo.d.ts +2 -4
- package/build/cards/repo.d.ts.map +1 -1
- package/build/cards/repo.js +61 -93
- package/build/cards/stats.d.ts +3 -8
- package/build/cards/stats.d.ts.map +1 -1
- package/build/cards/stats.js +119 -165
- package/build/cards/top-languages.d.ts +5 -31
- package/build/cards/top-languages.d.ts.map +1 -1
- package/build/cards/top-languages.js +176 -384
- package/build/cards/wakatime.d.ts +4 -6
- package/build/cards/wakatime.d.ts.map +1 -1
- package/build/cards/wakatime.js +118 -185
- package/build/common/Card.d.ts +14 -62
- package/build/common/Card.d.ts.map +1 -1
- package/build/common/Card.js +97 -199
- package/build/common/I18n.d.ts +0 -6
- package/build/common/I18n.d.ts.map +1 -1
- package/build/common/I18n.js +3 -6
- package/build/common/color.d.ts +4 -17
- package/build/common/color.d.ts.map +1 -1
- package/build/common/color.js +67 -49
- package/build/common/config.d.ts +4 -14
- package/build/common/config.d.ts.map +1 -1
- package/build/common/config.js +3 -16
- package/build/common/constants.d.ts +4 -2
- package/build/common/constants.d.ts.map +1 -1
- package/build/common/constants.js +3 -1
- package/build/common/date.d.ts +0 -2
- package/build/common/date.d.ts.map +1 -1
- package/build/common/date.js +0 -2
- package/build/common/emojiMap.d.ts +8 -0
- package/build/common/emojiMap.d.ts.map +1 -0
- package/build/common/emojiMap.js +10 -0
- package/build/common/emojiMap.json +1915 -0
- package/build/common/error.d.ts +1 -10
- package/build/common/error.d.ts.map +1 -1
- package/build/common/error.js +1 -10
- package/build/common/fmt.d.ts +3 -15
- package/build/common/fmt.d.ts.map +1 -1
- package/build/common/fmt.js +7 -35
- package/build/common/http.d.ts +2 -7
- package/build/common/http.d.ts.map +1 -1
- package/build/common/http.js +8 -17
- package/build/common/icons.d.ts +18 -20
- package/build/common/icons.d.ts.map +1 -1
- package/build/common/icons.js +103 -67
- package/build/common/languageColors.d.ts +0 -1
- package/build/common/languageColors.d.ts.map +1 -1
- package/build/common/languageColors.js +1 -4
- package/build/common/languageColors.json +18 -2
- package/build/common/ops.d.ts +3 -14
- package/build/common/ops.d.ts.map +1 -1
- package/build/common/ops.js +19 -30
- package/build/common/render.d.ts +28 -82
- package/build/common/render.d.ts.map +1 -1
- package/build/common/render.js +175 -211
- package/build/common/retryer.d.ts +2 -7
- package/build/common/retryer.d.ts.map +1 -1
- package/build/common/retryer.js +4 -11
- package/build/fetchers/gist.d.ts +2 -5
- package/build/fetchers/gist.d.ts.map +1 -1
- package/build/fetchers/gist.js +1 -5
- package/build/fetchers/index.d.ts +12 -12
- package/build/fetchers/repo.d.ts +2 -11
- package/build/fetchers/repo.d.ts.map +1 -1
- package/build/fetchers/repo.js +20 -33
- package/build/fetchers/stats.d.ts +2 -32
- package/build/fetchers/stats.d.ts.map +1 -1
- package/build/fetchers/stats.js +27 -86
- package/build/fetchers/top-languages.d.ts +2 -9
- package/build/fetchers/top-languages.d.ts.map +1 -1
- package/build/fetchers/top-languages.js +15 -31
- package/build/fetchers/types.d.ts +1 -1
- package/build/fetchers/wakatime.d.ts +2 -6
- package/build/fetchers/wakatime.d.ts.map +1 -1
- package/build/fetchers/wakatime.js +1 -5
- package/build/graphql/contributionsDocument.d.ts +3 -3
- package/build/graphql/contributionsDocument.d.ts.map +1 -1
- package/build/graphql/contributionsDocument.js +0 -1
- package/build/graphql/generated/gist.d.ts +1 -1
- package/build/graphql/generated/repo.d.ts +1 -1
- package/build/graphql/generated/stats.d.ts +3 -3
- package/build/graphql/generated/top-languages.d.ts +2 -2
- package/build/graphql/graphqlDocument.d.ts +0 -1
- package/build/graphql/graphqlDocument.d.ts.map +1 -1
- package/build/graphql/graphqlDocument.js +0 -1
- package/build/graphql/reposContributedToDocument.d.ts +4 -5
- package/build/graphql/reposContributedToDocument.d.ts.map +1 -1
- package/build/graphql/reposContributedToDocument.js +0 -2
- package/build/markup/css.d.ts +35 -0
- package/build/markup/css.d.ts.map +1 -0
- package/build/markup/css.js +64 -0
- package/build/markup/element.d.ts +41 -0
- package/build/markup/element.d.ts.map +1 -0
- package/build/markup/element.js +127 -0
- package/build/markup/html.d.ts +13 -0
- package/build/markup/html.d.ts.map +1 -0
- package/build/markup/html.js +27 -0
- package/build/markup/index.d.ts +9 -0
- package/build/markup/index.d.ts.map +1 -0
- package/build/markup/index.js +6 -0
- package/build/markup/serialize.d.ts +16 -0
- package/build/markup/serialize.d.ts.map +1 -0
- package/build/markup/serialize.js +17 -0
- package/build/themes/index.d.ts +332 -333
- package/build/themes/index.d.ts.map +1 -1
- package/build/themes/index.js +1 -1
- package/build/translations.d.ts +2 -6
- package/build/translations.d.ts.map +1 -1
- package/build/translations.js +621 -628
- package/package.json +17 -26
- package/src/api/api-result.ts +10 -9
- package/src/api/gist.ts +11 -18
- package/src/api/index.ts +12 -12
- package/src/api/params.ts +27 -21
- package/src/api/pin.ts +13 -28
- package/src/api/stats.ts +13 -39
- package/src/api/top-langs.ts +13 -33
- package/src/api/wakatime.ts +11 -28
- package/src/calculateRank.ts +12 -23
- package/src/cards/gist.ts +40 -43
- package/src/cards/options.ts +1 -1
- package/src/cards/repo.ts +97 -107
- package/src/cards/stats.ts +165 -184
- package/src/cards/top-languages.ts +297 -413
- package/src/cards/wakatime.ts +193 -221
- package/src/common/Card.ts +165 -217
- package/src/common/I18n.ts +3 -6
- package/src/common/color.ts +80 -57
- package/src/common/config.ts +5 -18
- package/src/common/constants.ts +5 -2
- package/src/common/date.ts +0 -2
- package/src/common/emojiMap.json +1915 -0
- package/src/common/emojiMap.ts +13 -0
- package/src/common/error.ts +2 -11
- package/src/common/fmt.ts +7 -44
- package/src/common/http.ts +9 -15
- package/src/common/icons.ts +117 -42
- package/src/common/languageColors.json +18 -2
- package/src/common/languageColors.ts +1 -4
- package/src/common/ops.ts +26 -34
- package/src/common/render.ts +279 -232
- package/src/common/retryer.ts +8 -15
- package/src/fetchers/gist.ts +8 -12
- package/src/fetchers/index.ts +12 -12
- package/src/fetchers/repo.ts +27 -40
- package/src/fetchers/stats.ts +44 -102
- package/src/fetchers/top-languages.ts +26 -40
- package/src/fetchers/types.ts +1 -1
- package/src/fetchers/wakatime.ts +5 -9
- package/src/graphql/contributionsDocument.ts +7 -5
- package/src/graphql/generated/gist.ts +1 -1
- package/src/graphql/generated/repo.ts +1 -1
- package/src/graphql/generated/stats.ts +2 -2
- package/src/graphql/generated/top-languages.ts +2 -2
- package/src/graphql/graphqlDocument.ts +0 -1
- package/src/graphql/reposContributedToDocument.ts +6 -7
- package/src/markup/css.ts +118 -0
- package/src/markup/element.ts +246 -0
- package/src/markup/html.ts +34 -0
- package/src/markup/index.ts +9 -0
- package/src/markup/serialize.ts +27 -0
- package/src/themes/index.ts +1 -1
- package/src/translations.ts +622 -629
- package/build/cards/index.d.ts +0 -11
- package/build/cards/index.d.ts.map +0 -1
- package/build/cards/index.js +0 -7
- package/build/common/html.d.ts +0 -8
- package/build/common/html.d.ts.map +0 -1
- package/build/common/html.js +0 -14
- package/build/index.d.ts +0 -15
- package/build/index.d.ts.map +0 -1
- package/build/index.js +0 -10
- package/src/_emoji-name-map.d.ts +0 -10
- package/src/_github-username-regex.d.ts +0 -4
- package/src/cards/index.ts +0 -21
- package/src/common/html.ts +0 -17
- package/src/index.ts +0 -18
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { Card } from '../common/Card.
|
|
2
|
-
import { getLightDarkColors, isPrefixedHexColor } from '../common/color.
|
|
3
|
-
import { formatBytes } from '../common/fmt.
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import type {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
import { Card } from '../common/Card.ts';
|
|
2
|
+
import { getLightDarkColors, isPrefixedHexColor } from '../common/color.ts';
|
|
3
|
+
import { formatBytes } from '../common/fmt.ts';
|
|
4
|
+
import { I18n } from '../common/I18n.ts';
|
|
5
|
+
import { DEFAULT_LANG_COLOR } from '../common/languageColors.ts';
|
|
6
|
+
import { chunkArray, clampValue, lowercaseTrim } from '../common/ops.ts';
|
|
7
|
+
import { createProgressNode, flexLayout, measureText } from '../common/render.ts';
|
|
8
|
+
import type { Lang, TopLangData } from '../fetchers/types.ts';
|
|
9
|
+
import type { Child, MarkupElement } from '../markup/index.ts';
|
|
10
|
+
import { atRule, cssComment, el, rule } from '../markup/index.ts';
|
|
11
|
+
import { langCardLocales } from '../translations.ts';
|
|
12
|
+
|
|
13
|
+
import type { CardOptions, CommonCardOptions } from './options.ts';
|
|
13
14
|
|
|
14
15
|
const DEFAULT_CARD_WIDTH = 300;
|
|
15
16
|
const MIN_CARD_WIDTH = 280;
|
|
@@ -49,38 +50,35 @@ interface TopLangOptions extends CommonCardOptions {
|
|
|
49
50
|
/**
|
|
50
51
|
* Retrieves the programming language whose name is the longest.
|
|
51
52
|
*
|
|
52
|
-
* @param arr Array of programming languages.
|
|
53
53
|
* @returns Longest programming language object.
|
|
54
54
|
*/
|
|
55
|
-
const getLongestLang = (arr: Array<Lang>): Pick<Lang, 'name' | 'size' | 'color'> =>
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
const getLongestLang = (arr: Array<Lang>): Pick<Lang, 'name' | 'size' | 'color'> => {
|
|
56
|
+
let longest: Pick<Lang, 'name' | 'size' | 'color'> = { name: '', size: 0, color: '' };
|
|
57
|
+
for (const lang of arr) {
|
|
58
|
+
if (lang.name.length > longest.name.length) {
|
|
59
|
+
longest = lang;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return longest;
|
|
63
|
+
};
|
|
60
64
|
|
|
61
65
|
/**
|
|
62
66
|
* Convert degrees to radians.
|
|
63
67
|
*
|
|
64
|
-
* @param angleInDegrees Angle in degrees.
|
|
65
68
|
* @returns Angle in radians.
|
|
66
69
|
*/
|
|
67
|
-
const degreesToRadians = (angleInDegrees: number): number => angleInDegrees * (Math.PI / 180
|
|
70
|
+
const degreesToRadians = (angleInDegrees: number): number => angleInDegrees * (Math.PI / 180);
|
|
68
71
|
|
|
69
72
|
/**
|
|
70
73
|
* Convert radians to degrees.
|
|
71
74
|
*
|
|
72
|
-
* @param angleInRadians Angle in radians.
|
|
73
75
|
* @returns Angle in degrees.
|
|
74
76
|
*/
|
|
75
|
-
const radiansToDegrees = (angleInRadians: number): number => angleInRadians / (Math.PI / 180
|
|
77
|
+
const radiansToDegrees = (angleInRadians: number): number => angleInRadians / (Math.PI / 180);
|
|
76
78
|
|
|
77
79
|
/**
|
|
78
80
|
* Convert polar coordinates to cartesian coordinates.
|
|
79
81
|
*
|
|
80
|
-
* @param centerX Center x coordinate.
|
|
81
|
-
* @param centerY Center y coordinate.
|
|
82
|
-
* @param radius Radius of the circle.
|
|
83
|
-
* @param angleInDegrees Angle in degrees.
|
|
84
82
|
* @returns Cartesian coordinates.
|
|
85
83
|
*/
|
|
86
84
|
const polarToCartesian = (
|
|
@@ -99,10 +97,6 @@ const polarToCartesian = (
|
|
|
99
97
|
/**
|
|
100
98
|
* Convert cartesian coordinates to polar coordinates.
|
|
101
99
|
*
|
|
102
|
-
* @param centerX Center x coordinate.
|
|
103
|
-
* @param centerY Center y coordinate.
|
|
104
|
-
* @param x Point x coordinate.
|
|
105
|
-
* @param y Point y coordinate.
|
|
106
100
|
* @returns Polar coordinates.
|
|
107
101
|
*/
|
|
108
102
|
const cartesianToPolar = (
|
|
@@ -111,7 +105,7 @@ const cartesianToPolar = (
|
|
|
111
105
|
x: number,
|
|
112
106
|
y: number,
|
|
113
107
|
): { radius: number; angleInDegrees: number } => {
|
|
114
|
-
const radius = Math.
|
|
108
|
+
const radius = Math.hypot(x - centerX, y - centerY);
|
|
115
109
|
let angleInDegrees = radiansToDegrees(Math.atan2(y - centerY, x - centerX));
|
|
116
110
|
if (angleInDegrees < 0) {
|
|
117
111
|
angleInDegrees += 360;
|
|
@@ -122,79 +116,60 @@ const cartesianToPolar = (
|
|
|
122
116
|
/**
|
|
123
117
|
* Calculates length of circle.
|
|
124
118
|
*
|
|
125
|
-
* @param radius Radius of the circle.
|
|
126
119
|
* @returns The length of the circle.
|
|
127
120
|
*/
|
|
128
|
-
const getCircleLength = (radius: number): number =>
|
|
129
|
-
return 2 * Math.PI * radius;
|
|
130
|
-
};
|
|
121
|
+
const getCircleLength = (radius: number): number => 2 * Math.PI * radius;
|
|
131
122
|
|
|
132
123
|
/**
|
|
133
124
|
* Calculates height for the compact layout.
|
|
134
125
|
*
|
|
135
|
-
* @param totalLangs Total number of languages.
|
|
136
126
|
* @returns Card height.
|
|
137
127
|
*/
|
|
138
|
-
const calculateCompactLayoutHeight = (totalLangs: number): number =>
|
|
139
|
-
|
|
140
|
-
};
|
|
128
|
+
const calculateCompactLayoutHeight = (totalLangs: number): number =>
|
|
129
|
+
COMPACT_LAYOUT_BASE_HEIGHT + Math.round(totalLangs / 2) * 25;
|
|
141
130
|
|
|
142
131
|
/**
|
|
143
132
|
* Calculates height for the normal layout.
|
|
144
133
|
*
|
|
145
|
-
* @param totalLangs Total number of languages.
|
|
146
134
|
* @returns Card height.
|
|
147
135
|
*/
|
|
148
|
-
const calculateNormalLayoutHeight = (totalLangs: number): number =>
|
|
149
|
-
return 45 + (totalLangs + 1) * 40;
|
|
150
|
-
};
|
|
136
|
+
const calculateNormalLayoutHeight = (totalLangs: number): number => 45 + (totalLangs + 1) * 40;
|
|
151
137
|
|
|
152
138
|
/**
|
|
153
139
|
* Calculates height for the donut layout.
|
|
154
140
|
*
|
|
155
|
-
* @param totalLangs Total number of languages.
|
|
156
141
|
* @returns Card height.
|
|
157
142
|
*/
|
|
158
|
-
const calculateDonutLayoutHeight = (totalLangs: number): number =>
|
|
159
|
-
|
|
160
|
-
};
|
|
143
|
+
const calculateDonutLayoutHeight = (totalLangs: number): number =>
|
|
144
|
+
215 + Math.max(totalLangs - 5, 0) * 32;
|
|
161
145
|
|
|
162
146
|
/**
|
|
163
147
|
* Calculates height for the donut vertical layout.
|
|
164
148
|
*
|
|
165
|
-
* @param totalLangs Total number of languages.
|
|
166
149
|
* @returns Card height.
|
|
167
150
|
*/
|
|
168
|
-
const calculateDonutVerticalLayoutHeight = (totalLangs: number): number =>
|
|
169
|
-
|
|
170
|
-
};
|
|
151
|
+
const calculateDonutVerticalLayoutHeight = (totalLangs: number): number =>
|
|
152
|
+
300 + Math.round(totalLangs / 2) * 25;
|
|
171
153
|
|
|
172
154
|
/**
|
|
173
155
|
* Calculates height for the pie layout.
|
|
174
156
|
*
|
|
175
|
-
* @param totalLangs Total number of languages.
|
|
176
157
|
* @returns Card height.
|
|
177
158
|
*/
|
|
178
|
-
const calculatePieLayoutHeight = (totalLangs: number): number =>
|
|
179
|
-
|
|
180
|
-
};
|
|
159
|
+
const calculatePieLayoutHeight = (totalLangs: number): number =>
|
|
160
|
+
300 + Math.round(totalLangs / 2) * 25;
|
|
181
161
|
|
|
182
162
|
/**
|
|
183
163
|
* Calculates the center translation needed to keep the donut chart centred.
|
|
184
164
|
*
|
|
185
|
-
* @param totalLangs Total number of languages.
|
|
186
165
|
* @returns Donut center translation.
|
|
187
166
|
*/
|
|
188
|
-
const donutCenterTranslation = (totalLangs: number): number =>
|
|
189
|
-
|
|
190
|
-
};
|
|
167
|
+
const donutCenterTranslation = (totalLangs: number): number =>
|
|
168
|
+
-45 + Math.max(totalLangs - 5, 0) * 16;
|
|
191
169
|
|
|
192
170
|
/**
|
|
193
171
|
* Trim top languages to lang_count while also hiding certain languages.
|
|
194
172
|
*
|
|
195
|
-
* @param topLangs Top languages.
|
|
196
|
-
* @param langs_count Number of languages to show.
|
|
197
|
-
* @param hide Languages to hide.
|
|
198
173
|
* @returns Trimmed top languages and total size.
|
|
199
174
|
*/
|
|
200
175
|
const trimTopLanguages = (
|
|
@@ -208,14 +183,14 @@ const trimTopLanguages = (
|
|
|
208
183
|
|
|
209
184
|
// populate langsToHide map for quick lookup while filtering out
|
|
210
185
|
if (hide) {
|
|
211
|
-
|
|
186
|
+
for (const langName of hide) {
|
|
212
187
|
langsToHide[lowercaseTrim(langName)] = true;
|
|
213
|
-
}
|
|
188
|
+
}
|
|
214
189
|
}
|
|
215
190
|
|
|
216
191
|
// filter out languages to be hidden
|
|
217
192
|
langs = langs
|
|
218
|
-
.
|
|
193
|
+
.toSorted((a, b) => b.size - a.size)
|
|
219
194
|
.filter((lang) => !langsToHide[lowercaseTrim(lang.name)])
|
|
220
195
|
.slice(0, langsCount);
|
|
221
196
|
|
|
@@ -227,20 +202,15 @@ const trimTopLanguages = (
|
|
|
227
202
|
/**
|
|
228
203
|
* Get display value corresponding to the format.
|
|
229
204
|
*
|
|
230
|
-
* @param size Bytes size.
|
|
231
|
-
* @param percentages Percentage value.
|
|
232
|
-
* @param format Format of the stats.
|
|
233
205
|
* @returns Display value.
|
|
234
206
|
*/
|
|
235
|
-
const getDisplayValue = (size: number, percentages: number, format: string): string =>
|
|
236
|
-
|
|
237
|
-
};
|
|
207
|
+
const getDisplayValue = (size: number, percentages: number, format: string): string =>
|
|
208
|
+
format === 'bytes' ? formatBytes(size) : `${percentages.toFixed(2)}%`;
|
|
238
209
|
|
|
239
210
|
/**
|
|
240
211
|
* Resolves a language's display color, falling back to the default, and
|
|
241
212
|
* validates it is a prefixed hex color.
|
|
242
213
|
*
|
|
243
|
-
* @param lang Programming language object.
|
|
244
214
|
* @returns Validated language color.
|
|
245
215
|
*/
|
|
246
216
|
const resolveLangColor = (lang: Lang): string => {
|
|
@@ -254,15 +224,6 @@ const resolveLangColor = (lang: Lang): string => {
|
|
|
254
224
|
/**
|
|
255
225
|
* Create progress bar text item for a programming language.
|
|
256
226
|
*
|
|
257
|
-
* @param props Function properties.
|
|
258
|
-
* @param props.width The card width
|
|
259
|
-
* @param props.color Color of the programming language.
|
|
260
|
-
* @param props.name Name of the programming language.
|
|
261
|
-
* @param props.size Size of the programming language.
|
|
262
|
-
* @param props.totalSize Total size of all languages.
|
|
263
|
-
* @param props.statsFormat Stats format.
|
|
264
|
-
* @param props.hideValues Whether to hide stats values.
|
|
265
|
-
* @param props.index Index of the programming language.
|
|
266
227
|
* @returns Programming language SVG node.
|
|
267
228
|
*/
|
|
268
229
|
const createProgressTextNode = ({
|
|
@@ -283,7 +244,7 @@ const createProgressTextNode = ({
|
|
|
283
244
|
statsFormat: string;
|
|
284
245
|
hideValues?: boolean | undefined;
|
|
285
246
|
index: number;
|
|
286
|
-
}):
|
|
247
|
+
}): MarkupElement => {
|
|
287
248
|
const staggerDelay = (index + 3) * 150;
|
|
288
249
|
const paddingRight = hideValues ? CARD_PADDING * 2 : 95;
|
|
289
250
|
const progressTextX = width - paddingRight + 10;
|
|
@@ -292,32 +253,25 @@ const createProgressTextNode = ({
|
|
|
292
253
|
const progress = (size / totalSize) * 100;
|
|
293
254
|
const displayValue = getDisplayValue(size, progress, statsFormat);
|
|
294
255
|
|
|
295
|
-
return
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
256
|
+
return el(
|
|
257
|
+
'g',
|
|
258
|
+
{ class: 'stagger', style: `animation-delay: ${staggerDelay}ms` },
|
|
259
|
+
el('text', { 'data-testid': 'lang-name', x: 2, y: 15, class: 'lang-name' }, name),
|
|
260
|
+
!hideValues && el('text', { x: progressTextX, y: 34, class: 'lang-name' }, displayValue),
|
|
261
|
+
createProgressNode({
|
|
262
|
+
x: 0,
|
|
263
|
+
y: 25,
|
|
264
|
+
color,
|
|
265
|
+
width: progressWidth,
|
|
266
|
+
progress,
|
|
267
|
+
delay: staggerDelay + 300,
|
|
268
|
+
}),
|
|
269
|
+
);
|
|
309
270
|
};
|
|
310
271
|
|
|
311
272
|
/**
|
|
312
273
|
* Creates compact text item for a programming language.
|
|
313
274
|
*
|
|
314
|
-
* @param props Function properties.
|
|
315
|
-
* @param props.lang Programming language object.
|
|
316
|
-
* @param props.totalSize Total size of all languages.
|
|
317
|
-
* @param props.hideProgress Whether to hide percentage.
|
|
318
|
-
* @param props.hideValues Whether to hide stats values (percentages/bytes).
|
|
319
|
-
* @param props.statsFormat Stats format
|
|
320
|
-
* @param props.index Index of the programming language.
|
|
321
275
|
* @returns Compact layout programming language SVG node.
|
|
322
276
|
*/
|
|
323
277
|
const createCompactLangNode = ({
|
|
@@ -334,32 +288,26 @@ const createCompactLangNode = ({
|
|
|
334
288
|
hideValues?: boolean | undefined;
|
|
335
289
|
statsFormat?: string | undefined;
|
|
336
290
|
index: number;
|
|
337
|
-
}):
|
|
291
|
+
}): MarkupElement => {
|
|
338
292
|
const percentages = (lang.size / totalSize) * 100;
|
|
339
293
|
const displayValue = getDisplayValue(lang.size, percentages, statsFormat);
|
|
340
294
|
|
|
341
295
|
const staggerDelay = (index + 3) * 150;
|
|
342
296
|
const color = resolveLangColor(lang);
|
|
343
297
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
298
|
+
const label = hideProgress || hideValues ? lang.name : `${lang.name} ${displayValue}`;
|
|
299
|
+
|
|
300
|
+
return el(
|
|
301
|
+
'g',
|
|
302
|
+
{ class: 'stagger', style: `animation-delay: ${staggerDelay}ms` },
|
|
303
|
+
el('circle', { cx: 5, cy: 6, r: 5, fill: color }),
|
|
304
|
+
el('text', { 'data-testid': 'lang-name', x: 15, y: 10, class: 'lang-name' }, label),
|
|
305
|
+
);
|
|
352
306
|
};
|
|
353
307
|
|
|
354
308
|
/**
|
|
355
309
|
* Create compact languages text items for all programming languages.
|
|
356
310
|
*
|
|
357
|
-
* @param props Function properties.
|
|
358
|
-
* @param props.langs Array of programming languages.
|
|
359
|
-
* @param props.totalSize Total size of all languages.
|
|
360
|
-
* @param props.hideProgress Whether to hide percentage.
|
|
361
|
-
* @param props.hideValues Whether to hide stats values.
|
|
362
|
-
* @param props.statsFormat Stats format
|
|
363
311
|
* @returns Programming languages SVG node.
|
|
364
312
|
*/
|
|
365
313
|
const createLanguageTextNode = ({
|
|
@@ -374,7 +322,7 @@ const createLanguageTextNode = ({
|
|
|
374
322
|
hideProgress?: boolean | undefined;
|
|
375
323
|
hideValues?: boolean | undefined;
|
|
376
324
|
statsFormat?: string | undefined;
|
|
377
|
-
}):
|
|
325
|
+
}): Array<Child> => {
|
|
378
326
|
const longestLang = getLongestLang(langs);
|
|
379
327
|
const chunked = chunkArray(langs, langs.length / 2);
|
|
380
328
|
const layouts = chunked.map((array) => {
|
|
@@ -388,30 +336,18 @@ const createLanguageTextNode = ({
|
|
|
388
336
|
index,
|
|
389
337
|
}),
|
|
390
338
|
);
|
|
391
|
-
return flexLayout({
|
|
392
|
-
items,
|
|
393
|
-
gap: 25,
|
|
394
|
-
direction: 'column',
|
|
395
|
-
}).join('');
|
|
339
|
+
return flexLayout({ items, gap: 25, direction: 'column' });
|
|
396
340
|
});
|
|
397
341
|
|
|
398
342
|
const percent = ((longestLang.size / totalSize) * 100).toFixed(2);
|
|
399
343
|
const minGap = 150;
|
|
400
344
|
const maxGap = 20 + measureText(`${longestLang.name} ${percent}%`, 11);
|
|
401
|
-
return flexLayout({
|
|
402
|
-
items: layouts,
|
|
403
|
-
gap: maxGap < minGap ? minGap : maxGap,
|
|
404
|
-
}).join('');
|
|
345
|
+
return flexLayout({ items: layouts, gap: maxGap < minGap ? minGap : maxGap });
|
|
405
346
|
};
|
|
406
347
|
|
|
407
348
|
/**
|
|
408
349
|
* Create donut languages text items for all programming languages.
|
|
409
350
|
*
|
|
410
|
-
* @param props Function properties.
|
|
411
|
-
* @param props.langs Array of programming languages.
|
|
412
|
-
* @param props.totalSize Total size of all languages.
|
|
413
|
-
* @param props.hideValues Whether to hide stats values.
|
|
414
|
-
* @param props.statsFormat Stats format
|
|
415
351
|
* @returns Donut layout programming language SVG node.
|
|
416
352
|
*/
|
|
417
353
|
const createDonutLanguagesNode = ({
|
|
@@ -424,31 +360,25 @@ const createDonutLanguagesNode = ({
|
|
|
424
360
|
totalSize: number;
|
|
425
361
|
hideValues?: boolean | undefined;
|
|
426
362
|
statsFormat?: string | undefined;
|
|
427
|
-
}):
|
|
428
|
-
|
|
429
|
-
items: langs.map((lang, index) =>
|
|
430
|
-
|
|
363
|
+
}): Array<Child> =>
|
|
364
|
+
flexLayout({
|
|
365
|
+
items: langs.map((lang, index) =>
|
|
366
|
+
createCompactLangNode({
|
|
431
367
|
lang,
|
|
432
368
|
totalSize,
|
|
433
369
|
hideProgress: false,
|
|
434
370
|
hideValues,
|
|
435
371
|
statsFormat,
|
|
436
372
|
index,
|
|
437
|
-
})
|
|
438
|
-
|
|
373
|
+
}),
|
|
374
|
+
),
|
|
439
375
|
gap: 32,
|
|
440
376
|
direction: 'column',
|
|
441
|
-
})
|
|
442
|
-
};
|
|
377
|
+
});
|
|
443
378
|
|
|
444
379
|
/**
|
|
445
380
|
* Renders the default language card layout.
|
|
446
381
|
*
|
|
447
|
-
* @param langs Array of programming languages.
|
|
448
|
-
* @param width Card width.
|
|
449
|
-
* @param totalLanguageSize Total size of all languages.
|
|
450
|
-
* @param statsFormat Stats format.
|
|
451
|
-
* @param hideValues Whether to hide stats values.
|
|
452
382
|
* @returns Normal layout card SVG object.
|
|
453
383
|
*/
|
|
454
384
|
const renderNormalLayout = (
|
|
@@ -457,10 +387,10 @@ const renderNormalLayout = (
|
|
|
457
387
|
totalLanguageSize: number,
|
|
458
388
|
statsFormat: string,
|
|
459
389
|
hideValues?: boolean,
|
|
460
|
-
):
|
|
461
|
-
|
|
462
|
-
items: langs.map((lang, index) =>
|
|
463
|
-
|
|
390
|
+
): Array<Child> =>
|
|
391
|
+
flexLayout({
|
|
392
|
+
items: langs.map((lang, index) =>
|
|
393
|
+
createProgressTextNode({
|
|
464
394
|
width,
|
|
465
395
|
name: lang.name,
|
|
466
396
|
color: lang.color || DEFAULT_LANG_COLOR,
|
|
@@ -469,22 +399,15 @@ const renderNormalLayout = (
|
|
|
469
399
|
statsFormat,
|
|
470
400
|
hideValues,
|
|
471
401
|
index,
|
|
472
|
-
})
|
|
473
|
-
|
|
402
|
+
}),
|
|
403
|
+
),
|
|
474
404
|
gap: 40,
|
|
475
405
|
direction: 'column',
|
|
476
|
-
})
|
|
477
|
-
};
|
|
406
|
+
});
|
|
478
407
|
|
|
479
408
|
/**
|
|
480
409
|
* Renders the compact language card layout.
|
|
481
410
|
*
|
|
482
|
-
* @param langs Array of programming languages.
|
|
483
|
-
* @param width Card width.
|
|
484
|
-
* @param totalLanguageSize Total size of all languages.
|
|
485
|
-
* @param hideProgress Whether to hide progress bar.
|
|
486
|
-
* @param statsFormat Stats format.
|
|
487
|
-
* @param hideValues Whether to hide stats values.
|
|
488
411
|
* @returns Compact layout card SVG object.
|
|
489
412
|
*/
|
|
490
413
|
const renderCompactLayout = (
|
|
@@ -494,66 +417,56 @@ const renderCompactLayout = (
|
|
|
494
417
|
hideProgress?: boolean,
|
|
495
418
|
statsFormat = 'percentages',
|
|
496
419
|
hideValues?: boolean,
|
|
497
|
-
):
|
|
420
|
+
): Array<Child> => {
|
|
498
421
|
const paddingRight = 50;
|
|
499
422
|
const offsetWidth = width - paddingRight;
|
|
500
423
|
// progressOffset holds the previous language's width and used to offset the next language
|
|
501
424
|
// so that we can stack them one after another, like this: [--][----][---]
|
|
502
425
|
let progressOffset = 0;
|
|
503
|
-
const compactProgressBar = langs
|
|
504
|
-
.
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
</mask>
|
|
535
|
-
${compactProgressBar}
|
|
536
|
-
`
|
|
537
|
-
}
|
|
538
|
-
<g transform="translate(0, ${hideProgress ? '0' : '25'})">
|
|
539
|
-
${createLanguageTextNode({
|
|
426
|
+
const compactProgressBar = langs.map((lang) => {
|
|
427
|
+
const percentage = Number.parseFloat(
|
|
428
|
+
((lang.size / totalLanguageSize) * offsetWidth).toFixed(2),
|
|
429
|
+
);
|
|
430
|
+
const x = progressOffset;
|
|
431
|
+
progressOffset += percentage;
|
|
432
|
+
|
|
433
|
+
return el('rect', {
|
|
434
|
+
mask: 'url(#rect-mask)',
|
|
435
|
+
'data-testid': 'lang-progress',
|
|
436
|
+
x,
|
|
437
|
+
y: 0,
|
|
438
|
+
width: percentage < 10 ? percentage + 10 : percentage,
|
|
439
|
+
height: 8,
|
|
440
|
+
fill: resolveLangColor(lang),
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
return [
|
|
445
|
+
!hideProgress && [
|
|
446
|
+
el(
|
|
447
|
+
'mask',
|
|
448
|
+
{ id: 'rect-mask' },
|
|
449
|
+
el('rect', { x: 0, y: 0, width: offsetWidth, height: 8, fill: 'white', rx: 5 }),
|
|
450
|
+
),
|
|
451
|
+
compactProgressBar,
|
|
452
|
+
],
|
|
453
|
+
el(
|
|
454
|
+
'g',
|
|
455
|
+
{ transform: `translate(0, ${hideProgress ? 0 : 25})` },
|
|
456
|
+
createLanguageTextNode({
|
|
540
457
|
langs,
|
|
541
458
|
totalSize: totalLanguageSize,
|
|
542
459
|
hideProgress,
|
|
543
460
|
statsFormat,
|
|
544
461
|
hideValues,
|
|
545
|
-
})
|
|
546
|
-
|
|
547
|
-
|
|
462
|
+
}),
|
|
463
|
+
),
|
|
464
|
+
];
|
|
548
465
|
};
|
|
549
466
|
|
|
550
467
|
/**
|
|
551
468
|
* Renders donut vertical layout to display user's most frequently used programming languages.
|
|
552
469
|
*
|
|
553
|
-
* @param langs Array of programming languages.
|
|
554
|
-
* @param totalLanguageSize Total size of all languages.
|
|
555
|
-
* @param statsFormat Stats format.
|
|
556
|
-
* @param hideValues Whether to hide stats values.
|
|
557
470
|
* @returns Compact layout card SVG object.
|
|
558
471
|
*/
|
|
559
472
|
const renderDonutVerticalLayout = (
|
|
@@ -561,7 +474,7 @@ const renderDonutVerticalLayout = (
|
|
|
561
474
|
totalLanguageSize: number,
|
|
562
475
|
statsFormat: string,
|
|
563
476
|
hideValues?: boolean,
|
|
564
|
-
):
|
|
477
|
+
): MarkupElement => {
|
|
565
478
|
// Donut vertical chart radius and total length
|
|
566
479
|
const radius = 80;
|
|
567
480
|
const totalCircleLength = getCircleLength(radius);
|
|
@@ -583,22 +496,24 @@ const renderDonutVerticalLayout = (
|
|
|
583
496
|
const circleLength = totalCircleLength * (percentage / 100);
|
|
584
497
|
const delay = startDelayCoefficient * 100;
|
|
585
498
|
|
|
586
|
-
circles.push(
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
stroke
|
|
596
|
-
stroke-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
499
|
+
circles.push(
|
|
500
|
+
el(
|
|
501
|
+
'g',
|
|
502
|
+
{ class: 'stagger', style: `animation-delay: ${delay}ms` },
|
|
503
|
+
el('circle', {
|
|
504
|
+
cx: 150,
|
|
505
|
+
cy: 100,
|
|
506
|
+
r: radius,
|
|
507
|
+
fill: 'transparent',
|
|
508
|
+
stroke: langColor,
|
|
509
|
+
'stroke-width': 25,
|
|
510
|
+
'stroke-dasharray': totalCircleLength,
|
|
511
|
+
'stroke-dashoffset': indent,
|
|
512
|
+
size: percentage,
|
|
513
|
+
'data-testid': 'lang-donut',
|
|
514
|
+
}),
|
|
515
|
+
),
|
|
516
|
+
);
|
|
602
517
|
|
|
603
518
|
// Update the indent for the next part
|
|
604
519
|
indent += circleLength;
|
|
@@ -606,35 +521,31 @@ const renderDonutVerticalLayout = (
|
|
|
606
521
|
startDelayCoefficient += 1;
|
|
607
522
|
}
|
|
608
523
|
|
|
609
|
-
return
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
524
|
+
return el(
|
|
525
|
+
'svg',
|
|
526
|
+
{ 'data-testid': 'lang-items' },
|
|
527
|
+
el('svg', { 'data-testid': 'donut' }, circles),
|
|
528
|
+
el(
|
|
529
|
+
'g',
|
|
530
|
+
{ transform: 'translate(0, 220)' },
|
|
531
|
+
el(
|
|
532
|
+
'svg',
|
|
533
|
+
{ 'data-testid': 'lang-names', x: CARD_PADDING },
|
|
534
|
+
createLanguageTextNode({
|
|
535
|
+
langs,
|
|
536
|
+
totalSize: totalLanguageSize,
|
|
537
|
+
hideProgress: false,
|
|
538
|
+
statsFormat,
|
|
539
|
+
hideValues,
|
|
540
|
+
}),
|
|
541
|
+
),
|
|
542
|
+
),
|
|
543
|
+
);
|
|
629
544
|
};
|
|
630
545
|
|
|
631
546
|
/**
|
|
632
547
|
* Renders pie layout to display user's most frequently used programming languages.
|
|
633
548
|
*
|
|
634
|
-
* @param langs Array of programming languages.
|
|
635
|
-
* @param totalLanguageSize Total size of all languages.
|
|
636
|
-
* @param statsFormat Stats format.
|
|
637
|
-
* @param hideValues Whether to hide stats values.
|
|
638
549
|
* @returns Compact layout card SVG object.
|
|
639
550
|
*/
|
|
640
551
|
const renderPieLayout = (
|
|
@@ -642,7 +553,7 @@ const renderPieLayout = (
|
|
|
642
553
|
totalLanguageSize: number,
|
|
643
554
|
statsFormat: string,
|
|
644
555
|
hideValues?: boolean,
|
|
645
|
-
):
|
|
556
|
+
): MarkupElement => {
|
|
646
557
|
// Pie chart radius and center coordinates
|
|
647
558
|
const radius = 90;
|
|
648
559
|
const centerX = 150;
|
|
@@ -662,17 +573,17 @@ const renderPieLayout = (
|
|
|
662
573
|
const langColor = resolveLangColor(lang);
|
|
663
574
|
|
|
664
575
|
if (langs.length === 1) {
|
|
665
|
-
paths.push(
|
|
666
|
-
|
|
667
|
-
cx
|
|
668
|
-
cy
|
|
669
|
-
r
|
|
670
|
-
stroke
|
|
671
|
-
fill
|
|
672
|
-
data-testid
|
|
673
|
-
size
|
|
674
|
-
|
|
675
|
-
|
|
576
|
+
paths.push(
|
|
577
|
+
el('circle', {
|
|
578
|
+
cx: centerX,
|
|
579
|
+
cy: centerY,
|
|
580
|
+
r: radius,
|
|
581
|
+
stroke: 'none',
|
|
582
|
+
fill: langColor,
|
|
583
|
+
'data-testid': 'lang-pie',
|
|
584
|
+
size: 100,
|
|
585
|
+
}),
|
|
586
|
+
);
|
|
676
587
|
break;
|
|
677
588
|
}
|
|
678
589
|
|
|
@@ -695,16 +606,18 @@ const renderPieLayout = (
|
|
|
695
606
|
const delay = startDelayCoefficient * 100;
|
|
696
607
|
|
|
697
608
|
// SVG arc markup
|
|
698
|
-
paths.push(
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
609
|
+
paths.push(
|
|
610
|
+
el(
|
|
611
|
+
'g',
|
|
612
|
+
{ class: 'stagger', style: `animation-delay: ${delay}ms` },
|
|
613
|
+
el('path', {
|
|
614
|
+
'data-testid': 'lang-pie',
|
|
615
|
+
size: percentage,
|
|
616
|
+
d: `M ${centerX} ${centerY} L ${startPoint.x} ${startPoint.y} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endPoint.x} ${endPoint.y} Z`,
|
|
617
|
+
fill: langColor,
|
|
618
|
+
}),
|
|
619
|
+
),
|
|
620
|
+
);
|
|
708
621
|
|
|
709
622
|
// Update the start angle for the next part
|
|
710
623
|
startAngle = endAngle;
|
|
@@ -712,35 +625,31 @@ const renderPieLayout = (
|
|
|
712
625
|
startDelayCoefficient += 1;
|
|
713
626
|
}
|
|
714
627
|
|
|
715
|
-
return
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
628
|
+
return el(
|
|
629
|
+
'svg',
|
|
630
|
+
{ 'data-testid': 'lang-items' },
|
|
631
|
+
el('svg', { 'data-testid': 'pie' }, paths),
|
|
632
|
+
el(
|
|
633
|
+
'g',
|
|
634
|
+
{ transform: 'translate(0, 220)' },
|
|
635
|
+
el(
|
|
636
|
+
'svg',
|
|
637
|
+
{ 'data-testid': 'lang-names', x: CARD_PADDING },
|
|
638
|
+
createLanguageTextNode({
|
|
639
|
+
langs,
|
|
640
|
+
totalSize: totalLanguageSize,
|
|
641
|
+
hideProgress: false,
|
|
642
|
+
statsFormat,
|
|
643
|
+
hideValues,
|
|
644
|
+
}),
|
|
645
|
+
),
|
|
646
|
+
),
|
|
647
|
+
);
|
|
735
648
|
};
|
|
736
649
|
|
|
737
650
|
/**
|
|
738
651
|
* Creates the SVG paths for the language donut chart.
|
|
739
652
|
*
|
|
740
|
-
* @param cx Donut center x-position.
|
|
741
|
-
* @param cy Donut center y-position.
|
|
742
|
-
* @param radius Donut arc Radius.
|
|
743
|
-
* @param percentages Array with donut section percentages.
|
|
744
653
|
* @returns Array of svg path elements
|
|
745
654
|
*/
|
|
746
655
|
const createDonutPaths = (
|
|
@@ -754,7 +663,7 @@ const createDonutPaths = (
|
|
|
754
663
|
|
|
755
664
|
const totalPercent = percentages.reduce((acc, curr) => acc + curr, 0);
|
|
756
665
|
for (const rawPercent of percentages) {
|
|
757
|
-
const percent = parseFloat(((rawPercent / totalPercent) * 100).toFixed(2));
|
|
666
|
+
const percent = Number.parseFloat(((rawPercent / totalPercent) * 100).toFixed(2));
|
|
758
667
|
|
|
759
668
|
const endAngle = 3.6 * percent + startAngle;
|
|
760
669
|
const startPoint = polarToCartesian(cx, cy, radius, endAngle - 90); // rotate donut 90 degrees counter-clockwise.
|
|
@@ -774,11 +683,6 @@ const createDonutPaths = (
|
|
|
774
683
|
/**
|
|
775
684
|
* Renders the donut language card layout.
|
|
776
685
|
*
|
|
777
|
-
* @param langs Array of programming languages.
|
|
778
|
-
* @param width Card width.
|
|
779
|
-
* @param totalLanguageSize Total size of all languages.
|
|
780
|
-
* @param statsFormat Stats format.
|
|
781
|
-
* @param hideValues Whether to hide stats values.
|
|
782
686
|
* @returns Donut layout card SVG object.
|
|
783
687
|
*/
|
|
784
688
|
const renderDonutLayout = (
|
|
@@ -787,9 +691,9 @@ const renderDonutLayout = (
|
|
|
787
691
|
totalLanguageSize: number,
|
|
788
692
|
statsFormat: string,
|
|
789
693
|
hideValues?: boolean,
|
|
790
|
-
):
|
|
694
|
+
): Array<Child> => {
|
|
791
695
|
if (!Number.isFinite(width)) {
|
|
792
|
-
throw new
|
|
696
|
+
throw new TypeError(`Invalid width: "${width}"`);
|
|
793
697
|
}
|
|
794
698
|
|
|
795
699
|
const centerX = width / 3;
|
|
@@ -797,59 +701,55 @@ const renderDonutLayout = (
|
|
|
797
701
|
const radius = centerX - 60;
|
|
798
702
|
const strokeWidth = 12;
|
|
799
703
|
|
|
800
|
-
const colors = langs.map(resolveLangColor);
|
|
704
|
+
const colors = langs.map((lang) => resolveLangColor(lang));
|
|
801
705
|
const langsPercents = langs.map((lang) =>
|
|
802
|
-
parseFloat(((lang.size / totalLanguageSize) * 100).toFixed(2)),
|
|
706
|
+
Number.parseFloat(((lang.size / totalLanguageSize) * 100).toFixed(2)),
|
|
803
707
|
);
|
|
804
708
|
|
|
805
709
|
const langPaths = createDonutPaths(centerX, centerY, radius, langsPercents);
|
|
806
710
|
|
|
807
|
-
const donutPaths =
|
|
711
|
+
const donutPaths: Array<Child> =
|
|
808
712
|
langs.length === 1
|
|
809
|
-
?
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
`;
|
|
713
|
+
? [
|
|
714
|
+
el('circle', {
|
|
715
|
+
cx: centerX,
|
|
716
|
+
cy: centerY,
|
|
717
|
+
r: radius,
|
|
718
|
+
stroke: colors[0] ?? DEFAULT_LANG_COLOR,
|
|
719
|
+
fill: 'none',
|
|
720
|
+
'stroke-width': strokeWidth,
|
|
721
|
+
'data-testid': 'lang-donut',
|
|
722
|
+
size: 100,
|
|
723
|
+
}),
|
|
724
|
+
]
|
|
725
|
+
: langPaths.map((section, index) =>
|
|
726
|
+
el(
|
|
727
|
+
'g',
|
|
728
|
+
{ class: 'stagger', style: `animation-delay: ${(index + 3) * 100 + 300}ms` },
|
|
729
|
+
el('path', {
|
|
730
|
+
'data-testid': 'lang-donut',
|
|
731
|
+
size: section.percent,
|
|
732
|
+
d: section.d,
|
|
733
|
+
stroke: colors[index] ?? DEFAULT_LANG_COLOR,
|
|
734
|
+
fill: 'none',
|
|
735
|
+
'stroke-width': strokeWidth,
|
|
736
|
+
}),
|
|
737
|
+
),
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
return [
|
|
741
|
+
createDonutLanguagesNode({ langs, totalSize: totalLanguageSize, hideValues, statsFormat }),
|
|
742
|
+
el(
|
|
743
|
+
'g',
|
|
744
|
+
{ transform: `translate(125, ${donutCenterTranslation(langs.length)})` },
|
|
745
|
+
el('svg', { width, height: width }, donutPaths),
|
|
746
|
+
),
|
|
747
|
+
];
|
|
845
748
|
};
|
|
846
749
|
|
|
847
750
|
/**
|
|
848
751
|
* Creates the no languages data SVG node.
|
|
849
752
|
*
|
|
850
|
-
* @param props Object with function properties.
|
|
851
|
-
* @param props.text No languages data translated text.
|
|
852
|
-
* @param props.layout Card layout.
|
|
853
753
|
* @returns No languages data SVG node string.
|
|
854
754
|
*/
|
|
855
755
|
const noLanguagesDataNode = ({
|
|
@@ -858,20 +758,20 @@ const noLanguagesDataNode = ({
|
|
|
858
758
|
}: {
|
|
859
759
|
text: string;
|
|
860
760
|
layout: TopLangLayout | undefined;
|
|
861
|
-
}):
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
761
|
+
}): MarkupElement =>
|
|
762
|
+
el(
|
|
763
|
+
'text',
|
|
764
|
+
{
|
|
765
|
+
x: layout === 'pie' || layout === 'donut-vertical' ? CARD_PADDING : 0,
|
|
766
|
+
y: 11,
|
|
767
|
+
class: 'stat bold',
|
|
768
|
+
},
|
|
769
|
+
text,
|
|
770
|
+
);
|
|
868
771
|
|
|
869
772
|
/**
|
|
870
773
|
* Get default languages count for provided card layout.
|
|
871
774
|
*
|
|
872
|
-
* @param props Function properties.
|
|
873
|
-
* @param props.layout Input layout string.
|
|
874
|
-
* @param props.hide_progress Input hide_progress parameter value.
|
|
875
775
|
* @returns Default languages count for input layout.
|
|
876
776
|
*/
|
|
877
777
|
const getDefaultLanguagesCountByLayout = ({
|
|
@@ -883,22 +783,22 @@ const getDefaultLanguagesCountByLayout = ({
|
|
|
883
783
|
}): number => {
|
|
884
784
|
if (layout === 'compact' || hide_progress === true) {
|
|
885
785
|
return COMPACT_LAYOUT_DEFAULT_LANGS_COUNT;
|
|
886
|
-
}
|
|
786
|
+
}
|
|
787
|
+
if (layout === 'donut') {
|
|
887
788
|
return DONUT_LAYOUT_DEFAULT_LANGS_COUNT;
|
|
888
|
-
}
|
|
789
|
+
}
|
|
790
|
+
if (layout === 'donut-vertical') {
|
|
889
791
|
return DONUT_VERTICAL_LAYOUT_DEFAULT_LANGS_COUNT;
|
|
890
|
-
}
|
|
792
|
+
}
|
|
793
|
+
if (layout === 'pie') {
|
|
891
794
|
return PIE_LAYOUT_DEFAULT_LANGS_COUNT;
|
|
892
|
-
} else {
|
|
893
|
-
return NORMAL_LAYOUT_DEFAULT_LANGS_COUNT;
|
|
894
795
|
}
|
|
796
|
+
return NORMAL_LAYOUT_DEFAULT_LANGS_COUNT;
|
|
895
797
|
};
|
|
896
798
|
|
|
897
799
|
/**
|
|
898
800
|
* Renders card that display user's most frequently used programming languages.
|
|
899
801
|
*
|
|
900
|
-
* @param topLangs User's most frequently used programming languages.
|
|
901
|
-
* @param options Card options.
|
|
902
802
|
* @returns Language card SVG object.
|
|
903
803
|
*/
|
|
904
804
|
const renderTopLanguages = (
|
|
@@ -929,7 +829,7 @@ const renderTopLanguages = (
|
|
|
929
829
|
const { langs, totalLanguageSize } = trimTopLanguages(topLangs, langs_count, hide);
|
|
930
830
|
|
|
931
831
|
let width = card_width
|
|
932
|
-
? isNaN(card_width)
|
|
832
|
+
? Number.isNaN(card_width)
|
|
933
833
|
? DEFAULT_CARD_WIDTH
|
|
934
834
|
: card_width < MIN_CARD_WIDTH
|
|
935
835
|
? MIN_CARD_WIDTH
|
|
@@ -939,7 +839,7 @@ const renderTopLanguages = (
|
|
|
939
839
|
|
|
940
840
|
const { lightColors, darkColors } = getLightDarkColors(options);
|
|
941
841
|
|
|
942
|
-
let finalLayout:
|
|
842
|
+
let finalLayout: Child;
|
|
943
843
|
if (langs.length === 0) {
|
|
944
844
|
height = COMPACT_LAYOUT_BASE_HEIGHT;
|
|
945
845
|
finalLayout = noLanguagesDataNode({
|
|
@@ -965,7 +865,7 @@ const renderTopLanguages = (
|
|
|
965
865
|
);
|
|
966
866
|
} else if (layout === 'donut') {
|
|
967
867
|
height = calculateDonutLayoutHeight(langs.length);
|
|
968
|
-
width
|
|
868
|
+
width += 50; // padding
|
|
969
869
|
finalLayout = renderDonutLayout(langs, width, totalLanguageSize, stats_format, hide_values);
|
|
970
870
|
} else {
|
|
971
871
|
finalLayout = renderNormalLayout(langs, width, totalLanguageSize, stats_format, hide_values);
|
|
@@ -987,52 +887,40 @@ const renderTopLanguages = (
|
|
|
987
887
|
card.setHideBorder(hide_border);
|
|
988
888
|
card.setHideTitle(hide_title);
|
|
989
889
|
card.setCSS({
|
|
990
|
-
light: ({ textColor, progBarBgColor }) =>
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
width: 0
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
width: calc(100%-100px)
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
width:
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
.
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
fill:
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
animation: slideInAnimation 1s ease-in-out forwards;
|
|
1025
|
-
}
|
|
1026
|
-
.lang-progress{
|
|
1027
|
-
animation: growWidthAnimation 0.6s ease-in-out forwards;
|
|
1028
|
-
}
|
|
1029
|
-
.progress-background { fill: ${progBarBgColor}; }
|
|
1030
|
-
`,
|
|
1031
|
-
dark: ({ textColor, progBarBgColor }) => `
|
|
1032
|
-
.stat { fill: ${textColor}; }
|
|
1033
|
-
.lang-name { fill: ${textColor}; }
|
|
1034
|
-
.progress-background { fill: ${progBarBgColor}; }
|
|
1035
|
-
`,
|
|
890
|
+
light: ({ textColor, progBarBgColor }) => [
|
|
891
|
+
atRule(
|
|
892
|
+
'@keyframes slideInAnimation',
|
|
893
|
+
rule('from', { width: 0 }),
|
|
894
|
+
// Invalid on purpose — `calc` needs spaces around its `-`. Browsers drop the
|
|
895
|
+
// declaration and animate to the rect's own width, which is the reveal the mask wants.
|
|
896
|
+
rule('to', { width: 'calc(100%-100px)' }),
|
|
897
|
+
),
|
|
898
|
+
atRule(
|
|
899
|
+
'@keyframes growWidthAnimation',
|
|
900
|
+
rule('from', { width: 0 }),
|
|
901
|
+
rule('to', { width: '100%' }),
|
|
902
|
+
),
|
|
903
|
+
rule('.stat', {
|
|
904
|
+
font: `600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif`,
|
|
905
|
+
fill: textColor,
|
|
906
|
+
}),
|
|
907
|
+
atRule(
|
|
908
|
+
'@supports(-moz-appearance: auto)',
|
|
909
|
+
cssComment('Selector detects Firefox'),
|
|
910
|
+
rule('.stat', { 'font-size': '12px' }),
|
|
911
|
+
),
|
|
912
|
+
rule('.bold', { 'font-weight': 700 }),
|
|
913
|
+
rule('.lang-name', { font: '400 11px "Segoe UI", Ubuntu, Sans-Serif', fill: textColor }),
|
|
914
|
+
rule('.stagger', { opacity: 0, animation: 'fadeInAnimation 0.3s ease-in-out forwards' }),
|
|
915
|
+
rule('#rect-mask rect', { animation: 'slideInAnimation 1s ease-in-out forwards' }),
|
|
916
|
+
rule('.lang-progress', { animation: 'growWidthAnimation 0.6s ease-in-out forwards' }),
|
|
917
|
+
rule('.progress-background', { fill: progBarBgColor }),
|
|
918
|
+
],
|
|
919
|
+
dark: ({ textColor, progBarBgColor }) => [
|
|
920
|
+
rule('.stat', { fill: textColor }),
|
|
921
|
+
rule('.lang-name', { fill: textColor }),
|
|
922
|
+
rule('.progress-background', { fill: progBarBgColor }),
|
|
923
|
+
],
|
|
1036
924
|
});
|
|
1037
925
|
|
|
1038
926
|
// `role="img"` hides the inner text from assistive tech, so everything the card
|
|
@@ -1048,11 +936,7 @@ const renderTopLanguages = (
|
|
|
1048
936
|
return card.render(finalLayout);
|
|
1049
937
|
}
|
|
1050
938
|
|
|
1051
|
-
return card.render(
|
|
1052
|
-
<svg data-testid="lang-items" x="${CARD_PADDING}">
|
|
1053
|
-
${finalLayout}
|
|
1054
|
-
</svg>
|
|
1055
|
-
`);
|
|
939
|
+
return card.render(el('svg', { 'data-testid': 'lang-items', x: CARD_PADDING }, finalLayout));
|
|
1056
940
|
};
|
|
1057
941
|
|
|
1058
942
|
export {
|