@stats-forge/github-stats-forge-core 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -10
- package/build/api/api-result.d.ts +6 -7
- package/build/api/api-result.d.ts.map +1 -1
- package/build/api/api-result.js +4 -5
- package/build/api/gist.d.ts +3 -10
- package/build/api/gist.d.ts.map +1 -1
- package/build/api/gist.js +4 -11
- package/build/api/index.d.ts +12 -12
- package/build/api/params.d.ts +11 -8
- package/build/api/params.d.ts.map +1 -1
- package/build/api/params.js +16 -14
- package/build/api/pin.d.ts +3 -19
- package/build/api/pin.d.ts.map +1 -1
- package/build/api/pin.js +6 -22
- package/build/api/stats.d.ts +10 -4
- package/build/api/stats.d.ts.map +1 -1
- package/build/api/stats.js +6 -32
- package/build/api/top-langs.d.ts +12 -6
- package/build/api/top-langs.d.ts.map +1 -1
- package/build/api/top-langs.js +6 -26
- package/build/api/wakatime.d.ts +13 -5
- package/build/api/wakatime.d.ts.map +1 -1
- package/build/api/wakatime.js +4 -21
- package/build/calculateRank.d.ts +0 -9
- package/build/calculateRank.d.ts.map +1 -1
- package/build/calculateRank.js +12 -17
- package/build/cards/gist.d.ts +2 -4
- package/build/cards/gist.d.ts.map +1 -1
- package/build/cards/gist.js +27 -36
- package/build/cards/options.d.ts +1 -1
- package/build/cards/repo.d.ts +2 -4
- package/build/cards/repo.d.ts.map +1 -1
- package/build/cards/repo.js +61 -93
- package/build/cards/stats.d.ts +3 -8
- package/build/cards/stats.d.ts.map +1 -1
- package/build/cards/stats.js +119 -165
- package/build/cards/top-languages.d.ts +5 -31
- package/build/cards/top-languages.d.ts.map +1 -1
- package/build/cards/top-languages.js +176 -384
- package/build/cards/wakatime.d.ts +4 -6
- package/build/cards/wakatime.d.ts.map +1 -1
- package/build/cards/wakatime.js +118 -185
- package/build/common/Card.d.ts +14 -62
- package/build/common/Card.d.ts.map +1 -1
- package/build/common/Card.js +97 -199
- package/build/common/I18n.d.ts +0 -6
- package/build/common/I18n.d.ts.map +1 -1
- package/build/common/I18n.js +3 -6
- package/build/common/color.d.ts +4 -17
- package/build/common/color.d.ts.map +1 -1
- package/build/common/color.js +67 -49
- package/build/common/config.d.ts +4 -14
- package/build/common/config.d.ts.map +1 -1
- package/build/common/config.js +3 -16
- package/build/common/constants.d.ts +4 -2
- package/build/common/constants.d.ts.map +1 -1
- package/build/common/constants.js +3 -1
- package/build/common/date.d.ts +0 -2
- package/build/common/date.d.ts.map +1 -1
- package/build/common/date.js +0 -2
- package/build/common/emojiMap.d.ts +8 -0
- package/build/common/emojiMap.d.ts.map +1 -0
- package/build/common/emojiMap.js +10 -0
- package/build/common/emojiMap.json +1915 -0
- package/build/common/error.d.ts +1 -10
- package/build/common/error.d.ts.map +1 -1
- package/build/common/error.js +1 -10
- package/build/common/fmt.d.ts +3 -15
- package/build/common/fmt.d.ts.map +1 -1
- package/build/common/fmt.js +7 -35
- package/build/common/http.d.ts +2 -7
- package/build/common/http.d.ts.map +1 -1
- package/build/common/http.js +8 -17
- package/build/common/icons.d.ts +18 -20
- package/build/common/icons.d.ts.map +1 -1
- package/build/common/icons.js +103 -67
- package/build/common/languageColors.d.ts +0 -1
- package/build/common/languageColors.d.ts.map +1 -1
- package/build/common/languageColors.js +1 -4
- package/build/common/languageColors.json +18 -2
- package/build/common/ops.d.ts +3 -14
- package/build/common/ops.d.ts.map +1 -1
- package/build/common/ops.js +19 -30
- package/build/common/render.d.ts +28 -82
- package/build/common/render.d.ts.map +1 -1
- package/build/common/render.js +175 -211
- package/build/common/retryer.d.ts +2 -7
- package/build/common/retryer.d.ts.map +1 -1
- package/build/common/retryer.js +4 -11
- package/build/fetchers/gist.d.ts +2 -5
- package/build/fetchers/gist.d.ts.map +1 -1
- package/build/fetchers/gist.js +1 -5
- package/build/fetchers/index.d.ts +12 -12
- package/build/fetchers/repo.d.ts +2 -11
- package/build/fetchers/repo.d.ts.map +1 -1
- package/build/fetchers/repo.js +20 -33
- package/build/fetchers/stats.d.ts +2 -32
- package/build/fetchers/stats.d.ts.map +1 -1
- package/build/fetchers/stats.js +27 -86
- package/build/fetchers/top-languages.d.ts +2 -9
- package/build/fetchers/top-languages.d.ts.map +1 -1
- package/build/fetchers/top-languages.js +15 -31
- package/build/fetchers/types.d.ts +1 -1
- package/build/fetchers/wakatime.d.ts +2 -6
- package/build/fetchers/wakatime.d.ts.map +1 -1
- package/build/fetchers/wakatime.js +1 -5
- package/build/graphql/contributionsDocument.d.ts +3 -3
- package/build/graphql/contributionsDocument.d.ts.map +1 -1
- package/build/graphql/contributionsDocument.js +0 -1
- package/build/graphql/generated/gist.d.ts +1 -1
- package/build/graphql/generated/repo.d.ts +1 -1
- package/build/graphql/generated/stats.d.ts +3 -3
- package/build/graphql/generated/top-languages.d.ts +2 -2
- package/build/graphql/graphqlDocument.d.ts +0 -1
- package/build/graphql/graphqlDocument.d.ts.map +1 -1
- package/build/graphql/graphqlDocument.js +0 -1
- package/build/graphql/reposContributedToDocument.d.ts +4 -5
- package/build/graphql/reposContributedToDocument.d.ts.map +1 -1
- package/build/graphql/reposContributedToDocument.js +0 -2
- package/build/markup/css.d.ts +35 -0
- package/build/markup/css.d.ts.map +1 -0
- package/build/markup/css.js +64 -0
- package/build/markup/element.d.ts +41 -0
- package/build/markup/element.d.ts.map +1 -0
- package/build/markup/element.js +127 -0
- package/build/markup/html.d.ts +13 -0
- package/build/markup/html.d.ts.map +1 -0
- package/build/markup/html.js +27 -0
- package/build/markup/index.d.ts +9 -0
- package/build/markup/index.d.ts.map +1 -0
- package/build/markup/index.js +6 -0
- package/build/markup/serialize.d.ts +16 -0
- package/build/markup/serialize.d.ts.map +1 -0
- package/build/markup/serialize.js +17 -0
- package/build/themes/index.d.ts +332 -333
- package/build/themes/index.d.ts.map +1 -1
- package/build/themes/index.js +1 -1
- package/build/translations.d.ts +2 -6
- package/build/translations.d.ts.map +1 -1
- package/build/translations.js +621 -628
- package/package.json +17 -26
- package/src/api/api-result.ts +10 -9
- package/src/api/gist.ts +11 -18
- package/src/api/index.ts +12 -12
- package/src/api/params.ts +27 -21
- package/src/api/pin.ts +13 -28
- package/src/api/stats.ts +13 -39
- package/src/api/top-langs.ts +13 -33
- package/src/api/wakatime.ts +11 -28
- package/src/calculateRank.ts +12 -23
- package/src/cards/gist.ts +40 -43
- package/src/cards/options.ts +1 -1
- package/src/cards/repo.ts +97 -107
- package/src/cards/stats.ts +165 -184
- package/src/cards/top-languages.ts +297 -413
- package/src/cards/wakatime.ts +193 -221
- package/src/common/Card.ts +165 -217
- package/src/common/I18n.ts +3 -6
- package/src/common/color.ts +80 -57
- package/src/common/config.ts +5 -18
- package/src/common/constants.ts +5 -2
- package/src/common/date.ts +0 -2
- package/src/common/emojiMap.json +1915 -0
- package/src/common/emojiMap.ts +13 -0
- package/src/common/error.ts +2 -11
- package/src/common/fmt.ts +7 -44
- package/src/common/http.ts +9 -15
- package/src/common/icons.ts +117 -42
- package/src/common/languageColors.json +18 -2
- package/src/common/languageColors.ts +1 -4
- package/src/common/ops.ts +26 -34
- package/src/common/render.ts +279 -232
- package/src/common/retryer.ts +8 -15
- package/src/fetchers/gist.ts +8 -12
- package/src/fetchers/index.ts +12 -12
- package/src/fetchers/repo.ts +27 -40
- package/src/fetchers/stats.ts +44 -102
- package/src/fetchers/top-languages.ts +26 -40
- package/src/fetchers/types.ts +1 -1
- package/src/fetchers/wakatime.ts +5 -9
- package/src/graphql/contributionsDocument.ts +7 -5
- package/src/graphql/generated/gist.ts +1 -1
- package/src/graphql/generated/repo.ts +1 -1
- package/src/graphql/generated/stats.ts +2 -2
- package/src/graphql/generated/top-languages.ts +2 -2
- package/src/graphql/graphqlDocument.ts +0 -1
- package/src/graphql/reposContributedToDocument.ts +6 -7
- package/src/markup/css.ts +118 -0
- package/src/markup/element.ts +246 -0
- package/src/markup/html.ts +34 -0
- package/src/markup/index.ts +9 -0
- package/src/markup/serialize.ts +27 -0
- package/src/themes/index.ts +1 -1
- package/src/translations.ts +622 -629
- package/build/cards/index.d.ts +0 -11
- package/build/cards/index.d.ts.map +0 -1
- package/build/cards/index.js +0 -7
- package/build/common/html.d.ts +0 -8
- package/build/common/html.d.ts.map +0 -1
- package/build/common/html.js +0 -14
- package/build/index.d.ts +0 -15
- package/build/index.d.ts.map +0 -1
- package/build/index.js +0 -10
- package/src/_emoji-name-map.d.ts +0 -10
- package/src/_github-username-regex.d.ts +0 -4
- package/src/cards/index.ts +0 -21
- package/src/common/html.ts +0 -17
- package/src/index.ts +0 -18
package/src/cards/stats.ts
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { Card } from '../common/Card.
|
|
2
|
-
import { getLightDarkColors } from '../common/color.
|
|
3
|
-
import type { CardColors } from '../common/color.
|
|
4
|
-
import { CardError } from '../common/error.
|
|
5
|
-
import { I18n } from '../common/I18n.
|
|
6
|
-
import { icons, rankIcon } from '../common/icons.
|
|
7
|
-
import { buildSearchFilter, clampValue } from '../common/ops.
|
|
8
|
-
import { createTextNode, flexLayout, measureText } from '../common/render.
|
|
9
|
-
import type { StatsData } from '../fetchers/types.
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
import
|
|
1
|
+
import { Card } from '../common/Card.ts';
|
|
2
|
+
import { getLightDarkColors } from '../common/color.ts';
|
|
3
|
+
import type { CardColors } from '../common/color.ts';
|
|
4
|
+
import { CardError } from '../common/error.ts';
|
|
5
|
+
import { I18n } from '../common/I18n.ts';
|
|
6
|
+
import { icons, rankIcon } from '../common/icons.ts';
|
|
7
|
+
import { buildSearchFilter, clampValue } from '../common/ops.ts';
|
|
8
|
+
import { createTextNode, flexLayout, measureText } from '../common/render.ts';
|
|
9
|
+
import type { StatsData } from '../fetchers/types.ts';
|
|
10
|
+
import type { Child, CssChild } from '../markup/index.ts';
|
|
11
|
+
import { atRule, cssComment, el, rule } from '../markup/index.ts';
|
|
12
|
+
import { statCardLocales, wakatimeCardLocales } from '../translations.ts';
|
|
13
|
+
|
|
14
|
+
import type { CardOptions, CommonCardOptions } from './options.ts';
|
|
13
15
|
|
|
14
16
|
const CARD_MIN_WIDTH = 287;
|
|
15
17
|
const CARD_DEFAULT_WIDTH = 287;
|
|
@@ -18,6 +20,18 @@ const RANK_CARD_DEFAULT_WIDTH = 450;
|
|
|
18
20
|
const RANK_ONLY_CARD_MIN_WIDTH = 290;
|
|
19
21
|
const RANK_ONLY_CARD_DEFAULT_WIDTH = 290;
|
|
20
22
|
|
|
23
|
+
const STAT_FONT_SIZE = 14;
|
|
24
|
+
/** Padding the card keeps at its edges; matches `Card`'s own `paddingX`. */
|
|
25
|
+
const CARD_PADDING_X = 25;
|
|
26
|
+
/** How far a stat row is translated into the card; see `createTextNode`. */
|
|
27
|
+
const STAT_ROW_X = 25;
|
|
28
|
+
/** Room the rank ring needs at the right edge, so a value never runs into it. */
|
|
29
|
+
const RANK_GUTTER = 120;
|
|
30
|
+
/** `createTextNode`'s own label offset, which it applies only when icons are shown. */
|
|
31
|
+
const LABEL_X_OFFSET = 25;
|
|
32
|
+
/** Smallest gap kept between the longest label and its value. */
|
|
33
|
+
const LABEL_VALUE_GAP = 16;
|
|
34
|
+
|
|
21
35
|
/** Rank indicators the card can draw; the api validates `rank_icon` against this. */
|
|
22
36
|
const RANK_ICONS = ['default', 'github', 'percentile'] as const;
|
|
23
37
|
type RankIcon = (typeof RANK_ICONS)[number];
|
|
@@ -44,7 +58,7 @@ interface StatCardOptions extends CommonCardOptions {
|
|
|
44
58
|
|
|
45
59
|
/** Meta data for a stat, used to build its text node and accessibility label. */
|
|
46
60
|
interface StatItem {
|
|
47
|
-
icon:
|
|
61
|
+
icon: Child;
|
|
48
62
|
label: string;
|
|
49
63
|
value: number | string;
|
|
50
64
|
id: string;
|
|
@@ -53,7 +67,7 @@ interface StatItem {
|
|
|
53
67
|
}
|
|
54
68
|
|
|
55
69
|
/** Long locales that need more space for text. Keep sorted alphabetically. */
|
|
56
|
-
const LONG_LOCALES:
|
|
70
|
+
const LONG_LOCALES: ReadonlySet<string> = new Set([
|
|
57
71
|
'az',
|
|
58
72
|
'bg',
|
|
59
73
|
'cs',
|
|
@@ -80,58 +94,37 @@ const LONG_LOCALES: Array<string> = [
|
|
|
80
94
|
'uk-ua',
|
|
81
95
|
'uz',
|
|
82
96
|
'zh-tw',
|
|
83
|
-
];
|
|
97
|
+
]);
|
|
84
98
|
|
|
85
99
|
/**
|
|
86
100
|
* Calculates progress along the boundary of the circle, i.e. its circumference.
|
|
87
101
|
*
|
|
88
|
-
* @param value The rank value to calculate progress for.
|
|
89
102
|
* @returns Progress value.
|
|
90
103
|
*/
|
|
91
104
|
const calculateCircleProgress = (value: number): number => {
|
|
92
105
|
const radius = 40;
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
if (value < 0) {
|
|
96
|
-
value = 0;
|
|
97
|
-
}
|
|
98
|
-
if (value > 100) {
|
|
99
|
-
value = 100;
|
|
100
|
-
}
|
|
106
|
+
const circumference = Math.PI * (radius * 2);
|
|
107
|
+
const clamped = Math.min(Math.max(value, 0), 100);
|
|
101
108
|
|
|
102
|
-
return ((100 -
|
|
109
|
+
return ((100 - clamped) / 100) * circumference;
|
|
103
110
|
};
|
|
104
111
|
|
|
105
112
|
/**
|
|
106
113
|
* Retrieves the animation to display progress along the circumference of circle
|
|
107
114
|
* from the beginning to the given value in a clockwise direction.
|
|
108
115
|
*
|
|
109
|
-
* @param props The props object.
|
|
110
|
-
* @param props.progress The progress value to animate to.
|
|
111
116
|
* @returns Progress animation css.
|
|
112
117
|
*/
|
|
113
|
-
const getProgressAnimation = ({ progress }: { progress: number }):
|
|
114
|
-
|
|
115
|
-
@keyframes rankAnimation
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
to {
|
|
120
|
-
stroke-dashoffset: ${calculateCircleProgress(progress)};
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
`;
|
|
124
|
-
};
|
|
118
|
+
const getProgressAnimation = ({ progress }: { progress: number }): CssChild =>
|
|
119
|
+
atRule(
|
|
120
|
+
'@keyframes rankAnimation',
|
|
121
|
+
rule('from', { 'stroke-dashoffset': calculateCircleProgress(0) }),
|
|
122
|
+
rule('to', { 'stroke-dashoffset': calculateCircleProgress(progress) }),
|
|
123
|
+
);
|
|
125
124
|
|
|
126
125
|
/**
|
|
127
126
|
* Retrieves CSS styles for a card.
|
|
128
127
|
*
|
|
129
|
-
* @param colors The colors to use for the card.
|
|
130
|
-
* @param colors.textColor The text color.
|
|
131
|
-
* @param colors.iconColor The icon color.
|
|
132
|
-
* @param colors.ringColor The ring color.
|
|
133
|
-
* @param colors.show_icons Whether to show icons.
|
|
134
|
-
* @param colors.progress The progress value to animate to.
|
|
135
128
|
* @returns Card CSS styles.
|
|
136
129
|
*/
|
|
137
130
|
const getStyles = ({
|
|
@@ -146,64 +139,56 @@ const getStyles = ({
|
|
|
146
139
|
ringColor: string;
|
|
147
140
|
show_icons: boolean;
|
|
148
141
|
progress: number;
|
|
149
|
-
}):
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
animation: rankAnimation 1s forwards ease-in-out;
|
|
196
|
-
}
|
|
197
|
-
${getProgressAnimation({ progress })}
|
|
198
|
-
`;
|
|
199
|
-
};
|
|
142
|
+
}): Array<CssChild> => [
|
|
143
|
+
rule('.stat', {
|
|
144
|
+
font: `400 ${STAT_FONT_SIZE}px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif`,
|
|
145
|
+
fill: textColor,
|
|
146
|
+
'font-variant-numeric': 'tabular-nums',
|
|
147
|
+
}),
|
|
148
|
+
atRule(
|
|
149
|
+
'@supports(-moz-appearance: auto)',
|
|
150
|
+
cssComment('Selector detects Firefox'),
|
|
151
|
+
rule('.stat', { 'font-size': '12px' }),
|
|
152
|
+
),
|
|
153
|
+
rule('.stagger', { opacity: 0, animation: 'fadeInAnimation 0.3s ease-in-out forwards' }),
|
|
154
|
+
rule('.rank-text', {
|
|
155
|
+
font: "700 22px 'Segoe UI', Ubuntu, Sans-Serif",
|
|
156
|
+
fill: textColor,
|
|
157
|
+
animation: 'scaleInAnimation 0.3s ease-in-out forwards',
|
|
158
|
+
}),
|
|
159
|
+
rule('.rank-percentile-header', { 'font-size': '14px' }),
|
|
160
|
+
rule('.rank-percentile-text', { 'font-size': '16px' }),
|
|
161
|
+
cssComment('Labels recede so that the values read first.'),
|
|
162
|
+
rule('.not_bold', { 'font-weight': 400, opacity: 0.75 }),
|
|
163
|
+
rule('.bold', { 'font-weight': 600 }),
|
|
164
|
+
rule('.icon', {
|
|
165
|
+
fill: iconColor,
|
|
166
|
+
opacity: 0.75,
|
|
167
|
+
display: show_icons ? 'block' : 'none',
|
|
168
|
+
}),
|
|
169
|
+
rule('.rank-circle-rim', {
|
|
170
|
+
stroke: ringColor,
|
|
171
|
+
fill: 'none',
|
|
172
|
+
'stroke-width': 4,
|
|
173
|
+
opacity: 0.15,
|
|
174
|
+
}),
|
|
175
|
+
rule('.rank-circle', {
|
|
176
|
+
stroke: ringColor,
|
|
177
|
+
'stroke-dasharray': 250,
|
|
178
|
+
fill: 'none',
|
|
179
|
+
'stroke-width': 4,
|
|
180
|
+
'stroke-linecap': 'round',
|
|
181
|
+
opacity: 1,
|
|
182
|
+
'transform-origin': '-10px 8px',
|
|
183
|
+
transform: 'rotate(-90deg)',
|
|
184
|
+
animation: 'rankAnimation 0.8s forwards ease-in-out',
|
|
185
|
+
}),
|
|
186
|
+
getProgressAnimation({ progress }),
|
|
187
|
+
];
|
|
200
188
|
|
|
201
189
|
/**
|
|
202
190
|
* Return the label for commits according to the selected options
|
|
203
191
|
*
|
|
204
|
-
* @param include_all_commits Option to include all years
|
|
205
|
-
* @param commits_year Option to include only selected year
|
|
206
|
-
* @param i18n The I18n instance.
|
|
207
192
|
* @returns The label corresponding to the options.
|
|
208
193
|
*/
|
|
209
194
|
const getTotalCommitsYearLabel = (
|
|
@@ -220,11 +205,6 @@ const getTotalCommitsYearLabel = (
|
|
|
220
205
|
/**
|
|
221
206
|
* Renders the stats card.
|
|
222
207
|
*
|
|
223
|
-
* @param stats The stats data.
|
|
224
|
-
* @param options The card options.
|
|
225
|
-
* @param username GitHub username, used to build stat search links.
|
|
226
|
-
* @param repo Repositories to scope the search links to.
|
|
227
|
-
* @param owner Owners to scope the search links to.
|
|
228
208
|
* @returns The stats card SVG object.
|
|
229
209
|
*/
|
|
230
210
|
const renderStatsCard = (
|
|
@@ -276,7 +256,7 @@ const renderStatsCard = (
|
|
|
276
256
|
show = [],
|
|
277
257
|
} = options;
|
|
278
258
|
|
|
279
|
-
const lheight = parseInt(String(line_height), 10);
|
|
259
|
+
const lheight = Number.parseInt(String(line_height), 10);
|
|
280
260
|
|
|
281
261
|
const { lightColors, darkColors } = getLightDarkColors(options);
|
|
282
262
|
|
|
@@ -290,13 +270,13 @@ const renderStatsCard = (
|
|
|
290
270
|
});
|
|
291
271
|
|
|
292
272
|
// Meta data for creating text nodes with createTextNode function
|
|
293
|
-
const STATS: Record<string, StatItem> = {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
273
|
+
const STATS: Record<string, StatItem> = {
|
|
274
|
+
stars: {
|
|
275
|
+
icon: icons.star,
|
|
276
|
+
label: i18n.t('statcard.totalstars'),
|
|
277
|
+
value: totalStars,
|
|
278
|
+
id: 'stars',
|
|
279
|
+
},
|
|
300
280
|
};
|
|
301
281
|
|
|
302
282
|
if (show.includes('contributions')) {
|
|
@@ -445,88 +425,96 @@ const renderStatsCard = (
|
|
|
445
425
|
};
|
|
446
426
|
}
|
|
447
427
|
|
|
448
|
-
const isLongLocale = locale ? LONG_LOCALES.
|
|
428
|
+
const isLongLocale = locale ? LONG_LOCALES.has(locale) : false;
|
|
449
429
|
|
|
450
|
-
//
|
|
451
|
-
const
|
|
452
|
-
.filter(([key]) => !hide.includes(key))
|
|
453
|
-
.some(([, stat]) => stat.label.length > 18);
|
|
454
|
-
|
|
455
|
-
// filter out hidden stats defined by user & create the text nodes
|
|
456
|
-
const statItems = Object.entries(STATS)
|
|
457
|
-
.filter(([key]) => !hide.includes(key))
|
|
458
|
-
// pass index so that we can calculate the line spacing
|
|
459
|
-
.map(([, stat], index) =>
|
|
460
|
-
createTextNode({
|
|
461
|
-
icon: stat.icon,
|
|
462
|
-
label: stat.label,
|
|
463
|
-
value: stat.value,
|
|
464
|
-
id: stat.id,
|
|
465
|
-
unitSymbol: stat.unitSymbol,
|
|
466
|
-
index,
|
|
467
|
-
showIcons: show_icons,
|
|
468
|
-
shiftValuePos: 29.01 + (longLabels ? 50 : 0) + (isLongLocale ? 50 : 0),
|
|
469
|
-
bold: text_bold,
|
|
470
|
-
numberFormat: number_format,
|
|
471
|
-
numberPrecision: number_precision,
|
|
472
|
-
link: stat.link,
|
|
473
|
-
}),
|
|
474
|
-
);
|
|
430
|
+
// filter out hidden stats defined by user
|
|
431
|
+
const visibleStats = Object.entries(STATS).filter(([key]) => !hide.includes(key));
|
|
475
432
|
|
|
476
|
-
if (
|
|
433
|
+
if (visibleStats.length === 0 && hide_rank) {
|
|
477
434
|
throw new CardError('Could not render stats card.', {
|
|
478
435
|
code: 'invalid_param',
|
|
479
436
|
secondaryMessage: 'Either stats or rank are required.',
|
|
480
437
|
});
|
|
481
438
|
}
|
|
482
439
|
|
|
483
|
-
//
|
|
484
|
-
|
|
440
|
+
// check if all used labels are short
|
|
441
|
+
const longLabels = visibleStats.some(([, stat]) => stat.label.length > 18);
|
|
442
|
+
|
|
443
|
+
// The rank ring sets the floor: 150 beside the stats, 180 when it is the whole card.
|
|
485
444
|
const height = Math.max(
|
|
486
|
-
45 + (
|
|
487
|
-
hide_rank ? 0 :
|
|
445
|
+
45 + (visibleStats.length + 1) * lheight,
|
|
446
|
+
hide_rank ? 0 : visibleStats.length > 0 ? 150 : 180,
|
|
488
447
|
);
|
|
489
448
|
|
|
490
449
|
// the lower the user's percentile the better
|
|
491
450
|
const progress = 100 - rank.percentile;
|
|
492
451
|
|
|
493
|
-
const calculateTextWidth = () =>
|
|
494
|
-
|
|
495
|
-
custom_title
|
|
496
|
-
?
|
|
497
|
-
: statItems.length
|
|
498
|
-
? i18n.t('statcard.title')
|
|
499
|
-
: i18n.t('statcard.ranktitle'),
|
|
452
|
+
const calculateTextWidth = (): number =>
|
|
453
|
+
measureText(
|
|
454
|
+
custom_title ||
|
|
455
|
+
(visibleStats.length > 0 ? i18n.t('statcard.title') : i18n.t('statcard.ranktitle')),
|
|
500
456
|
);
|
|
501
|
-
};
|
|
502
457
|
|
|
503
458
|
/*
|
|
504
459
|
When hide_rank=true, the minimum card width is 270 px + the title length and padding.
|
|
505
460
|
When hide_rank=false, the minimum card_width is 340 px + the icon width (if show_icons=true).
|
|
506
461
|
Numbers are picked by looking at existing dimensions on production.
|
|
507
462
|
*/
|
|
508
|
-
const iconWidth = show_icons &&
|
|
463
|
+
const iconWidth = show_icons && visibleStats.length > 0 ? 16 + /* padding */ 1 : 0;
|
|
509
464
|
const minCardWidth =
|
|
510
465
|
(hide_rank
|
|
511
466
|
? clampValue(50 /* padding */ + calculateTextWidth() * 2, CARD_MIN_WIDTH, Infinity)
|
|
512
|
-
:
|
|
467
|
+
: visibleStats.length > 0
|
|
513
468
|
? RANK_CARD_MIN_WIDTH
|
|
514
469
|
: RANK_ONLY_CARD_MIN_WIDTH) + iconWidth;
|
|
515
470
|
const defaultCardWidth =
|
|
516
471
|
(hide_rank
|
|
517
472
|
? CARD_DEFAULT_WIDTH
|
|
518
|
-
:
|
|
473
|
+
: visibleStats.length > 0
|
|
519
474
|
? RANK_CARD_DEFAULT_WIDTH
|
|
520
475
|
: RANK_ONLY_CARD_DEFAULT_WIDTH) + iconWidth;
|
|
521
476
|
const width = card_width
|
|
522
|
-
? isNaN(card_width)
|
|
477
|
+
? Number.isNaN(card_width)
|
|
523
478
|
? Math.max(defaultCardWidth, minCardWidth)
|
|
524
479
|
: card_width
|
|
525
480
|
: Math.max(defaultCardWidth, minCardWidth);
|
|
526
481
|
|
|
482
|
+
// A value ends at the card's inner edge, clear of the rank ring — or right after
|
|
483
|
+
// the longest label, on a card too narrow for that.
|
|
484
|
+
const widestLabel =
|
|
485
|
+
visibleStats.length > 0
|
|
486
|
+
? Math.max(...visibleStats.map(([, stat]) => measureText(`${stat.label}:`, STAT_FONT_SIZE)))
|
|
487
|
+
: 0;
|
|
488
|
+
const valueAnchorX = Math.round(
|
|
489
|
+
Math.max(
|
|
490
|
+
width - CARD_PADDING_X - STAT_ROW_X - (hide_rank ? 0 : RANK_GUTTER),
|
|
491
|
+
(show_icons ? LABEL_X_OFFSET : 0) + widestLabel + LABEL_VALUE_GAP,
|
|
492
|
+
),
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
// pass index so that we can calculate the line spacing
|
|
496
|
+
const statItems = visibleStats.map(([, stat], index) =>
|
|
497
|
+
createTextNode({
|
|
498
|
+
icon: stat.icon,
|
|
499
|
+
label: stat.label,
|
|
500
|
+
value: stat.value,
|
|
501
|
+
id: stat.id,
|
|
502
|
+
unitSymbol: stat.unitSymbol,
|
|
503
|
+
index,
|
|
504
|
+
showIcons: show_icons,
|
|
505
|
+
shiftValuePos: 29.01 + (longLabels ? 50 : 0) + (isLongLocale ? 50 : 0),
|
|
506
|
+
valueAnchorX,
|
|
507
|
+
bold: text_bold,
|
|
508
|
+
labelBold: false,
|
|
509
|
+
numberFormat: number_format,
|
|
510
|
+
numberPrecision: number_precision,
|
|
511
|
+
link: stat.link,
|
|
512
|
+
}),
|
|
513
|
+
);
|
|
514
|
+
|
|
527
515
|
const card = new Card({
|
|
528
516
|
customTitle: custom_title,
|
|
529
|
-
defaultTitle:
|
|
517
|
+
defaultTitle: visibleStats.length > 0 ? i18n.t('statcard.title') : i18n.t('statcard.ranktitle'),
|
|
530
518
|
width,
|
|
531
519
|
height,
|
|
532
520
|
border_radius,
|
|
@@ -535,7 +523,7 @@ const renderStatsCard = (
|
|
|
535
523
|
|
|
536
524
|
card.setHideBorder(hide_border);
|
|
537
525
|
card.setHideTitle(hide_title);
|
|
538
|
-
const cardStyles = ({ ringColor, textColor, iconColor }: CardColors):
|
|
526
|
+
const cardStyles = ({ ringColor, textColor, iconColor }: CardColors): Array<CssChild> =>
|
|
539
527
|
getStyles({ ringColor, textColor, iconColor, show_icons, progress });
|
|
540
528
|
|
|
541
529
|
card.setCSS({ light: cardStyles, dark: cardStyles });
|
|
@@ -555,34 +543,33 @@ const renderStatsCard = (
|
|
|
555
543
|
* @returns Rank circle translation value.
|
|
556
544
|
*/
|
|
557
545
|
const calculateRankXTranslation = (): number => {
|
|
558
|
-
if (
|
|
546
|
+
if (visibleStats.length > 0) {
|
|
559
547
|
const minXTranslation = RANK_CARD_MIN_WIDTH + iconWidth - 70;
|
|
560
548
|
if (width > RANK_CARD_DEFAULT_WIDTH) {
|
|
561
549
|
const xMaxExpansion = minXTranslation + (450 - minCardWidth) / 2;
|
|
562
550
|
return xMaxExpansion + width - RANK_CARD_DEFAULT_WIDTH;
|
|
563
|
-
} else {
|
|
564
|
-
return minXTranslation + (width - minCardWidth) / 2;
|
|
565
551
|
}
|
|
566
|
-
|
|
567
|
-
return width / 2 + 20 - 10;
|
|
552
|
+
return minXTranslation + (width - minCardWidth) / 2;
|
|
568
553
|
}
|
|
554
|
+
return width / 2 + 20 - 10;
|
|
569
555
|
};
|
|
570
556
|
|
|
571
557
|
// Conditionally rendered elements
|
|
572
|
-
const rankCircle =
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
558
|
+
const rankCircle =
|
|
559
|
+
!hide_rank &&
|
|
560
|
+
el(
|
|
561
|
+
'g',
|
|
562
|
+
{
|
|
563
|
+
'data-testid': 'rank-circle',
|
|
564
|
+
transform: `translate(${calculateRankXTranslation()}, ${height / 2 - 50})`,
|
|
565
|
+
},
|
|
566
|
+
el('circle', { class: 'rank-circle-rim', cx: -10, cy: 8, r: 40 }),
|
|
567
|
+
el('circle', { class: 'rank-circle', cx: -10, cy: 8, r: 40 }),
|
|
568
|
+
el('g', { class: 'rank-text' }, rankIcon(rank_icon, rank.level, rank.percentile)),
|
|
569
|
+
);
|
|
582
570
|
|
|
583
571
|
// Accessibility Labels
|
|
584
|
-
const labels =
|
|
585
|
-
.filter(([key]) => !hide.includes(key))
|
|
572
|
+
const labels = visibleStats
|
|
586
573
|
.map(([key, stat]) => {
|
|
587
574
|
if (key === 'commits') {
|
|
588
575
|
return `${i18n.t('statcard.commits')} ${getTotalCommitsYearLabel(
|
|
@@ -600,16 +587,10 @@ const renderStatsCard = (
|
|
|
600
587
|
desc: labels,
|
|
601
588
|
});
|
|
602
589
|
|
|
603
|
-
return card.render(
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
items: statItems,
|
|
608
|
-
gap: lheight,
|
|
609
|
-
direction: 'column',
|
|
610
|
-
}).join('')}
|
|
611
|
-
</svg>
|
|
612
|
-
`);
|
|
590
|
+
return card.render([
|
|
591
|
+
rankCircle,
|
|
592
|
+
el('svg', { x: 0, y: 0 }, flexLayout({ items: statItems, gap: lheight, direction: 'column' })),
|
|
593
|
+
]);
|
|
613
594
|
};
|
|
614
595
|
|
|
615
596
|
export { RANK_ICONS, renderStatsCard };
|