Skip to content

Commit

Permalink
feat: Added gradient background tuner
Browse files Browse the repository at this point in the history
Signed-off-by: Mohd Faraz <[email protected]>
  • Loading branch information
AndroiableDroid committed Mar 30, 2023
1 parent 94157b0 commit 1d93d0b
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 28 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ If the `theme` parameter is specified, any color customizations specified will b
| `hide_border` | Make the border transparent (Default: `false`) | `true` or `false` |
| `border_radius` | Set the roundness of the edges (Default: `4.5`) | Number `0` (sharp corners) to `248` (ellipse) |
| `background` | Background color | **hex code** without `#` or **css color** |
| `gradientBg` | Background color | **rotation** and two colors **hex code** without `#` or **css color** |
| `border` | Border color | **hex code** without `#` or **css color** |
| `stroke` | Stroke line color between sections | **hex code** without `#` or **css color** |
| `ring` | Color of the ring around the current streak | **hex code** without `#` or **css color** |
Expand Down
13 changes: 12 additions & 1 deletion src/card.php
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ function generateCard(array $stats, array $params = null): string
// read border_radius parameter, default to 4.5 if not set
$borderRadius = $params["border_radius"] ?? "4.5";

// Set Background
$bg = $params['gradientBg'] ? 'url(#gradient)' : $theme["background"];
$gradient = explode(",",$params['gradientBg'] ?? "");

// total contributions
$totalContributions = $numFormatter->format($stats["totalContributions"]);
$firstContribution = formatDate($stats["firstContribution"], $dateFormat, $localeCode);
Expand Down Expand Up @@ -325,6 +329,13 @@ function generateCard(array $stats, array $params = null): string
100% { opacity: 1; }
}
</style>
<defs>
<linearGradient id='gradient' gradientTransform='rotate({$gradient[0]})'>
<stop offset='0%' stop-color='#{$gradient[1]}' />
<stop offset='100%' stop-color='#{$gradient[2]}' />
</linearGradient>
</defs>
<defs>
<clipPath id='outer_rectangle'>
<rect width='495' height='195' rx='{$borderRadius}'/>
Expand All @@ -336,7 +347,7 @@ function generateCard(array $stats, array $params = null): string
</defs>
<g clip-path='url(#outer_rectangle)'>
<g style='isolation: isolate'>
<rect stroke='{$theme["border"]}' fill='{$theme["background"]}' rx='{$borderRadius}' x='0.5' y='0.5' width='494' height='194'/>
<rect stroke='{$theme["border"]}' fill='{$bg}' rx='{$borderRadius}' x='0.5' y='0.5' width='494' height='194'/>
</g>
<g style='isolation: isolate'>
<line x1='330' y1='28' x2='330' y2='170' vector-effect='non-scaling-stroke' stroke-width='1' stroke='{$theme["stroke"]}' stroke-linejoin='miter' stroke-linecap='square' stroke-miterlimit='3'/>
Expand Down
5 changes: 5 additions & 0 deletions src/demo/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ input:focus:invalid {
grid-template-columns: auto 1fr auto;
}

.advanced .grid-middle {
display: grid;
grid-template-columns: 30% 35% 35%;
}

.advanced .color-properties label:first-of-type {
font-weight: bold;
}
Expand Down
3 changes: 2 additions & 1 deletion src/demo/index.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

