WIP demo site

pull/11/head
Jonah Lawrence 3 years ago
parent 4909266f5f
commit 096cd6d401

@ -0,0 +1,305 @@
html {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
*,
*::before,
*::after {
-webkit-box-sizing: inherit;
-moz-box-sizing: inherit;
box-sizing: inherit;
}
:root {
--background: #eee;
--card-background: white;
--text: #1a1a1a;
--border: #ccc;
--stroke: #a9a9a9;
--blue-light: #2196f3;
--blue-transparent: #2196f3aa;
--blue-dark: #1e88e5;
--button-outline: black;
--red: #ff6464;
--red-alt: #e45353;
--yellow: #ffee58;
--yellow-alt: #fffde7;
}
[data-theme="dark"] {
--background: #090d13;
--card-background: #0d1117;
--text: #efefef;
--border: #2a2e34;
--stroke: #737373;
--blue-light: #1976d2;
--blue-transparent: #2196f320;
--blue-dark: #1565c0;
--button-outline: black;
--red: #ff6464;
--red-alt: #e45353;
--yellow: #a59809;
--yellow-alt: #716800;
}
body {
background: var(--background);
font-family: "Open Sans", Roboto, Arial, sans-serif;
padding-top: 10px;
color: var(--text);
}
.github {
text-align: center;
margin-bottom: 12px;
}
.github a {
color: var(--text);
}
@media only screen and (max-width: 340px) {
.github a {
display: none;
}
.github iframe {
margin-left: 0 !important;
}
}
.container {
margin: auto;
max-width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.properties,
.output {
max-width: 550px;
margin: 10px;
background: var(--card-background);
padding: 25px;
padding-top: 0;
border: 1px solid var(--border);
border-radius: 6px;
}
@media only screen and (max-width: 1024px) {
.properties,
.output {
width: 100%;
}
}
h1 {
text-align: center;
}
h2 {
border-bottom: 1px solid var(--stroke);
}
:not(.btn):focus {
outline: var(--blue-light) auto 2px;
}
.btn {
max-width: 100%;
background-color: var(--blue-light);
color: white;
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-family: inherit;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
transition: 0.2s ease-in-out;
}
.btn:focus {
outline: var(--button-outline) auto 2px;
}
.btn:hover {
background-color: var(--blue-dark);
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
}
.btn:disabled {
background: var(--blue-transparent);
box-shadow: none;
}
.parameters {
margin: auto;
display: grid;
align-items: center;
justify-content: start;
text-align: left;
grid-gap: 8px;
}
.two-columns {
grid-template-columns: auto 1fr;
}
.three-columns {
grid-template-columns: auto 1fr auto;
}
.parameters .btn {
margin-top: 8px;
}
.parameters input[type="text"],
.parameters input.jscolor,
.parameters select {
padding: 10px 14px;
display: inline-block;
border: 1px solid var(--border);
border-radius: 6px;
box-sizing: border-box;
font-family: inherit;
background: var(--card-background);
width: 100%;
color: inherit;
}
.parameters input.jscolor {
font-size: 12px;
max-width: 130px;
}
@media only screen and (max-width: 1024px) {
.parameters input.jscolor {
max-width: none;
}
}
.parameters select {
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml;utf8,<svg fill='black' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>");
background-repeat: no-repeat;
background-position-x: 100%;
background-position-y: 5px;
}
[data-theme="dark"] .parameters select {
background-image: url("data:image/svg+xml;utf8,<svg fill='white' height='24' viewBox='0 0 24 24' width='24' xmlns='http://www.w3.org/2000/svg'><path d='M7 10l5 5 5-5z'/><path d='M0 0h24v24H0z' fill='none'/></svg>");
}
.parameters select option[disabled] {
display: none;
}
.parameters label {
text-transform: capitalize;
}
span[title="required"] {
color: var(--red);
}
input:focus:invalid {
outline: 2px var(--red) auto;
}
.x.btn {
margin: 0;
background: inherit;
color: inherit;
font-size: 22px;
padding: 0;
height: 40px;
width: 40px;
background: var(--red);
color: var(--card-background);
}
.x.btn:hover {
background: var(--red-alt);
}
.add-line.btn {
width: 100px;
font-size: 1em;
padding: 6px 0;
margin-top: 0.8em;
}
.output img {
max-width: 100%;
}
.output .warning {
font-size: 0.84em;
background: var(--yellow-alt);
padding: 8px 14px;
border-left: 4px solid var(--yellow);
}
.output .md {
background: var(--border);
border-radius: 6px;
padding: 12px 16px;
word-break: break-all;
}
.output .btn {
margin-top: 16px;
}
/* tooltips */
.btn.tooltip {
display: flex;
justify-content: center;
align-items: center;
}
/* tooltip bubble */
.btn.tooltip:before {
content: attr(title);
position: absolute;
transform: translateY(-45px);
height: auto;
width: auto;
background: #4a4a4afa;
border-radius: 4px;
color: white;
line-height: 30px;
font-size: 1em;
padding: 0 12px;
pointer-events: none;
opacity: 0;
}
/* tooltip bottom triangle */
.btn.tooltip:after {
content: "";
position: absolute;
transform: translateY(-27px);
border-style: solid;
border-color: #4a4a4afa transparent transparent transparent;
pointer-events: none;
opacity: 0;
}
/* show tooltip on hover */
.btn.tooltip[title]:hover:before,
.btn.tooltip[title]:hover:after,
.btn.tooltip:disabled:hover:before,
.btn.tooltip:disabled:hover:after {
transition: 0.2s ease-in opacity;
opacity: 1;
}
.btn.tooltip:disabled:before {
content: "You must first input a valid username.";
}

@ -0,0 +1,28 @@
a.darkmode {
position: fixed;
top: 2em;
right: 2em;
color: var(--text);
background: var(--background);
height: 3em;
width: 3em;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
border: 2px solid var(--border);
box-shadow: 0 0 3px rgb(0 0 0 / 12%), 0 1px 2px rgb(0 0 0 / 24%);
transition: 0.2s ease-in box-shadow;
}
a.darkmode:hover {
box-shadow: 0 0 6px rgb(0 0 0 / 16%), 0 3px 6px rgb(0 0 0 / 23%);
}
@media only screen and (max-width: 600px) {
a.darkmode {
top: unset;
bottom: 1em;
right: 1em;
}
}

@ -0,0 +1,96 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-48CYVH0XEF"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-48CYVH0XEF');
</script>
<title>Readme Typing SVG</title>
<link rel="preconnect" href="https://fonts.gstatic.com">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap">
<link href="https://css.gg/css?=|moon|sun" rel="stylesheet">
<link rel="stylesheet" href="./css/style.css">
<link rel="stylesheet" href="./css/toggle-dark.css">
<script type="text/javascript" src="./js/script.js" defer></script>
<script type="text/javascript" src="./js/toggle-dark.js" defer></script>
<script type="text/javascript" src="./js/jscolor.min.js" defer></script>
</head>
<body <?php echo $_COOKIE["darkmode"] == "on" ? 'data-theme="dark"' : ""; ?>>
<h1>⌨️ Readme Typing SVG</h1>
<div class="github">
<!-- GitHub Sponsors -->
<iframe src="https://github.com/sponsors/DenverCoder1/button" title="Sponsor DenverCoder1" height="32"
width="116" style="border: 0; margin-left: 12px;"></iframe>
<!-- GitHub Logo -->
<a href="https://git.io/typing-svg" title="GitHub" style="text-decoration: none;">
<svg stroke="#0000" fill="currentColor" stroke-width="0" viewBox="0 0 448 512" height="32px" width="32px"
xmlns="http://www.w3.org/2000/svg">
<path
d="M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48zM277.3 415.7c-8.4 1.5-11.5-3.7-11.5-8 0-5.4.2-33 .2-55.3 0-15.6-5.2-25.5-11.3-30.7 37-4.1 76-9.2 76-73.1 0-18.2-6.5-27.3-17.1-39 1.7-4.3 7.4-22-1.7-45-13.9-4.3-45.7 17.9-45.7 17.9-13.2-3.7-27.5-5.6-41.6-5.6-14.1 0-28.4 1.9-41.6 5.6 0 0-31.8-22.2-45.7-17.9-9.1 22.9-3.5 40.6-1.7 45-10.6 11.7-15.6 20.8-15.6 39 0 63.6 37.3 69 74.3 73.1-4.8 4.3-9.1 11.7-10.6 22.3-9.5 4.3-33.8 11.7-48.3-13.9-9.1-15.8-25.5-17.1-25.5-17.1-16.2-.2-1.1 10.2-1.1 10.2 10.8 5 18.4 24.2 18.4 24.2 9.7 29.7 56.1 19.7 56.1 19.7 0 13.9.2 36.5.2 40.6 0 4.3-3 9.5-11.5 8-66-22.1-112.2-84.9-112.2-158.3 0-91.8 70.2-161.5 162-161.5S388 165.6 388 257.4c.1 73.4-44.7 136.3-110.7 158.3zm-98.1-61.1c-1.9.4-3.7-.4-3.9-1.7-.2-1.5 1.1-2.8 3-3.2 1.9-.2 3.7.6 3.9 1.9.3 1.3-1 2.6-3 3zm-9.5-.9c0 1.3-1.5 2.4-3.5 2.4-2.2.2-3.7-.9-3.7-2.4 0-1.3 1.5-2.4 3.5-2.4 1.9-.2 3.7.9 3.7 2.4zm-13.7-1.1c-.4 1.3-2.4 1.9-4.1 1.3-1.9-.4-3.2-1.9-2.8-3.2.4-1.3 2.4-1.9 4.1-1.5 2 .6 3.3 2.1 2.8 3.4zm-12.3-5.4c-.9 1.1-2.8.9-4.3-.6-1.5-1.3-1.9-3.2-.9-4.1.9-1.1 2.8-.9 4.3.6 1.3 1.3 1.8 3.3.9 4.1zm-9.1-9.1c-.9.6-2.6 0-3.7-1.5s-1.1-3.2 0-3.9c1.1-.9 2.8-.2 3.7 1.3 1.1 1.5 1.1 3.3 0 4.1zm-6.5-9.7c-.9.9-2.4.4-3.5-.6-1.1-1.3-1.3-2.8-.4-3.5.9-.9 2.4-.4 3.5.6 1.1 1.3 1.3 2.8.4 3.5zm-6.7-7.4c-.4.9-1.7 1.1-2.8.4-1.3-.6-1.9-1.7-1.5-2.6.4-.6 1.5-.9 2.8-.4 1.3.7 1.9 1.8 1.5 2.6z">
</path>
</svg></a>
<!-- GitHub Star -->
<iframe
src="https://ghbtns.com/github-btn.html?user=DenverCoder1&repo=readme-typing-svg&type=star&count=true&size=large"
width="140" height="30" title="GitHub stars" style="border: 0;"></iframe>
</div>
<div class="container">
<div class="properties">
<h2>Add your text</h2>
<form class="parameters three-columns lines"></form>
<button class="add-line btn" onclick="return preview.addLine(this);">+ Add line</button>
<h2>Options</h2>
<form class="parameters two-columns options">
<label for="user">Username<span title="required">*</span></label>
<input class="param" type="text" id="user" name="user" placeholder="DenverCoder1" required
pattern="^[A-Za-z\d-]{0,39}[A-Za-z\d]$"
title="Up to 40 letters or hyphens but not ending with hyphen">
<label for="color">Font color</label>
<input class="param jscolor jscolor-active" id="color" name="background"
data-jscolor="{ format: 'hexa' }" value="#36BCF7">
<label for="size">Font size</label>
<select class="param" id="hide_border" name="hide_border" placeholder="false">
<option>false</option>
<option>true</option>
</select>
</form>
</div>
<div class="output">
<h2>Preview</h2>
<img alt="Readme Typing SVG" src="preview.php?user=" />
<h2>Markdown</h2>
<div class="md">
<code></code>
</div>
<button class="copy-button btn tooltip" onclick="clipboard.copy(this);" onmouseout="tooltip.reset(this);"
disabled>
Copy To Clipboard
</button>
</div>
</div>
<a href="javascript:toggleTheme()" class="darkmode" title="toggle dark mode">
<i class="<?php echo $_COOKIE["darkmode"] == "on" ? 'gg-sun' : "gg-moon"; ?>"></i>
</a>
</body>
</html>

File diff suppressed because one or more lines are too long

@ -0,0 +1,165 @@
let preview = {
// default values
defaults: {
theme: "default",
hide_border: "false",
},
// update the preview
update: function () {
// get parameter values from all .param elements
const params = Array.from(document.querySelectorAll(".param")).reduce(
(acc, next) => {
let obj = {
...acc
};
let value = next.value;
if (value.indexOf('#') >= 0) {
// if the value is colour, remove the hash sign
value = value.replace(/#/g, "");
if (value.length > 6) {
// if the value is in hexa and opacity is 1, remove FF
value = value.replace(/(F|f){2}$/, "");
}
}
obj[next.id] = value;
return obj;
}, {}
);
// convert parameters to query string
const encode = encodeURIComponent;
const query = Object.keys(params)
.filter((key) => params[key] !== this.defaults[key])
.map((key) => encode(key) + "=" + encode(params[key]))
.join("&");
// generate links and markdown
const imageURL = `${window.location.origin}?${query}`;
const demoImageURL = `preview.php?${query}`;
const repoLink = "https://git.io/streak-stats";
const md = `[![GitHub Streak](${imageURL})](${repoLink})`;
// update image preview
document.querySelector(".output img").src = demoImageURL;
// update markdown
document.querySelector(".md code").innerText = md;
// disable copy button if username is invalid
const copyButton = document.querySelector(".copy-button");
copyButton.disabled = !!document.querySelectorAll("#user:invalid").length;
},
addLine: function () {
const parent = document.querySelector(".lines");
const index = parent.querySelectorAll("input").length + 1;
// label
const label = document.createElement("label");
label.innerText = `Line ${index}`;
label.setAttribute("for", `line-${index}`);
label.dataset.index = index;
// color picker
const input = document.createElement("input");
input.className = "param";
input.type = "text";
input.id = `line-${index}`;
input.name = `line-${index}`;
input.placeholder = "Enter text here";
input.pattern = "^[^;]*$";
input.title = "Text cannot contain semicolons";
input.dataset.index = index;
// removal button
const xButton = document.createElement("button");
xButton.className = "x btn";
xButton.setAttribute(
"onclick",
"return preview.removeLine(this.dataset.index);"
);
xButton.innerText = "✕";
xButton.dataset.index = index;
// add elements
parent.appendChild(label);
parent.appendChild(input);
parent.appendChild(xButton);
// update and exit
this.update();
return false;
},
removeLine: function (index) {
index = Number(index);
const parent = document.querySelector(".lines");
// remove all elements for given property
parent
.querySelectorAll(`[data-index="${index}"]`)
.forEach((el) => {
parent.removeChild(el);
});
// update index numbers
parent
.querySelectorAll(`label[data-index]`)
.forEach((label) => {
const labelIndex = Number(label.dataset.index);
if (labelIndex > index) {
label.dataset.index = labelIndex - 1;
label.setAttribute("for", `line-${labelIndex - 1}`);
label.innerText = `Line ${labelIndex - 1}`;
}
});
parent
.querySelectorAll(`input[data-index]`)
.forEach((input) => {
const inputIndex = Number(input.dataset.index);
if (inputIndex > index) {
input.dataset.index = inputIndex - 1;
input.setAttribute("id", `line-${inputIndex - 1}`);
input.setAttribute("name", `line-${inputIndex - 1}`);
}
});
parent
.querySelectorAll(`button[data-index]`)
.forEach((button) => {
const buttonIndex = Number(button.dataset.index);
if (buttonIndex > index) {
button.dataset.index = buttonIndex - 1;
}
});
// update and exit
this.update();
return false;
},
};
let clipboard = {
copy: function (el) {
// create input box to copy from
const input = document.createElement("input");
input.value = document.querySelector(".md code").innerText;
document.body.appendChild(input);
// select all
input.select();
input.setSelectionRange(0, 99999);
// copy
document.execCommand("copy");
// remove input box
input.parentElement.removeChild(input);
// set tooltip text
el.title = "Copied!";
},
};
let tooltip = {
reset: function (el) {
// remove tooltip text
el.removeAttribute("title");
},
};
// refresh preview on interactions with the page
document.addEventListener("keyup", () => preview.update(), false);
document.addEventListener("click", () => preview.update(), false);
// when the page loads
window.addEventListener(
"load",
() => {
// add first line
preview.addLine();
},
false
);

@ -0,0 +1,22 @@
function toggleTheme() {
const icon = document.querySelector(".darkmode i");
/* dark mode on */
if (document.body.getAttribute("data-theme") !== "dark") {
icon.className = "gg-sun";
setCookie("darkmode", "on", 9999);
document.body.setAttribute("data-theme", "dark");
}
/* dark mode off */
else {
icon.className = "gg-moon";
setCookie("darkmode", "off", 9999);
document.body.removeAttribute("data-theme");
}
}
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + exdays * 24 * 60 * 60 * 1000);
var expires = "expires=" + d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}

@ -4,6 +4,7 @@ require '../vendor/autoload.php';
// set content type
header("Content-type: image/svg+xml");
// set cache to refresh once per day
$timestamp = gmdate("D, d M Y 23:59:00") . " GMT";
header("Expires: $timestamp");
@ -11,6 +12,12 @@ header("Last-Modified: $timestamp");
header("Pragma: no-cache");
header("Cache-Control: no-cache, must-revalidate");
// redirect to demo site if no text is given
if (!isset($_REQUEST["lines"])) {
header('Location: demo/');
exit;
}
try {
// create renderer model
$model = new RendererModel("templates/main.php", $_REQUEST);

Loading…
Cancel
Save