Skip to content

Commit

Permalink
Live Preview (#41)
Browse files Browse the repository at this point in the history
* Preview Button

* Add preview Icon
* Add preview button and block
* Add js script for controling preview

* Update preview.js

* Add base data to preview

* Add preview
- name
- url
- photo
- description
- email

* Update preview.png

* Add Links to preview

* Add links to preview

* Add default preview

* Add animation

* Move preview.js to index.js

* Update Icon

* Add Preview for theme

* Themes Load images

* js preview in new html window independent

* default theme

* Rename css

* Final preview Feature

* Add meta data

* Make preview only for devices min-width: 1000px

* Update index.php

* Update .gitignore
  • Loading branch information
Daynlight authored Jun 29, 2024
1 parent 60c41ac commit 05d4f9f
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
/dist/
/dist/
44 changes: 44 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
:root{
--buttonSize: 70px;
--buttonOffset: 10px;
--buttonRadius: 10px;
--previewWidth: 30vw;
--previewHeight: 100vh;
--previewXOffset: 0;
--previewYOffset: 0;
--animationDuration: 500ms;
}
#previewButton {
position: fixed;
width: var(--buttonSize);
height: var(--buttonSize);
bottom: var(--buttonOffset);
right: var(--buttonOffset);
border-radius: var(--buttonRadius);
z-index: 2;
}

#previewButton > *{
width: 100%;
height: 100%;
}

#previewBlock{
position: fixed;
top: var(--previewYOffset);
right: var(--previewXOffset);
width: var(--previewWidth);
height: var(--previewHeight);
z-index: 1;
transition: var(--animationDuration);
right: -100%;
}

@media screen and (max-width: 1000px){
#previewButton{
display: none;
}
#previewBlock{
display: none;
}
}
150 changes: 150 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const localStorageIgnore = ["photo"];
const themeUrl = "https://cdn.jsdelivr.net/npm/linkfree-themes@1.1.0";

