feat: wakatime card width customization (#4458)
* add card_width to wakatime card * better handling of card_width in wakatime card * move default card_width for wakatime card, add to readme * review * review --------- Co-authored-by: Alexandr <qwerty541zxc@gmail.com>
This commit is contained in:
@@ -16,6 +16,7 @@ export default async (req, res) => {
|
||||
title_color,
|
||||
icon_color,
|
||||
hide_border,
|
||||
card_width,
|
||||
line_height,
|
||||
text_color,
|
||||
bg_color,
|
||||
@@ -90,6 +91,7 @@ export default async (req, res) => {
|
||||
custom_title,
|
||||
hide_title: parseBoolean(hide_title),
|
||||
hide_border: parseBoolean(hide_border),
|
||||
card_width: parseInt(card_width, 10),
|
||||
hide: parseArray(hide),
|
||||
line_height,
|
||||
title_color,
|
||||
|
||||
@@ -648,6 +648,7 @@ You can customize the appearance and behavior of the WakaTime stats card using t
|
||||
| --- | --- | --- | --- |
|
||||
| `hide` | Hides the languages specified from the card. | string (comma-separated values) | `null` |
|
||||
| `hide_title` | Hides the title of your card. | boolean | `false` |
|
||||
| `card_width` | Sets the card's width manually. | number | `495` |
|
||||
| `line_height` | Sets the line height between text. | integer | `25` |
|
||||
| `hide_progress` | Hides the progress bar and percentage. | boolean | `false` |
|
||||
| `custom_title` | Sets a custom title for the card. | string | `WakaTime Stats` |
|
||||
|
||||
Vendored
+1
@@ -51,6 +51,7 @@ export type TopLangOptions = CommonOptions & {
|
||||
export type WakaTimeOptions = CommonOptions & {
|
||||
hide_title: boolean;
|
||||
hide: string[];
|
||||
card_width: number;
|
||||
line_height: string;
|
||||
hide_progress: boolean;
|
||||
custom_title: string;
|
||||
|
||||
+44
-17
@@ -1,4 +1,5 @@
|
||||
// @ts-check
|
||||
|
||||
import { Card } from "../common/Card.js";
|
||||
import { createProgressNode } from "../common/createProgressNode.js";
|
||||
import { I18n } from "../common/I18n.js";
|
||||
@@ -21,6 +22,10 @@ import { createRequire } from "module";
|
||||
const require = createRequire(import.meta.url);
|
||||
const languageColors = require("../common/languageColors.json"); // now works
|
||||
|
||||
const DEFAULT_CARD_WIDTH = 495;
|
||||
const MIN_CARD_WIDTH = 250;
|
||||
const COMPACT_LAYOUT_MIN_WIDTH = 400;
|
||||
|
||||
/**
|
||||
* Creates the no coding activity SVG node.
|
||||
*
|
||||
@@ -84,22 +89,21 @@ const createCompactLangNode = ({ lang, x, y, display_format }) => {
|
||||
* @param {WakaTimeLang[]} args.langs The language objects.
|
||||
* @param {number} args.y The y position of the language node.
|
||||
* @param {"time" | "percent"} args.display_format The display format of the language node.
|
||||
* @param {number} args.card_width Width in px of the card.
|
||||
* @returns {string[]} The language text node items.
|
||||
*/
|
||||
const createLanguageTextNode = ({ langs, y, display_format }) => {
|
||||
const createLanguageTextNode = ({ langs, y, display_format, card_width }) => {
|
||||
const LEFT_X = 25;
|
||||
const RIGHT_X_BASE = 230;
|
||||
const rightOffset = (card_width - DEFAULT_CARD_WIDTH) / 2;
|
||||
const RIGHT_X = RIGHT_X_BASE + rightOffset;
|
||||
|
||||
return langs.map((lang, index) => {
|
||||
if (index % 2 === 0) {
|
||||
return createCompactLangNode({
|
||||
lang,
|
||||
x: 25,
|
||||
y: 12.5 * index + y,
|
||||
display_format,
|
||||
});
|
||||
}
|
||||
const isLeft = index % 2 === 0;
|
||||
return createCompactLangNode({
|
||||
lang,
|
||||
x: 230,
|
||||
y: 12.5 + 12.5 * index,
|
||||
x: isLeft ? LEFT_X : RIGHT_X,
|
||||
y: isLeft ? 12.5 * index + y : 12.5 + 12.5 * index,
|
||||
display_format,
|
||||
});
|
||||
});
|
||||
@@ -117,6 +121,7 @@ const createLanguageTextNode = ({ langs, y, display_format }) => {
|
||||
* @param {boolean=} args.hideProgress Whether to hide the progress bar.
|
||||
* @param {string} args.progressBarColor The color of the progress bar.
|
||||
* @param {string} args.progressBarBackgroundColor The color of the progress bar background.
|
||||
* @param {number} args.progressBarWidth The width of the progress bar.
|
||||
* @returns {string} The text SVG node.
|
||||
*/
|
||||
const createTextNode = ({
|
||||
@@ -128,6 +133,7 @@ const createTextNode = ({
|
||||
hideProgress,
|
||||
progressBarColor,
|
||||
progressBarBackgroundColor,
|
||||
progressBarWidth,
|
||||
}) => {
|
||||
const staggerDelay = (index + 3) * 150;
|
||||
|
||||
@@ -138,7 +144,7 @@ const createTextNode = ({
|
||||
y: 4,
|
||||
progress: percent,
|
||||
color: progressBarColor,
|
||||
width: 220,
|
||||
width: progressBarWidth,
|
||||
// @ts-ignore
|
||||
name: label,
|
||||
progressBarBackgroundColor,
|
||||
@@ -150,7 +156,7 @@ const createTextNode = ({
|
||||
<text class="stat bold" y="12.5" data-testid="${id}">${label}:</text>
|
||||
<text
|
||||
class="stat"
|
||||
x="${hideProgress ? 170 : 350}"
|
||||
x="${hideProgress ? 170 : 130 + progressBarWidth}"
|
||||
y="12.5"
|
||||
>${value}</text>
|
||||
${cardProgress}
|
||||
@@ -206,6 +212,24 @@ const getStyles = ({
|
||||
`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize incoming width (string or number) and clamp to minimum.
|
||||
*
|
||||
* @param {Object} args The function arguments.
|
||||
* @param {WakaTimeOptions["layout"] | undefined} args.layout The incoming layout value.
|
||||
* @param {number|undefined} args.value The incoming width value.
|
||||
* @returns {number} The normalized width value.
|
||||
*/
|
||||
const normalizeCardWidth = ({ value, layout }) => {
|
||||
if (value === undefined || value === null || isNaN(value)) {
|
||||
return DEFAULT_CARD_WIDTH;
|
||||
}
|
||||
return Math.max(
|
||||
layout === "compact" ? COMPACT_LAYOUT_MIN_WIDTH : MIN_CARD_WIDTH,
|
||||
value,
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {import('../fetchers/types').WakaTimeData} WakaTimeData
|
||||
* @typedef {import('./types').WakaTimeOptions} WakaTimeOptions
|
||||
@@ -223,6 +247,7 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
const {
|
||||
hide_title = false,
|
||||
hide_border = false,
|
||||
card_width,
|
||||
hide,
|
||||
line_height = 25,
|
||||
title_color,
|
||||
@@ -241,6 +266,8 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
disable_animations,
|
||||
} = options;
|
||||
|
||||
const normalizedWidth = normalizeCardWidth({ value: card_width, layout });
|
||||
|
||||
const shouldHideLangs = Array.isArray(hide) && hide.length > 0;
|
||||
if (shouldHideLangs) {
|
||||
const languagesToHide = new Set(hide.map((lang) => lowercaseTrim(lang)));
|
||||
@@ -289,11 +316,9 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
|
||||
let finalLayout = "";
|
||||
|
||||
let width = 440;
|
||||
|
||||
// RENDER COMPACT LAYOUT
|
||||
if (layout === "compact") {
|
||||
width = width + 50;
|
||||
let width = normalizedWidth - 5;
|
||||
height = 90 + Math.round(filteredLanguages.length / 2) * 25;
|
||||
|
||||
// progressOffset holds the previous language's width and used to offset the next language
|
||||
@@ -333,6 +358,7 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
y: 25,
|
||||
langs: filteredLanguages,
|
||||
display_format,
|
||||
card_width: normalizedWidth,
|
||||
}).join("")
|
||||
: noCodingActivityNode({
|
||||
// @ts-ignore
|
||||
@@ -360,6 +386,7 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
// @ts-ignore
|
||||
progressBarBackgroundColor: textColor,
|
||||
hideProgress: hide_progress,
|
||||
progressBarWidth: normalizedWidth - 275,
|
||||
});
|
||||
})
|
||||
: [
|
||||
@@ -392,7 +419,7 @@ const renderWakatimeCard = (stats = {}, options = { hide: [] }) => {
|
||||
const card = new Card({
|
||||
customTitle: custom_title,
|
||||
defaultTitle: titleText,
|
||||
width: 495,
|
||||
width: normalizedWidth,
|
||||
height,
|
||||
border_radius,
|
||||
colors: {
|
||||
|
||||
Reference in New Issue
Block a user