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