// Elements
const addCustomLinkButton = document.querySelector("a.btn");
Expand Down Expand Up @@ -218,3 +219,152 @@ checkbox_preview.addEventListener("change", () => {
checkbox_zip.addEventListener("change", () => {
checkbox_preview.disabled = checkbox_zip.checked;
});

/*****************************************************/
/***************** Real Time Preview *****************/
/*****************************************************/

// Get Window Elements
var previewButton = document.getElementById('previewButton');
var previewBlock = document.getElementById('previewBlock');
var additionalLinkButton = document.getElementById("additionalLink");
var formData = document.getElementById('form');
var theme = document.getElementById('theme');

// Real time variables
var preview = false;
var photo = "";
var linkCount = Number(additionalLinkButton.getAttribute("data-index"));
const styleElement = document.createElement('style');

// Preview Button functionality
previewButton.addEventListener('click', () => {
preview = !preview;
if (preview) {
// previewBlock.style.display = 'block';
previewBlock.style.right = '0';
previewButton.style.filter = 'invert(1)';
UpdatePreview();
} else {
// previewBlock.style.display = 'none';
previewBlock.style.right = '-100%';
previewButton.style.filter = 'invert(0)';
}
});

// Update Preview Photo On Input
formData['photo'].addEventListener('input', (e) => {
var photoData = e.target.files[0];
if(photoData) {
const reader = new FileReader();
reader.onload = (e) => {
photo = e.target.result;
UpdatePreview();
}
reader.readAsDataURL(photoData);
}
});

// Add Listner for additionalLinkButton and for form data
additionalLinkButton.addEventListener('click', () => {
linkCount++;
var linkId = `links[${linkCount}]`;
document.getElementById(linkId + "[url]").addEventListener('input', UpdatePreview);
document.getElementById(linkId + "[name]").addEventListener('input', UpdatePreview);
document.getElementById(linkId + "[icon]").addEventListener('input', UpdatePreview);
});

// Update Prview Function
function UpdatePreview() {
var name = formData['name'].value;
var mainUrl= formData['url'].value;
var description = formData['description'].value;
var email = formData['email'].value;
var links = "";
var photoCode = "";
var themePath = "";

// Links
for (var i = 0; i < linkCount; i++) {
var linkId = `links[${i}]`;
var linkUrl = document.getElementById(linkId + "[url]").value;
var linkName = document.getElementById(linkId + "[name]").value;
var linkIcon = document.getElementById(linkId + "[icon]").value;

if(linkUrl !== ""){
if(linkIcon !== ""){
links +=
`<a class="link" href="${linkUrl}" target="_blank">
<ion-icon name="${linkIcon}"></ion-icon>
${linkName} </a>`;
}
else{
links +=
`<a class="link" href="${linkUrl}" target="_blank">
${linkName} </a>`;
}
}
}

// Check if data is added
if(photo !== '') photoCode = `<img id="userPhoto" src="${photo}" alt="User Photo"></img>`;
if(name !== '') name = `<a href="${mainUrl}"><h1 id="userName">${name}</h1></a>`;
if(description !== '') description =`<p id="description">${description}</p>`;
if(email !== '') email = `<a class="link" href="mailto:${email}" target="_blank"><ion-icon name="mail"></ion-icon> Email</a>`;

// Add path to files
if(theme.value !== "")
{
let jsonString = theme.value.match(/{.*}/)[0];
var json = JSON.parse(jsonString);
themePath = "themes/" + json.id;
}
else themePath = "themes/darkmode";

// Add Style
var themeStylePath = themeUrl + "/" + themePath+ "/" + "style.css";

// Add JS
var themeJSPath = themeUrl + "/" + themePath+ "/" + "index.js";

// Update Preview
var previewHTMLCode =
`<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<link rel="stylesheet" href="${themeStylePath}">
</head>
<body>
${photoCode}
${name}
${description}
<div id="links">
${links}
${email}
</div>
</body>
<script src="${themeJSPath}"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/ionicons@7.4.0/dist/ionicons/ionicons.esm.js"></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/ionicons@7.4.0/dist/ionicons/ionicons.js"></script>
</html>`;

var blob = new Blob([previewHTMLCode], { type: 'text/html' });
var url = URL.createObjectURL(blob);
previewBlock.innerHTML = `<object style="width: 100%; height: 100%;" type='text/html' data='${url}'></object>`;
};

// Add Listner for all links on file Load
for (var i = 0; i < linkCount; i++) {
var linkId = `links[${i}]`;
document.getElementById(linkId + "[url]").addEventListener('input', UpdatePreview);
document.getElementById(linkId + "[name]").addEventListener('input', UpdatePreview);
document.getElementById(linkId + "[icon]").addEventListener('input', UpdatePreview);
}

// Add Listner for all forms inputs on file Load
formData['name'].addEventListener('input', UpdatePreview);
formData['url'].addEventListener('input', UpdatePreview);
formData['description'].addEventListener('input', UpdatePreview);
formData['email'].addEventListener('input', UpdatePreview);
theme.addEventListener('input', UpdatePreview);
27 changes: 24 additions & 3 deletions src/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,24 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<meta name="description" content="Create your own LinkFree and have all your links in one place">
<meta name="author" content="Chris K. Thomas">
<meta name="keywords" content="linkfree, linktree, link in bio, link in bio alternative, linkfree generator, linktree generator, link in bio generator">
<meta property="og:title" content="LinkFree Generator">
<meta property="og:description" content="Create your own LinkFree and have all your links in one place">
<meta property="og:url" content="https://chriskthomas.github.io/linkfree-generator/">
<meta property="og:image" content="https://lh3.googleusercontent.com/p/AF1QipMjTWdGPL1Ch8Q0poYcH5vhl_tvqF-1o1_4slJD=s680-w680-h510">
<meta property="og:type" content="website">
<meta property="og:site_name" content="LinkFree Generator">
<meta property="og:locale" content="en_US">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="LinkFree Generator">
<meta name="twitter:description" content="Create your own LinkFree and have all your links in one place">
<meta name="twitter:image" content="https://lh3.googleusercontent.com/p/AF1QipMjTWdGPL1Ch8Q0poYcH5vhl_tvqF-1o1_4slJD=s680-w680-h510">

<title>LinkFree Generator</title>
<link rel="stylesheet" href="index.css">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>

Expand All @@ -53,7 +70,7 @@
<p>
Fill out this form to generate your own single page website. All fields are optional except for your name. So, don't worry if you don't have all these accounts. The output will be a single <code>index.html</code> file that you can upload to any static hosting provider such as GitHub Pages, Cloudflare Pages, Vercel, Netlify, or DigitalOcean Apps.
</p>
<form class="mb-3" action="api.php" method="post" enctype="multipart/form-data" accept-charset="utf-8">
<form id="form" class="mb-3" action="api.php" method="post" enctype="multipart/form-data" accept-charset="utf-8">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" id="name" name="name" class="form-control" placeholder="Chris K. Thomas" required>
Expand Down Expand Up @@ -102,7 +119,7 @@
</div>
<?php } ?>
<?php if ($num_clinks < 50) { ?>
<a class="btn btn-secondary mb-2" data-index="<?= ($lastsite_index + $num_clinks) ?>" role="button">+ Add Additional Link</a>
<a id="additionalLink" class="btn btn-secondary mb-2" data-index="<?= ($lastsite_index + $num_clinks) ?>" role="button">+ Add Additional Link</a>
<?php } ?>
<div class="mb-3">
<div class="form-text">
Expand Down Expand Up @@ -154,7 +171,11 @@

</div>
</footer>

<button id="previewButton"><ion-icon name="eye"></ion-icon></button>
<div id="previewBlock"></div>
<script src="./index.js"></script>
</body>

<script type="module" src="https://cdn.jsdelivr.net/npm/ionicons@7.4.0/dist/ionicons/ionicons.esm.js"></script>
</body>
</html>

0 comments on commit 05d4f9f

Please sign in to comment.