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