This PR mainly refactors the code by extracting a "core" package which is then used by the "backend" and "frontend" apps. Apart from this the PR also contains several smaller changes like fixing dependabot, upgrading dependencies, aligned "package.json" files, added tests, ... addresses step 1 of #32 closes #136 --------- Co-authored-by: Marco Pasqualetti <marco.pasqualetti@live.com>
276 lines
6.4 KiB
JavaScript
276 lines
6.4 KiB
JavaScript
// @ts-check
|
|
|
|
import { encodeHTML } from "./html.js";
|
|
import { flexLayout } from "./render.js";
|
|
|
|
class Card {
|
|
/**
|
|
* Creates a new card instance.
|
|
*
|
|
* @param {object} args Card arguments.
|
|
* @param {number=} args.width Card width.
|
|
* @param {number=} args.height Card height.
|
|
* @param {number=} args.border_radius Card border radius.
|
|
* @param {string=} args.customTitle Card custom title.
|
|
* @param {string=} args.defaultTitle Card default title.
|
|
* @param {string=} args.titlePrefixIcon Card title prefix icon.
|
|
* @param {object} [args.colors={}] Card colors arguments.
|
|
* @param {string=} args.colors.titleColor Card title color.
|
|
* @param {string=} args.colors.textColor Card text color.
|
|
* @param {string=} args.colors.iconColor Card icon color.
|
|
* @param {string|string[]=} args.colors.bgColor Card background color.
|
|
* @param {string=} args.colors.borderColor Card border color.
|
|
*/
|
|
constructor({
|
|
width = 100,
|
|
height = 100,
|
|
border_radius = 4.5,
|
|
colors = {},
|
|
customTitle,
|
|
defaultTitle = "",
|
|
titlePrefixIcon,
|
|
}) {
|
|
this.width = width;
|
|
this.height = height;
|
|
|
|
this.hideBorder = false;
|
|
this.hideTitle = false;
|
|
|
|
this.border_radius = border_radius;
|
|
|
|
// returns theme based colors with proper overrides and defaults
|
|
this.colors = colors;
|
|
this.title =
|
|
customTitle === undefined
|
|
? encodeHTML(defaultTitle)
|
|
: encodeHTML(customTitle);
|
|
|
|
this.css = "";
|
|
|
|
this.paddingX = 25;
|
|
this.paddingY = 35;
|
|
this.titlePrefixIcon = titlePrefixIcon;
|
|
this.animations = true;
|
|
this.a11yTitle = "";
|
|
this.a11yDesc = "";
|
|
}
|
|
|
|
/**
|
|
* @returns {void}
|
|
*/
|
|
disableAnimations() {
|
|
this.animations = false;
|
|
}
|
|
|
|
/**
|
|
* @param {Object} props The props object.
|
|
* @param {string} props.title Accessibility title.
|
|
* @param {string} props.desc Accessibility description.
|
|
* @returns {void}
|
|
*/
|
|
setAccessibilityLabel({ title, desc }) {
|
|
this.a11yTitle = title;
|
|
this.a11yDesc = desc;
|
|
}
|
|
|
|
/**
|
|
* @param {string} value The CSS to add to the card.
|
|
* @returns {void}
|
|
*/
|
|
setCSS(value) {
|
|
this.css = value;
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} value Whether to hide the border or not.
|
|
* @returns {void}
|
|
*/
|
|
setHideBorder(value) {
|
|
this.hideBorder = value;
|
|
}
|
|
|
|
/**
|
|
* @param {boolean} value Whether to hide the title or not.
|
|
* @returns {void}
|
|
*/
|
|
setHideTitle(value) {
|
|
this.hideTitle = value;
|
|
if (value) {
|
|
this.height -= 30;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} text The title to set.
|
|
* @returns {void}
|
|
*/
|
|
setTitle(text) {
|
|
this.title = text;
|
|
}
|
|
|
|
/**
|
|
* @returns {string} The rendered card title.
|
|
*/
|
|
renderTitle() {
|
|
const titleText = `
|
|
<text
|
|
x="0"
|
|
y="0"
|
|
class="header"
|
|
data-testid="header"
|
|
>${this.title}</text>
|
|
`;
|
|
|
|
const prefixIcon = `
|
|
<svg
|
|
class="icon"
|
|
x="0"
|
|
y="-13"
|
|
viewBox="0 0 16 16"
|
|
version="1.1"
|
|
width="16"
|
|
height="16"
|
|
>
|
|
${this.titlePrefixIcon}
|
|
</svg>
|
|
`;
|
|
return `
|
|
<g
|
|
data-testid="card-title"
|
|
transform="translate(${this.paddingX}, ${this.paddingY})"
|
|
>
|
|
${flexLayout({
|
|
items: [this.titlePrefixIcon ? prefixIcon : "", titleText],
|
|
gap: 25,
|
|
}).join("")}
|
|
</g>
|
|
`;
|
|
}
|
|
|
|
/**
|
|
* @returns {string} The rendered card gradient.
|
|
*/
|
|
renderGradient() {
|
|
if (typeof this.colors.bgColor !== "object") {
|
|
return "";
|
|
}
|
|
|
|
const gradients = this.colors.bgColor.slice(1);
|
|
return typeof this.colors.bgColor === "object"
|
|
? `
|
|
<defs>
|
|
<linearGradient
|
|
id="gradient"
|
|
gradientTransform="rotate(${this.colors.bgColor[0]})"
|
|
gradientUnits="userSpaceOnUse"
|
|
>
|
|
${gradients.map((grad, index) => {
|
|
let offset = (index * 100) / (gradients.length - 1);
|
|
return `<stop offset="${offset}%" stop-color="#${grad}" />`;
|
|
})}
|
|
</linearGradient>
|
|
</defs>
|
|
`
|
|
: "";
|
|
}
|
|
|
|
/**
|
|
* Retrieves css animations for a card.
|
|
*
|
|
* @returns {string} Animation css.
|
|
*/
|
|
getAnimations = () => {
|
|
return `
|
|
/* Animations */
|
|
@keyframes scaleInAnimation {
|
|
from {
|
|
transform: translate(-5px, 5px) scale(0);
|
|
}
|
|
to {
|
|
transform: translate(-5px, 5px) scale(1);
|
|
}
|
|
}
|
|
@keyframes fadeInAnimation {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
`;
|
|
};
|
|
|
|
/**
|
|
* @param {string} body The inner body of the card.
|
|
* @returns {string} The rendered card.
|
|
*/
|
|
render(body) {
|
|
return `
|
|
<svg
|
|
width="${this.width}"
|
|
height="${this.height}"
|
|
viewBox="0 0 ${this.width} ${this.height}"
|
|
fill="none"
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
role="img"
|
|
aria-labelledby="descId"
|
|
>
|
|
<title id="titleId">${this.a11yTitle}</title>
|
|
<desc id="descId">${this.a11yDesc}</desc>
|
|
<style>
|
|
.header {
|
|
font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif;
|
|
fill: ${this.colors.titleColor};
|
|
animation: fadeInAnimation 0.8s ease-in-out forwards;
|
|
}
|
|
@supports(-moz-appearance: auto) {
|
|
/* Selector detects Firefox */
|
|
.header { font-size: 15.5px; }
|
|
}
|
|
${this.css}
|
|
|
|
${this.getAnimations()}
|
|
${
|
|
this.animations === false
|
|
? `* { animation-duration: 0s !important; animation-delay: 0s !important; }`
|
|
: ""
|
|
}
|
|
</style>
|
|
|
|
${this.renderGradient()}
|
|
|
|
<rect
|
|
data-testid="card-bg"
|
|
x="0.5"
|
|
y="0.5"
|
|
rx="${this.border_radius}"
|
|
height="99%"
|
|
stroke="${this.colors.borderColor}"
|
|
width="${this.width - 1}"
|
|
fill="${
|
|
typeof this.colors.bgColor === "object"
|
|
? "url(#gradient)"
|
|
: this.colors.bgColor
|
|
}"
|
|
stroke-opacity="${this.hideBorder ? 0 : 1}"
|
|
/>
|
|
|
|
${this.hideTitle ? "" : this.renderTitle()}
|
|
|
|
<g
|
|
data-testid="main-card-body"
|
|
transform="translate(0, ${
|
|
this.hideTitle ? this.paddingX : this.paddingY + 20
|
|
})"
|
|
>
|
|
${body}
|
|
</g>
|
|
</svg>
|
|
`;
|
|
}
|
|
}
|
|
|
|
export { Card };
|
|
export default Card;
|