Skip to content

Commit

Permalink
feat(layout): improve flexLayout & fixed layout overlaps (anuraghazra…
Browse files Browse the repository at this point in the history
…#1314)

* feat(layout): improve flexLayout & fixed layout overlaps

* chore: fix vercel build
  • Loading branch information
anuraghazra authored and Kab1r committed Apr 4, 2022
1 parent fefe0f1 commit f7c8a03
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 111 deletions.
55 changes: 26 additions & 29 deletions src/cards/repo-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const {
getCardColors,
flexLayout,
wrapTextMultiline,
measureText,
} = require("../common/utils");
const I18n = require("../common/I18n");
const Card = require("../common/Card");
Expand Down Expand Up @@ -61,20 +62,15 @@ const renderRepoCard = (repo, options = {}) => {
});

// returns theme based colors with proper overrides and defaults
const {
titleColor,
textColor,
iconColor,
bgColor,
borderColor,
} = getCardColors({
title_color,
icon_color,
text_color,
bg_color,
border_color,
theme,
});
const { titleColor, textColor, iconColor, bgColor, borderColor } =
getCardColors({
title_color,
icon_color,
text_color,
bg_color,
border_color,
theme,
});

const totalStars = kFormatter(stargazers.totalCount);
const totalForks = kFormatter(forkCount);
Expand All @@ -96,30 +92,38 @@ const renderRepoCard = (repo, options = {}) => {

const svgLanguage = primaryLanguage
? `
<g data-testid="primary-lang" transform="translate(30, 0)">
<g data-testid="primary-lang">
<circle data-testid="lang-color" cx="0" cy="-5" r="6" fill="${langColor}" />
<text data-testid="lang-name" class="gray" x="15">${langName}</text>
</g>
`
: "";

const iconSize = 16;
const iconWithLabel = (icon, label, testid) => {
return `
<svg class="icon" y="-12" viewBox="0 0 16 16" version="1.1" width="16" height="16">
const iconSvg = `
<svg class="icon" y="-12" viewBox="0 0 16 16" version="1.1" width="${iconSize}" height="${iconSize}">
${icon}
</svg>
<text data-testid="${testid}" class="gray" x="25">${label}</text>
`;
const text = `<text data-testid="${testid}" class="gray">${label}</text>`;
return flexLayout({ items: [iconSvg, text], gap: 20 }).join("");
};

const svgStars =
stargazers.totalCount > 0 &&
iconWithLabel(icons.star, totalStars, "stargazers");
const svgForks =
forkCount > 0 && iconWithLabel(icons.fork, totalForks, "forkcount");

const starAndForkCount = flexLayout({
items: [svgStars, svgForks],
gap: 65,
items: [svgLanguage, svgStars, svgForks],
sizes: [
measureText(langName, 12),
iconSize + measureText(`${totalStars}`, 12),
iconSize + measureText(`${totalForks}`, 12),
],
gap: 25,
}).join("");

const card = new Card({
Expand Down Expand Up @@ -163,15 +167,8 @@ const renderRepoCard = (repo, options = {}) => {
.join("")}
</text>
<g transform="translate(0, ${height - 75})">
${svgLanguage}
<g
data-testid="star-fork-group"
transform="translate(${primaryLanguage ? 155 - shiftText : 25}, 0)"
>
${starAndForkCount}
</g>
<g transform="translate(30, ${height - 75})">
${starAndForkCount}
</g>
`);
};
Expand Down
63 changes: 40 additions & 23 deletions src/cards/top-languages-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ const {
getCardColors,
flexLayout,
lowercaseTrim,
measureText,
chunkArray,
} = require("../common/utils");

