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