@stats-organization/github-readme-stats-core 2.1.3 → 2.1.5
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.map +1 -1
- package/build/api/gist.js +34 -1
- package/build/api/index.d.ts.map +1 -1
- package/build/api/index.js +18 -1
- package/build/api/pin.d.ts.map +1 -1
- package/build/api/pin.js +17 -1
- package/build/api/top-langs.d.ts.map +1 -1
- package/build/api/top-langs.js +36 -5
- package/build/api/wakatime.d.ts.map +1 -1
- package/build/api/wakatime.js +34 -1
- package/build/calculateRank.d.ts +12 -11
- package/build/calculateRank.d.ts.map +1 -1
- package/build/calculateRank.js +18 -18
- package/build/cards/gist.d.ts.map +1 -1
- package/build/cards/gist.js +0 -1
- package/build/cards/repo.d.ts.map +1 -1
- package/build/cards/repo.js +29 -21
- package/build/cards/stats.d.ts +4 -2
- package/build/cards/stats.d.ts.map +1 -1
- package/build/cards/stats.js +21 -9
- package/build/cards/top-languages.d.ts.map +1 -1
- package/build/cards/top-languages.js +39 -12
- package/build/cards/wakatime.d.ts.map +1 -1
- package/build/cards/wakatime.js +29 -9
- package/build/common/Card.d.ts +54 -60
- package/build/common/Card.d.ts.map +1 -1
- package/build/common/Card.js +88 -61
- package/build/common/I18n.d.ts +13 -14
- package/build/common/I18n.d.ts.map +1 -1
- package/build/common/I18n.js +13 -12
- package/build/common/color.d.ts +56 -32
- package/build/common/color.d.ts.map +1 -1
- package/build/common/color.js +87 -50
- package/build/common/config.d.ts +18 -2
- package/build/common/config.d.ts.map +1 -1
- package/build/common/config.js +17 -26
- package/build/common/constants.d.ts +2 -2
- package/build/common/constants.d.ts.map +1 -1
- package/build/common/constants.js +5 -3
- package/build/common/error.d.ts +36 -29
- package/build/common/error.d.ts.map +1 -1
- package/build/common/error.js +14 -10
- package/build/common/fmt.d.ts +14 -13
- package/build/common/fmt.d.ts.map +1 -1
- package/build/common/fmt.js +19 -15
- package/build/common/html.d.ts +2 -4
- package/build/common/html.d.ts.map +1 -1
- package/build/common/html.js +2 -6
- package/build/common/http.d.ts +13 -4
- package/build/common/http.d.ts.map +1 -1
- package/build/common/http.js +3 -4
- package/build/common/icons.d.ts +22 -21
- package/build/common/icons.d.ts.map +1 -1
- package/build/common/icons.js +4 -5
- package/build/common/languageColors.json +9 -1
- package/build/common/ops.d.ts +30 -29
- package/build/common/ops.d.ts.map +1 -1
- package/build/common/ops.js +32 -38
- package/build/common/render.d.ts +115 -111
- package/build/common/render.d.ts.map +1 -1
- package/build/common/render.js +138 -88
- package/build/common/retryer.d.ts +24 -13
- package/build/common/retryer.d.ts.map +1 -1
- package/build/common/retryer.js +22 -30
- package/build/fetchers/gist.d.ts +6 -21
- package/build/fetchers/gist.d.ts.map +1 -1
- package/build/fetchers/gist.js +30 -36
- package/build/fetchers/repo.d.ts.map +1 -1
- package/build/fetchers/repo.js +4 -7
- package/build/fetchers/stats.d.ts.map +1 -1
- package/build/fetchers/stats.js +3 -7
- package/build/fetchers/top-languages.d.ts.map +1 -1
- package/build/fetchers/top-languages.js +0 -1
- package/build/fetchers/types.d.ts +126 -0
- package/build/fetchers/types.d.ts.map +1 -0
- package/build/fetchers/types.js +1 -0
- package/build/fetchers/wakatime.d.ts.map +1 -1
- package/build/fetchers/wakatime.js +0 -1
- package/build/translations.d.ts +627 -24
- package/build/translations.d.ts.map +1 -1
- package/build/translations.js +7 -8
- package/package.json +4 -4
- package/src/_emoji-name-map.d.ts +10 -0
- package/src/api/gist.js +36 -2
- package/src/api/index.js +19 -2
- package/src/api/pin.js +18 -2
- package/src/api/top-langs.js +38 -6
- package/src/api/wakatime.js +36 -2
- package/src/{calculateRank.js → calculateRank.ts} +29 -19
- package/src/cards/gist.js +0 -2
- package/src/cards/repo.js +30 -22
- package/src/cards/stats.js +22 -10
- package/src/cards/top-languages.js +49 -13
- package/src/cards/wakatime.js +33 -10
- package/src/common/Card.ts +338 -0
- package/src/common/I18n.ts +49 -0
- package/src/common/color.ts +212 -0
- package/src/common/config.ts +83 -0
- package/src/common/constants.ts +10 -0
- package/src/common/{error.js → error.ts} +20 -14
- package/src/common/{fmt.js → fmt.ts} +27 -19
- package/src/common/{html.js → html.ts} +3 -8
- package/src/common/http.ts +31 -0
- package/src/common/{icons.js → icons.ts} +9 -7
- package/src/common/languageColors.json +9 -1
- package/src/common/{ops.js → ops.ts} +48 -52
- package/src/common/{render.js → render.ts} +208 -98
- package/src/common/retryer.ts +125 -0
- package/src/fetchers/gist.ts +136 -0
- package/src/fetchers/repo.js +4 -8
- package/src/fetchers/stats.js +3 -8
- package/src/fetchers/top-languages.js +0 -2
- package/src/fetchers/{types.d.ts → types.ts} +1 -1
- package/src/fetchers/wakatime.js +0 -2
- package/src/{translations.js → translations.ts} +14 -10
- package/src/common/Card.js +0 -275
- package/src/common/I18n.js +0 -42
- package/src/common/color.js +0 -145
- package/src/common/config.js +0 -78
- package/src/common/constants.js +0 -9
- package/src/common/http.js +0 -21
- package/src/common/retryer.js +0 -104
- package/src/fetchers/gist.js +0 -114
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { AxiosResponse } from "axios";
|
|
2
|
+
|
|
3
|
+
import { getConfig } from "./config.js";
|
|
4
|
+
import { CustomError } from "./error.js";
|
|
5
|
+
import { logger } from "./log.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Error-detection fields the retryer inspects to detect rate-limiting and credential failures.
|
|
9
|
+
* Every fetcher's payload is intersected with
|
|
10
|
+
* this, so the retryer can read `errors`/`message` regardless of the payload's own shape.
|
|
11
|
+
*/
|
|
12
|
+
interface ResponseErrors {
|
|
13
|
+
errors?: Array<{ type?: string; message?: string }>;
|
|
14
|
+
message?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns a random integer from 0 (inclusive) to `max` (exclusive).
|
|
19
|
+
*
|
|
20
|
+
* The value is generated using `Math.random()` and uniformly distributed
|
|
21
|
+
* across the range.
|
|
22
|
+
*
|
|
23
|
+
* @param max The upper bound (exclusive). Must be a positive number.
|
|
24
|
+
*
|
|
25
|
+
* @returns A random integer `n` such that `0 <= n < max`.
|
|
26
|
+
*/
|
|
27
|
+
function getRandomInt(max: number): number {
|
|
28
|
+
return Math.floor(Math.random() * max);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A fetcher's Axios response. `TData` is the shape of `response.data`,
|
|
33
|
+
* which is intersected with {@link ResponseErrors} so the retryer can inspect
|
|
34
|
+
* `errors`/`message`.
|
|
35
|
+
* Defaults to `unknown` (error fields only) for callers that don't care about the payload.
|
|
36
|
+
*/
|
|
37
|
+
type FetcherResponse<TData = unknown> = AxiosResponse<TData & ResponseErrors>;
|
|
38
|
+
|
|
39
|
+
type FetcherFunction<TData = unknown> = (
|
|
40
|
+
variables: Record<string, unknown>,
|
|
41
|
+
token: string,
|
|
42
|
+
retriesForTests?: number,
|
|
43
|
+
) => Promise<FetcherResponse<TData>>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Try to execute the fetcher function until it succeeds or the max number of retries is reached.
|
|
47
|
+
*
|
|
48
|
+
* @template TData Shape of `response.data` returned by the fetcher.
|
|
49
|
+
* @param fetcher The fetcher function.
|
|
50
|
+
* @param variables Object with arguments to pass to the fetcher function.
|
|
51
|
+
* @param pat Optional PAT override.
|
|
52
|
+
* @returns The response from the fetcher function.
|
|
53
|
+
*/
|
|
54
|
+
const retryer = async <TData = unknown>(
|
|
55
|
+
fetcher: FetcherFunction<TData>,
|
|
56
|
+
variables: Record<string, unknown>,
|
|
57
|
+
pat: string | null = null,
|
|
58
|
+
): Promise<FetcherResponse<TData>> => {
|
|
59
|
+
const PATs = pat
|
|
60
|
+
? [{ name: "user PAT from database", value: pat }]
|
|
61
|
+
: getConfig().pats;
|
|
62
|
+
|
|
63
|
+
if (!PATs.length) {
|
|
64
|
+
throw new CustomError("No GitHub API tokens found", CustomError.NO_TOKENS);
|
|
65
|
+
}
|
|
66
|
+
const startPAT = getRandomInt(PATs.length);
|
|
67
|
+
|
|
68
|
+
for (let retries = 0; retries < PATs.length; retries++) {
|
|
69
|
+
const currentPAT = PATs[(startPAT + retries) % PATs.length];
|
|
70
|
+
if (!currentPAT) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const response = await fetcher(
|
|
76
|
+
variables,
|
|
77
|
+
currentPAT.value,
|
|
78
|
+
// used in tests for faking rate limit
|
|
79
|
+
retries,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// react on both type and message-based rate-limit signals.
|
|
83
|
+
// https://github.com/anuraghazra/github-readme-stats/issues/4425
|
|
84
|
+
const errors = response.data.errors;
|
|
85
|
+
const errorType = errors?.[0]?.type;
|
|
86
|
+
const errorMsg = errors?.[0]?.message ?? "";
|
|
87
|
+
const isRateLimited =
|
|
88
|
+
(!!errors && errorType === "RATE_LIMITED") ||
|
|
89
|
+
/rate limit/i.test(errorMsg);
|
|
90
|
+
|
|
91
|
+
if (isRateLimited) {
|
|
92
|
+
logger.log(`${currentPAT.name} Failed due to rate limiting`);
|
|
93
|
+
} else {
|
|
94
|
+
return response;
|
|
95
|
+
}
|
|
96
|
+
} catch (err) {
|
|
97
|
+
const e = err as { response?: FetcherResponse<TData> };
|
|
98
|
+
|
|
99
|
+
// network/unexpected error → let caller treat as failure
|
|
100
|
+
if (!e.response) {
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// also checking for bad credentials if any tokens gets invalidated
|
|
105
|
+
const message = e.response.data.message;
|
|
106
|
+
const isBadCredential = message === "Bad credentials";
|
|
107
|
+
const isAccountSuspended =
|
|
108
|
+
message === "Sorry. Your account was suspended.";
|
|
109
|
+
|
|
110
|
+
if (isBadCredential || isAccountSuspended) {
|
|
111
|
+
logger.log(`${currentPAT.name} Failed due to bad credentials`);
|
|
112
|
+
} else {
|
|
113
|
+
// HTTP error with a response → return it for caller-side handling
|
|
114
|
+
return e.response;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
throw new CustomError(
|
|
120
|
+
"Downtime due to GitHub API rate limiting",
|
|
121
|
+
CustomError.MAX_RETRY,
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
export { retryer };
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { AxiosResponse } from "axios";
|
|
2
|
+
|
|
3
|
+
import { MissingParamError } from "../common/error.js";
|
|
4
|
+
import { request } from "../common/http.js";
|
|
5
|
+
import { retryer } from "../common/retryer.js";
|
|
6
|
+
|
|
7
|
+
import type { GistData } from "./types.js";
|
|
8
|
+
|
|
9
|
+
const QUERY = `
|
|
10
|
+
query gistInfo($gistName: String!) {
|
|
11
|
+
viewer {
|
|
12
|
+
gist(name: $gistName) {
|
|
13
|
+
description
|
|
14
|
+
owner {
|
|
15
|
+
login
|
|
16
|
+
}
|
|
17
|
+
stargazerCount
|
|
18
|
+
forks {
|
|
19
|
+
totalCount
|
|
20
|
+
}
|
|
21
|
+
files {
|
|
22
|
+
name
|
|
23
|
+
language {
|
|
24
|
+
name
|
|
25
|
+
}
|
|
26
|
+
size
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Gist data fetcher.
|
|
35
|
+
*
|
|
36
|
+
* @param variables Fetcher variables.
|
|
37
|
+
* @param token GitHub token.
|
|
38
|
+
* @returns The response.
|
|
39
|
+
*/
|
|
40
|
+
const fetcher = (
|
|
41
|
+
variables: Record<string, unknown>,
|
|
42
|
+
token: string,
|
|
43
|
+
): Promise<AxiosResponse> => {
|
|
44
|
+
return request(
|
|
45
|
+
{ query: QUERY, variables },
|
|
46
|
+
{ Authorization: `token ${token}` },
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** A single file within a gist. */
|
|
51
|
+
interface GistFile {
|
|
52
|
+
name: string;
|
|
53
|
+
language: { name: string } | null;
|
|
54
|
+
size: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Shape of `response.data` returned by the gist GraphQL query. */
|
|
58
|
+
interface GistQueryResponse {
|
|
59
|
+
data: {
|
|
60
|
+
viewer: {
|
|
61
|
+
gist: {
|
|
62
|
+
description: string | null;
|
|
63
|
+
owner: { login: string };
|
|
64
|
+
stargazerCount: number;
|
|
65
|
+
forks: { totalCount: number };
|
|
66
|
+
files: Array<GistFile>;
|
|
67
|
+
} | null;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* This function calculates the primary language of a gist by files size.
|
|
74
|
+
*
|
|
75
|
+
* @param files Files.
|
|
76
|
+
* @returns Primary language, or `null` when no file has a language.
|
|
77
|
+
*/
|
|
78
|
+
const calculatePrimaryLanguage = (files: Array<GistFile>): string | null => {
|
|
79
|
+
const languages: Record<string, number> = {};
|
|
80
|
+
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
if (file.language) {
|
|
83
|
+
languages[file.language.name] =
|
|
84
|
+
(languages[file.language.name] ?? 0) + file.size;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let primaryLanguage: string | null = null;
|
|
89
|
+
let maxSize = -1;
|
|
90
|
+
for (const [language, size] of Object.entries(languages)) {
|
|
91
|
+
if (size > maxSize) {
|
|
92
|
+
maxSize = size;
|
|
93
|
+
primaryLanguage = language;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return primaryLanguage;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Fetch GitHub gist information by given username and ID.
|
|
102
|
+
*
|
|
103
|
+
* @param id GitHub gist ID.
|
|
104
|
+
* @param pat Optional PAT override.
|
|
105
|
+
* @returns Gist data.
|
|
106
|
+
*/
|
|
107
|
+
const fetchGist = async (
|
|
108
|
+
id: string,
|
|
109
|
+
pat: string | null = null,
|
|
110
|
+
): Promise<GistData> => {
|
|
111
|
+
if (!id) {
|
|
112
|
+
throw new MissingParamError(["id"], "/api/gist?id=GIST_ID");
|
|
113
|
+
}
|
|
114
|
+
const res = await retryer<GistQueryResponse>(fetcher, { gistName: id }, pat);
|
|
115
|
+
if (res.data.errors) {
|
|
116
|
+
throw new Error(res.data.errors[0]?.message);
|
|
117
|
+
}
|
|
118
|
+
const gist = res.data.data.viewer.gist;
|
|
119
|
+
if (!gist) {
|
|
120
|
+
throw new Error("Gist not found");
|
|
121
|
+
}
|
|
122
|
+
const firstFile = gist.files[0];
|
|
123
|
+
if (!firstFile) {
|
|
124
|
+
throw new Error("Gist has no files");
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
name: firstFile.name,
|
|
128
|
+
nameWithOwner: `${gist.owner.login}/${firstFile.name}`,
|
|
129
|
+
description: gist.description,
|
|
130
|
+
language: calculatePrimaryLanguage(gist.files),
|
|
131
|
+
starsCount: gist.stargazerCount,
|
|
132
|
+
forksCount: gist.forks.totalCount,
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export { fetchGist };
|
package/src/fetchers/repo.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { MissingParamError } from "../common/error.js";
|
|
4
2
|
import { request } from "../common/http.js";
|
|
5
3
|
import { retryer } from "../common/retryer.js";
|
|
@@ -23,9 +21,7 @@ const fetcher = (variables, token) => {
|
|
|
23
21
|
isPrivate
|
|
24
22
|
isArchived
|
|
25
23
|
isTemplate
|
|
26
|
-
|
|
27
|
-
totalCount
|
|
28
|
-
}
|
|
24
|
+
stargazerCount
|
|
29
25
|
description
|
|
30
26
|
primaryLanguage {
|
|
31
27
|
color
|
|
@@ -55,7 +51,7 @@ const fetcher = (variables, token) => {
|
|
|
55
51
|
);
|
|
56
52
|
};
|
|
57
53
|
|
|
58
|
-
const urlExample = "/api/pin?username=USERNAME&
|
|
54
|
+
const urlExample = "/api/pin?username=USERNAME&repo=REPO_NAME";
|
|
59
55
|
|
|
60
56
|
/**
|
|
61
57
|
* @typedef {import("./types").RepositoryData} RepositoryData Repository data.
|
|
@@ -130,7 +126,7 @@ const fetchRepo = async (
|
|
|
130
126
|
return {
|
|
131
127
|
...repoUserStats,
|
|
132
128
|
...data.user.repository,
|
|
133
|
-
starCount: data.user.repository.
|
|
129
|
+
starCount: data.user.repository.stargazerCount,
|
|
134
130
|
};
|
|
135
131
|
}
|
|
136
132
|
|
|
@@ -155,7 +151,7 @@ const fetchRepo = async (
|
|
|
155
151
|
return {
|
|
156
152
|
...repoUserStats,
|
|
157
153
|
...data.organization.repository,
|
|
158
|
-
starCount: data.organization.repository.
|
|
154
|
+
starCount: data.organization.repository.stargazerCount,
|
|
159
155
|
};
|
|
160
156
|
}
|
|
161
157
|
|
package/src/fetchers/stats.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import axios from "axios";
|
|
4
2
|
import githubUsernameRegex from "github-username-regex";
|
|
5
3
|
|
|
@@ -18,9 +16,7 @@ const GRAPHQL_REPOS_FIELD = `
|
|
|
18
16
|
totalCount
|
|
19
17
|
nodes {
|
|
20
18
|
name
|
|
21
|
-
|
|
22
|
-
totalCount
|
|
23
|
-
}
|
|
19
|
+
stargazerCount
|
|
24
20
|
}
|
|
25
21
|
pageInfo {
|
|
26
22
|
hasNextPage
|
|
@@ -156,10 +152,9 @@ const statsFetcher = async ({
|
|
|
156
152
|
stats = res;
|
|
157
153
|
}
|
|
158
154
|
|
|
159
|
-
// Disable multi page fetching on public Vercel instance due to rate limits.
|
|
160
155
|
fetchedPages++;
|
|
161
156
|
const repoNodesWithStars = repoNodes.filter(
|
|
162
|
-
(node) => node.
|
|
157
|
+
(node) => node.stargazerCount !== 0,
|
|
163
158
|
);
|
|
164
159
|
|
|
165
160
|
hasNextPage =
|
|
@@ -458,7 +453,7 @@ const fetchStats = async (
|
|
|
458
453
|
return !repoToHide.has(data.name);
|
|
459
454
|
})
|
|
460
455
|
.reduce((prev, curr) => {
|
|
461
|
-
return prev + curr.
|
|
456
|
+
return prev + curr.stargazerCount;
|
|
462
457
|
}, 0);
|
|
463
458
|
|
|
464
459
|
stats.rank = calculateRank({
|
package/src/fetchers/wakatime.js
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
1
|
import { encodeHTML } from "./common/html.js";
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* Retrieves stat card labels in the available locales.
|
|
7
5
|
*
|
|
8
|
-
* @param
|
|
9
|
-
* @param
|
|
10
|
-
* @param
|
|
11
|
-
* @returns
|
|
6
|
+
* @param props Function arguments.
|
|
7
|
+
* @param props.name The name of the locale.
|
|
8
|
+
* @param props.apostrophe Whether to use apostrophe or not.
|
|
9
|
+
* @returns The locales object.
|
|
12
10
|
*
|
|
13
11
|
* @see https://www.andiamo.co.uk/resources/iso-language-codes/ for language codes.
|
|
14
12
|
*/
|
|
15
|
-
const statCardLocales = ({
|
|
13
|
+
const statCardLocales = ({
|
|
14
|
+
name,
|
|
15
|
+
apostrophe,
|
|
16
|
+
}: {
|
|
17
|
+
name: string;
|
|
18
|
+
apostrophe: string;
|
|
19
|
+
}) => {
|
|
16
20
|
const encodedName = encodeHTML(name);
|
|
17
21
|
return {
|
|
18
22
|
"statcard.title": {
|
|
@@ -1140,10 +1144,10 @@ const availableLocales = Object.keys(repoCardLocales["repocard.archived"]);
|
|
|
1140
1144
|
/**
|
|
1141
1145
|
* Checks whether the locale is available or not.
|
|
1142
1146
|
*
|
|
1143
|
-
* @param
|
|
1144
|
-
* @returns
|
|
1147
|
+
* @param locale The locale to check.
|
|
1148
|
+
* @returns Boolean specifying whether the locale is available or not.
|
|
1145
1149
|
*/
|
|
1146
|
-
const isLocaleAvailable = (locale) => {
|
|
1150
|
+
const isLocaleAvailable = (locale: string): boolean => {
|
|
1147
1151
|
return availableLocales.includes(locale.toLowerCase());
|
|
1148
1152
|
};
|
|
1149
1153
|
|
package/src/common/Card.js
DELETED
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
import { encodeHTML } from "./html.js";
|
|
4
|
-
import { flexLayout } from "./render.js";
|
|
5
|
-
|
|
6
|
-
class Card {
|
|
7
|
-
/**
|
|
8
|
-
* Creates a new card instance.
|
|
9
|
-
*
|
|
10
|
-
* @param {object} args Card arguments.
|
|
11
|
-
* @param {number=} args.width Card width.
|
|
12
|
-
* @param {number=} args.height Card height.
|
|
13
|
-
* @param {number=} args.border_radius Card border radius.
|
|
14
|
-
* @param {string=} args.customTitle Card custom title.
|
|
15
|
-
* @param {string=} args.defaultTitle Card default title.
|
|
16
|
-
* @param {string=} args.titlePrefixIcon Card title prefix icon.
|
|
17
|
-
* @param {object} [args.colors={}] Card colors arguments.
|
|
18
|
-
* @param {string=} args.colors.titleColor Card title color.
|
|
19
|
-
* @param {string=} args.colors.textColor Card text color.
|
|
20
|
-
* @param {string=} args.colors.iconColor Card icon color.
|
|
21
|
-
* @param {string|string[]=} args.colors.bgColor Card background color.
|
|
22
|
-
* @param {string=} args.colors.borderColor Card border color.
|
|
23
|
-
*/
|
|
24
|
-
constructor({
|
|
25
|
-
width = 100,
|
|
26
|
-
height = 100,
|
|
27
|
-
border_radius = 4.5,
|
|
28
|
-
colors = {},
|
|
29
|
-
customTitle,
|
|
30
|
-
defaultTitle = "",
|
|
31
|
-
titlePrefixIcon,
|
|
32
|
-
}) {
|
|
33
|
-
this.width = width;
|
|
34
|
-
this.height = height;
|
|
35
|
-
|
|
36
|
-
this.hideBorder = false;
|
|
37
|
-
this.hideTitle = false;
|
|
38
|
-
|
|
39
|
-
this.border_radius = border_radius;
|
|
40
|
-
|
|
41
|
-
// returns theme based colors with proper overrides and defaults
|
|
42
|
-
this.colors = colors;
|
|
43
|
-
this.title =
|
|
44
|
-
customTitle === undefined
|
|
45
|
-
? encodeHTML(defaultTitle)
|
|
46
|
-
: encodeHTML(customTitle);
|
|
47
|
-
|
|
48
|
-
this.css = "";
|
|
49
|
-
|
|
50
|
-
this.paddingX = 25;
|
|
51
|
-
this.paddingY = 35;
|
|
52
|
-
this.titlePrefixIcon = titlePrefixIcon;
|
|
53
|
-
this.animations = true;
|
|
54
|
-
this.a11yTitle = "";
|
|
55
|
-
this.a11yDesc = "";
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* @returns {void}
|
|
60
|
-
*/
|
|
61
|
-
disableAnimations() {
|
|
62
|
-
this.animations = false;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @param {Object} props The props object.
|
|
67
|
-
* @param {string} props.title Accessibility title.
|
|
68
|
-
* @param {string} props.desc Accessibility description.
|
|
69
|
-
* @returns {void}
|
|
70
|
-
*/
|
|
71
|
-
setAccessibilityLabel({ title, desc }) {
|
|
72
|
-
this.a11yTitle = title;
|
|
73
|
-
this.a11yDesc = desc;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* @param {string} value The CSS to add to the card.
|
|
78
|
-
* @returns {void}
|
|
79
|
-
*/
|
|
80
|
-
setCSS(value) {
|
|
81
|
-
this.css = value;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* @param {boolean} value Whether to hide the border or not.
|
|
86
|
-
* @returns {void}
|
|
87
|
-
*/
|
|
88
|
-
setHideBorder(value) {
|
|
89
|
-
this.hideBorder = value;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* @param {boolean} value Whether to hide the title or not.
|
|
94
|
-
* @returns {void}
|
|
95
|
-
*/
|
|
96
|
-
setHideTitle(value) {
|
|
97
|
-
this.hideTitle = value;
|
|
98
|
-
if (value) {
|
|
99
|
-
this.height -= 30;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* @param {string} text The title to set.
|
|
105
|
-
* @returns {void}
|
|
106
|
-
*/
|
|
107
|
-
setTitle(text) {
|
|
108
|
-
this.title = text;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* @returns {string} The rendered card title.
|
|
113
|
-
*/
|
|
114
|
-
renderTitle() {
|
|
115
|
-
const titleText = `
|
|
116
|
-
<text
|
|
117
|
-
x="0"
|
|
118
|
-
y="0"
|
|
119
|
-
class="header"
|
|
120
|
-
data-testid="header"
|
|
121
|
-
>${this.title}</text>
|
|
122
|
-
`;
|
|
123
|
-
|
|
124
|
-
const prefixIcon = `
|
|
125
|
-
<svg
|
|
126
|
-
class="icon"
|
|
127
|
-
x="0"
|
|
128
|
-
y="-13"
|
|
129
|
-
viewBox="0 0 16 16"
|
|
130
|
-
version="1.1"
|
|
131
|
-
width="16"
|
|
132
|
-
height="16"
|
|
133
|
-
>
|
|
134
|
-
${this.titlePrefixIcon}
|
|
135
|
-
</svg>
|
|
136
|
-
`;
|
|
137
|
-
return `
|
|
138
|
-
<g
|
|
139
|
-
data-testid="card-title"
|
|
140
|
-
transform="translate(${this.paddingX}, ${this.paddingY})"
|
|
141
|
-
>
|
|
142
|
-
${flexLayout({
|
|
143
|
-
items: [this.titlePrefixIcon ? prefixIcon : "", titleText],
|
|
144
|
-
gap: 25,
|
|
145
|
-
}).join("")}
|
|
146
|
-
</g>
|
|
147
|
-
`;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* @returns {string} The rendered card gradient.
|
|
152
|
-
*/
|
|
153
|
-
renderGradient() {
|
|
154
|
-
if (typeof this.colors.bgColor !== "object") {
|
|
155
|
-
return "";
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const gradients = this.colors.bgColor.slice(1);
|
|
159
|
-
return typeof this.colors.bgColor === "object"
|
|
160
|
-
? `
|
|
161
|
-
<defs>
|
|
162
|
-
<linearGradient
|
|
163
|
-
id="gradient"
|
|
164
|
-
gradientTransform="rotate(${this.colors.bgColor[0]})"
|
|
165
|
-
gradientUnits="userSpaceOnUse"
|
|
166
|
-
>
|
|
167
|
-
${gradients.map((grad, index) => {
|
|
168
|
-
let offset = (index * 100) / (gradients.length - 1);
|
|
169
|
-
return `<stop offset="${offset}%" stop-color="#${grad}" />`;
|
|
170
|
-
})}
|
|
171
|
-
</linearGradient>
|
|
172
|
-
</defs>
|
|
173
|
-
`
|
|
174
|
-
: "";
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Retrieves css animations for a card.
|
|
179
|
-
*
|
|
180
|
-
* @returns {string} Animation css.
|
|
181
|
-
*/
|
|
182
|
-
getAnimations = () => {
|
|
183
|
-
return `
|
|
184
|
-
/* Animations */
|
|
185
|
-
@keyframes scaleInAnimation {
|
|
186
|
-
from {
|
|
187
|
-
transform: translate(-5px, 5px) scale(0);
|
|
188
|
-
}
|
|
189
|
-
to {
|
|
190
|
-
transform: translate(-5px, 5px) scale(1);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
@keyframes fadeInAnimation {
|
|
194
|
-
from {
|
|
195
|
-
opacity: 0;
|
|
196
|
-
}
|
|
197
|
-
to {
|
|
198
|
-
opacity: 1;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
`;
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* @param {string} body The inner body of the card.
|
|
206
|
-
* @returns {string} The rendered card.
|
|
207
|
-
*/
|
|
208
|
-
render(body) {
|
|
209
|
-
return `
|
|
210
|
-
<svg
|
|
211
|
-
width="${this.width}"
|
|
212
|
-
height="${this.height}"
|
|
213
|
-
viewBox="0 0 ${this.width} ${this.height}"
|
|
214
|
-
fill="none"
|
|
215
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
216
|
-
role="img"
|
|
217
|
-
aria-labelledby="descId"
|
|
218
|
-
>
|
|
219
|
-
<title id="titleId">${this.a11yTitle}</title>
|
|
220
|
-
<desc id="descId">${this.a11yDesc}</desc>
|
|
221
|
-
<style>
|
|
222
|
-
.header {
|
|
223
|
-
font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif;
|
|
224
|
-
fill: ${this.colors.titleColor};
|
|
225
|
-
animation: fadeInAnimation 0.8s ease-in-out forwards;
|
|
226
|
-
}
|
|
227
|
-
@supports(-moz-appearance: auto) {
|
|
228
|
-
/* Selector detects Firefox */
|
|
229
|
-
.header { font-size: 15.5px; }
|
|
230
|
-
}
|
|
231
|
-
${this.css}
|
|
232
|
-
|
|
233
|
-
${this.getAnimations()}
|
|
234
|
-
${
|
|
235
|
-
this.animations === false
|
|
236
|
-
? `* { animation-duration: 0s !important; animation-delay: 0s !important; }`
|
|
237
|
-
: ""
|
|
238
|
-
}
|
|
239
|
-
</style>
|
|
240
|
-
|
|
241
|
-
${this.renderGradient()}
|
|
242
|
-
|
|
243
|
-
<rect
|
|
244
|
-
data-testid="card-bg"
|
|
245
|
-
x="0.5"
|
|
246
|
-
y="0.5"
|
|
247
|
-
rx="${this.border_radius}"
|
|
248
|
-
height="99%"
|
|
249
|
-
stroke="${this.colors.borderColor}"
|
|
250
|
-
width="${this.width - 1}"
|
|
251
|
-
fill="${
|
|
252
|
-
typeof this.colors.bgColor === "object"
|
|
253
|
-
? "url(#gradient)"
|
|
254
|
-
: this.colors.bgColor
|
|
255
|
-
}"
|
|
256
|
-
stroke-opacity="${this.hideBorder ? 0 : 1}"
|
|
257
|
-
/>
|
|
258
|
-
|
|
259
|
-
${this.hideTitle ? "" : this.renderTitle()}
|
|
260
|
-
|
|
261
|
-
<g
|
|
262
|
-
data-testid="main-card-body"
|
|
263
|
-
transform="translate(0, ${
|
|
264
|
-
this.hideTitle ? this.paddingX : this.paddingY + 20
|
|
265
|
-
})"
|
|
266
|
-
>
|
|
267
|
-
${body}
|
|
268
|
-
</g>
|
|
269
|
-
</svg>
|
|
270
|
-
`;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export { Card };
|
|
275
|
-
export default Card;
|