const DEFAULT_CARD_WIDTH = 300;
Expand All @@ -33,12 +35,12 @@ const createProgressTextNode = ({ width, color, name, progress }) => {
`;
};

const createCompactLangNode = ({ lang, totalSize, x, y }) => {
const createCompactLangNode = ({ lang, totalSize }) => {
const percentage = ((lang.size / totalSize) * 100).toFixed(2);
const color = lang.color || "#858585";

return `
<g transform="translate(${x}, ${y})">
<g>
<circle cx="5" cy="6" r="5" fill="${color}" />
<text data-testid="lang-name" x="15" y="10" class='lang-name'>
${lang.name} ${percentage}%
Expand All @@ -47,25 +49,38 @@ const createCompactLangNode = ({ lang, totalSize, x, y }) => {
`;
};

const createLanguageTextNode = ({ langs, totalSize, x, y }) => {
return langs.map((lang, index) => {
if (index % 2 === 0) {
return createCompactLangNode({
const getLongestLang = (arr) =>
arr.reduce(
(savedLang, lang) =>
lang.name.length > savedLang.name.length ? lang : savedLang,
{ name: "" },
);

const createLanguageTextNode = ({ langs, totalSize }) => {
const longestLang = getLongestLang(langs);
const chunked = chunkArray(langs, langs.length / 2);
const layouts = chunked.map((array) => {
const items = array.map((lang, index) =>
createCompactLangNode({
lang,
x,
y: 12.5 * index + y,
totalSize,
index,
});
}
return createCompactLangNode({
lang,
x: 150,
y: 12.5 + 12.5 * index,
totalSize,
index,
});
}),
);
return flexLayout({
items,
gap: 25,
direction: "column",
}).join("");
});

const percent = ((longestLang.size / totalSize) * 100).toFixed(2);
const minGap = 150;
const maxGap = 20 + measureText(`${longestLang.name} ${percent}%`, 11);
return flexLayout({
items: layouts,
gap: maxGap < minGap ? minGap : maxGap,
}).join("");
};

/**
Expand Down Expand Up @@ -132,12 +147,14 @@ const renderCompactLayout = (langs, width, totalLanguageSize) => {
<rect x="0" y="0" width="${offsetWidth}" height="8" fill="white" rx="5" />
</mask>
${compactProgressBar}
${createLanguageTextNode({
x: 0,
y: 25,
langs,
totalSize: totalLanguageSize,
}).join("")}
<g transform="translate(0, 25)">
${createLanguageTextNode({
langs,
totalSize: totalLanguageSize,
width,
})}
</g>
`;
};

Expand Down
36 changes: 31 additions & 5 deletions src/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,26 @@ function request(data, headers) {

/**
*
* @param {String[]} items
* @param {string[]} items
* @param {Number} gap
* @param {string} direction
* @param {"column" | "row"} direction
*
* @returns {string[]}
*
* @description
* Auto layout utility, allows us to layout things
* vertically or horizontally with proper gaping
*/
function flexLayout({ items, gap, direction }) {
function flexLayout({ items, gap, direction, sizes = [] }) {
let lastSize = 0;
// filter() for filtering out empty strings
return items.filter(Boolean).map((item, i) => {
let transform = `translate(${gap * i}, 0)`;
const size = sizes[i] || 0;
let transform = `translate(${lastSize}, 0)`;
if (direction === "column") {
transform = `translate(0, ${gap * i})`;
transform = `translate(0, ${lastSize})`;
}
lastSize += size + gap;
return `<g transform="${transform}">${item}</g>`;
});
}
Expand Down Expand Up @@ -232,6 +237,26 @@ function measureText(str, fontSize = 10) {
}
const lowercaseTrim = (name) => name.toLowerCase().trim();

/**
* @template T
* @param {Array<T>} arr
* @param {number} perChunk
* @returns {Array<T>}
*/
function chunkArray(arr, perChunk) {
return arr.reduce((resultArray, item, index) => {
const chunkIndex = Math.floor(index / perChunk);

if (!resultArray[chunkIndex]) {
resultArray[chunkIndex] = []; // start a new chunk
}

resultArray[chunkIndex].push(item);

return resultArray;
}, []);
}

module.exports = {
renderError,
kFormatter,
Expand All @@ -250,4 +275,5 @@ module.exports = {
CONSTANTS,
CustomError,
lowercaseTrim,
chunkArray,
};
46 changes: 46 additions & 0 deletions tests/flexLayout.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const { flexLayout } = require("../src/common/utils");

describe("flexLayout", () => {
it("should work with row & col layouts", () => {
const layout = flexLayout({
items: ["<text>1</text>", "<text>2</text>"],
gap: 60,
});

expect(layout).toStrictEqual([
`<g transform="translate(0, 0)"><text>1</text></g>`,
`<g transform="translate(60, 0)"><text>2</text></g>`,
]);

const columns = flexLayout({
items: ["<text>1</text>", "<text>2</text>"],
gap: 60,
direction: "column",
});

expect(columns).toStrictEqual([
`<g transform="translate(0, 0)"><text>1</text></g>`,
`<g transform="translate(0, 60)"><text>2</text></g>`,
]);
});

it("should work with sizes", () => {
const layout = flexLayout({
items: [
"<text>1</text>",
"<text>2</text>",
"<text>3</text>",
"<text>4</text>",
],
gap: 20,
sizes: [200, 100, 55, 25],
});

expect(layout).toStrictEqual([
`<g transform="translate(0, 0)"><text>1</text></g>`,
`<g transform="translate(220, 0)"><text>2</text></g>`,
`<g transform="translate(340, 0)"><text>3</text></g>`,
`<g transform="translate(415, 0)"><text>4</text></g>`,
]);
});
});
38 changes: 5 additions & 33 deletions tests/renderRepoCard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,36 +89,6 @@ describe("Test renderRepoCard", () => {
);
});

it("should shift the text position depending on language length", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
primaryLanguage: {
...data_repo.repository.primaryLanguage,
name: "Jupyter Notebook",
},
});

expect(queryByTestId(document.body, "primary-lang")).toBeInTheDocument();
expect(queryByTestId(document.body, "star-fork-group")).toHaveAttribute(
"transform",
"translate(155, 0)",
);

// Small lang
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
primaryLanguage: {
...data_repo.repository.primaryLanguage,
name: "Ruby",
},
});