$THEMES = include "../themes.php";
$PROPERTIES = include "../properties.php";
$TRANSLATIONS = include "../translations.php";
// Get the keys of the first value in the translations array
// and filter to only include locales that have an array as the value
Expand Down Expand Up @@ -149,7 +150,7 @@ function gtag() {
<div class="content color-properties parameters">
<label for="theme">Add Property</label>
<select id="properties" name="properties">
<?php foreach ($THEMES["default"] as $option => $color): ?>
<?php foreach ($PROPERTIES as $option): ?>
<option><?php echo $option; ?></option>
<?php endforeach; ?>
</select>
Expand Down
121 changes: 95 additions & 26 deletions src/demo/js/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ const preview = {
// convert parameters to query string
const query = Object.keys(params)
.filter((key) => params[key] !== this.defaults[key])
.map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
.map((key) => {
if (key === 'gradientBg') {
return `${encodeURIComponent(key)}=${encodeURIComponent(params[key][0])},${encodeURIComponent(params[key][1])},${encodeURIComponent(params[key][2])}`
}
return `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`
})
.join("&");
// generate links and markdown
const imageURL = `${window.location.origin}?${query}`;
Expand Down Expand Up @@ -61,18 +66,12 @@ const preview = {
*/
addProperty(property, value = "#EB5454FF") {
const selectElement = document.querySelector("#properties");
Array.prototype.find.call(selectElement.options, (o) => o.value === property);
// if no property passed, get the currently selected property
const propertyName = property || selectElement.value;
if (!selectElement.disabled) {
// disable option in menu
Array.prototype.find.call(selectElement.options, (o) => o.value === propertyName).disabled = true;
// select first unselected option
const firstAvailable = Array.prototype.find.call(selectElement.options, (o) => !o.disabled);
if (firstAvailable) {
firstAvailable.selected = true;
} else {
selectElement.disabled = true;
}
// label
const label = document.createElement("label");
label.innerText = propertyName;
Expand All @@ -83,26 +82,80 @@ const preview = {
onChange: `preview.pickerChange(this, '${propertyName}')`,
onInput: `preview.pickerChange(this, '${propertyName}')`,
};
const input = document.createElement("input");
input.className = "param jscolor";
input.id = propertyName;
input.name = propertyName;
input.setAttribute("data-property", propertyName);
input.setAttribute("data-jscolor", JSON.stringify(jscolorConfig));
input.value = value;

const parent = document.querySelector(".advanced .color-properties");
if (propertyName === "gradientBg") {
Array.prototype.find.call(selectElement.options, (o) => o.value === 'background').disabled = true;

const input = document.createElement("span");
input.className = "grid-middle";
input.setAttribute("data-property", propertyName);

const rotate = document.createElement("input");
rotate.className = "param";
rotate.type = "text";
rotate.id = "rotate";
rotate.placeholder = "30deg";
rotate.value = "30deg";
rotate.pattern = "^-[0-9]+deg|^[0-9]+[deg]+"

const color1 = document.createElement("input");
color1.className = "param jscolor";
color1.id = "color1";
color1.setAttribute("data-jscolor", JSON.stringify({
format: "hexa",
onChange: `preview.pickerChange(this, '${color1.id}')`,
onInput: `preview.pickerChange(this, '${color1.id}')`,
}));
const color2 = document.createElement("input");
color2.className = "param jscolor";
color2.id = "color2";
color2.setAttribute("data-jscolor", JSON.stringify({
format: "hexa",
onChange: `preview.pickerChange(this, '${color2.id}')`,
onInput: `preview.pickerChange(this, '${color2.id}')`,
}));
rotate.name = color1.name = color2.name = propertyName;
color1.value = color2.value = value;
// add elements
parent.appendChild(label);
input.appendChild(rotate);
input.appendChild(color1);
input.appendChild(color2);
parent.appendChild(input);
} else {
const input = document.createElement("input");
input.className = "param jscolor";
input.id = propertyName;
input.name = propertyName;
input.setAttribute("data-property", propertyName);
input.setAttribute("data-jscolor", JSON.stringify(jscolorConfig));
input.value = value;
// add elements
parent.appendChild(label);
parent.appendChild(input);
}

if (propertyName === 'background') {
Array.prototype.find.call(selectElement.options, (o) => o.value === 'gradientBg').disabled = true;
}

// select first unselected option
const firstAvailable = Array.prototype.find.call(selectElement.options, (o) => !o.disabled);
if (firstAvailable) {
firstAvailable.selected = true;
} else {
selectElement.disabled = true;
}
// removal button
const minus = document.createElement("button");
minus.className = "minus btn";
minus.setAttribute("onclick", "return preview.removeProperty(this.getAttribute('data-property'));");
minus.setAttribute("type", "button");
minus.innerText = "−";
minus.setAttribute("data-property", propertyName);
// add elements
const parent = document.querySelector(".advanced .color-properties");
parent.appendChild(label);
parent.appendChild(input);
parent.appendChild(minus);

// initialise jscolor on element
jscolor.install(parent);

Expand All @@ -127,6 +180,11 @@ const preview = {
const option = Array.prototype.find.call(selectElement.options, (o) => o.value === property);
selectElement.disabled = false;
option.disabled = false;
if (property === 'gradientBg') {
Array.prototype.find.call(selectElement.options, (o) => o.value === 'background').disabled = false;
} else if (property === 'background') {
Array.prototype.find.call(selectElement.options, (o) => o.value === 'gradientBg').disabled = false;
}
// update and exit
this.update();
},
Expand All @@ -151,8 +209,12 @@ const preview = {
* @returns {Object} the key-value mapping
*/
objectFromElements(elements) {
let mCount = 0;
return Array.from(elements).reduce((acc, next) => {
const obj = { ...acc };
if (obj.gradientBg !== undefined) {
mCount++;
} else if (mCount >= 3) mCount = 0;
let value = next.value;
if (value.indexOf("#") >= 0) {
// if the value is colour, remove the hash sign
Expand All @@ -161,8 +223,12 @@ const preview = {
// if the value is in hexa and opacity is 1, remove FF
value = value.replace(/[Ff]{2}$/, "");
}
} else if (value.indexOf("deg") >= 0) {
value = value.replace(/deg/g, "");
}
obj[next.name] = value;
if (mCount <= 0)
obj[next.name] = [];
obj[next.name].push(value);
return obj;
}, {});
},
Expand Down Expand Up @@ -194,12 +260,15 @@ const preview = {
* Remove "FF" from a hex color if opacity is 1
* @param {string} color - the hex color
* @param {string} input - the property name, or id of the element to update
* @param {boolean} setColor - if true set the color to the input else update original value
*/
checkColor(color, input) {
checkColor(color, input, setColor = false) {
// if color has hex alpha value -> remove it
if (color.length === 9 && color.slice(-2) === "FF") {
// if color has hex alpha value -> remove it
document.querySelector(`[name="${input}"]`).value = color.slice(0, -2);
}
for (let el of document.querySelectorAll(`[name="${input}"]`))
if (el.value.length === 9 && color.slice(-2) === "FF")
el.value = setColor ? color.slice(0, -2) : el.value.slice(0, -2);
}
},

/**
Expand All @@ -209,7 +278,7 @@ const preview = {
*/
pickerChange(picker, input) {
// color was changed by picker - check it
this.checkColor(picker.toHEXAString(), input);
this.checkColor(picker.toHEXAString(), input, true);
// update preview
this.update();
},
Expand Down
15 changes: 15 additions & 0 deletions src/properties.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
return [
"background",
"gradientBg",
"border",
"stroke",
"ring",
"fire",
"currStreakNum",
"sideNums",
"currStreakLabel",
"sideLabels",
"dates"
];
?>
6 changes: 6 additions & 0 deletions tests/expected/test_card.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 1d93d0b

Please sign in to comment.