expect(queryByTestId(document.body, "star-fork-group")).toHaveAttribute(
"transform",
"translate(125, 0)",
);
});

it("should hide language if primaryLanguage is null & fallback to correct values", () => {
document.body.innerHTML = renderRepoCard({
...data_repo.repository,
Expand Down Expand Up @@ -332,11 +302,13 @@ describe("Test renderRepoCard", () => {
);
expect(queryByTestId(document.body, "badge")).toHaveTextContent("模板");
});

it("should render without rounding", () => {
document.body.innerHTML = renderRepoCard(data_repo.repository, { border_radius: "0" });
document.body.innerHTML = renderRepoCard(data_repo.repository, {
border_radius: "0",
});
expect(document.querySelector("rect")).toHaveAttribute("rx", "0");
document.body.innerHTML = renderRepoCard(data_repo.repository, { });
document.body.innerHTML = renderRepoCard(data_repo.repository, {});
expect(document.querySelector("rect")).toHaveAttribute("rx", "4.5");
});
});
21 changes: 0 additions & 21 deletions tests/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,27 +44,6 @@ describe("Test utils.js", () => {
).toHaveTextContent(/Secondary Message/gim);
});

it("should test flexLayout", () => {
const layout = flexLayout({
items: ["<text>1</text>", "<text>2</text>"],
gap: 60,
}).join("");

expect(layout).toBe(
`<g transform=\"translate(0, 0)\"><text>1</text></g><g transform=\"translate(60, 0)\"><text>2</text></g>`,
);

const columns = flexLayout({
items: ["<text>1</text>", "<text>2</text>"],
gap: 60,
direction: "column",
}).join("");

expect(columns).toBe(
`<g transform=\"translate(0, 0)\"><text>1</text></g><g transform=\"translate(0, 60)\"><text>2</text></g>`,
);
});

it("getCardColors: should return expected values", () => {
let colors = getCardColors({
title_color: "f00",
Expand Down

0 comments on commit f7c8a03

Please sign in to comment.