30 Commits

Author SHA1 Message Date
gurusabarish 9b9e8a6b84 option to customize search placeholder 2023-03-21 07:30:05 +05:30
gurusabarish de4c1e1a57 Merge branch 'master' into search_placeholder 2023-03-21 07:18:02 +05:30
gurusabarish a36ef35350 fix for #113 2023-03-20 07:25:23 +05:30
gurusabarish 6da60eaa31 fix for #108 2023-03-19 10:43:47 +05:30
Ari Kalfus 84e118a723 fix: provide clearer placeholder for search bar 2023-03-12 14:07:46 -04:00
gurusabarish e26564cde6 added option to disable hero bottom image 2023-03-07 07:37:27 +05:30
gurusabarish 2e8daeaa0d added scroll progress bar in single page template 2023-02-12 00:41:29 +05:30
Guru Sabarish edde3e1123 Merge pull request #101 from pantera-rosa/clean-up-code
clean up code
2023-02-09 23:15:01 +05:30
pantera-rosa 1286527ddd clean up code
Revert "make navbar sticky"

This reverts commit 72d40a3005.

clean up code

make navbar sticky
2023-02-08 22:44:35 -08:00
Guru Sabarish 98f6f9c223 Merge pull request #97 from Monpoke/master
Adds translations for static texts
2023-02-06 22:17:15 +05:30
Guru Sabarish 03a9f1b237 Merge pull request #95 from benarmstead/round-hero-image
Make hero image round if roundImage: true
2023-02-06 22:14:13 +05:30
Guru Sabarish 308342eefc Merge pull request #94 from benarmstead/master
Add showBrandLogo flag to allow hiding in nav bar
2023-02-06 22:12:05 +05:30
Pierre Bourgeois aae2408ef7 Adds translations for static texts 2023-02-05 20:09:51 +01:00
Ben Armstead 1f289595f9 Make hero image round if roundImage: true 2023-02-05 12:05:22 +00:00
Ben Armstead 62a2507e19 Add showBrandLogo flag to allow hiding in nav bar 2023-02-05 11:45:13 +00:00
Guru Sabarish 6930cf2da2 Merge pull request #91 from pantera-rosa/patch-1
add primary-font to experience
2023-02-03 00:02:06 +05:30
ilona 7ce2ea3ae2 add primary-font to experience
keeping font consistent
2023-01-31 23:33:51 -08:00
gurusabarish 4dad914f15 Merge branch 'master' of https://github.com/gurusabarish/hugo-profile 2023-01-22 12:36:56 +05:30
gurusabarish 8e8d33799b added bootstrap js map file 2023-01-22 12:36:42 +05:30
Guru Sabarish bd4225c25c Update config.yaml 2023-01-22 01:12:44 +05:30
gurusabarish 123418edea on escape and schortcut functionality added for search 2023-01-22 01:10:14 +05:30
gurusabarish c28b579ab6 window.matchMedia check 2023-01-21 23:34:17 +05:30
gurusabarish 1d476eb2b5 added reading time in single page 2023-01-21 23:31:22 +05:30
Guru Sabarish 07c0b9652f Merge pull request #88 from gurusabarish/87
Integrate with FormSpree for submitting 'Contact me' form #87 by @dmantula
2023-01-16 20:22:27 +05:30
gurusabarish cd85ec46cd added styling 2023-01-16 20:15:05 +05:30
Dmytro Mantula 001841d6e6 Integrate with FormSpree for submitting 'Contact me' form
To enable the integration, uncomment the section `params.contact.formspree` and set the `formId` value (provided by FormSpree).
It's recommended to change the `btnName` from "Mail me" to "Contact me" as far as visitors won't see your email address.
FormSpree section has preference over `email` if the both are set.
2023-01-14 15:47:14 +01:00
Guru Sabarish 9c1b16ec3d Update FUNDING.yml 2023-01-10 00:07:33 +05:30
Guru Sabarish a15bcb6699 Update config.yaml 2023-01-02 23:27:30 +05:30
Guru Sabarish 43d6a7b957 Merge pull request #84 from fbartels/patch-1
Fix toggle to disable projects from the menu
2022-12-22 23:44:23 +05:30
Felix Bartels fcbbf9219b Fix toggle to disable projects from the menu 2022-12-21 17:42:37 +01:00
22 changed files with 338 additions and 74 deletions
+1 -1
View File
@@ -9,4 +9,4 @@ community_bridge: # Replace with a single Community Bridge project-name e.g., cl
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['https://donate.stripe.com/7sIeWp3aG07vd6o8ww'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
custom: ['https://www.buymeacoffee.com/gurusabarish'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+1
View File
@@ -24,6 +24,7 @@ A high performance and mobile first hugo template for personal portfolio and blo
- [Google Analytics](https://gohugo.io/templates/internal/#google-analytics)
- Comment Support
- [Disqus](https://gohugo.io/content-management/comments/)
- Integration with [FormSpree](https://formspree.io/) for submitting "Contact me" form
Technology used: Bootstrap, fontawesome
+33
View File
@@ -14,6 +14,8 @@ outputs:
Paginate: 3
enableRobotsTXT: true
# disqusShortname: your-disqus-shortname
# googleAnalytics: G-MEASUREMENT_ID
markup:
goldmark:
@@ -100,8 +102,10 @@ params:
navbar:
align: ms-auto # Left: ms-auto | center: mx-auto | right: me-auto | Default: ms-auto
# brandLogo: "/logo.png" # Logo for the brand | default is the favicon variable
# showBrandLogo: false # Show brand logo in nav bar | default is true
brandName: "Hugo Profile" # Brand name for the brand | default is the title variable
disableSearch: false
# searchPlaceholder: "Search"
menus:
disableAbout: false
disableExperience: false
@@ -118,6 +122,9 @@ params:
subtitle: "I build things for the web"
content: "A passionate web app developer. I tend to make use of modern web technologies to build websites that looks great, feels fantastic, and functions correctly."
image: /images/hero.svg
bottomImage:
enable: true
# roundImage: true # Make hero image circular | default false
button:
enable: true
name: "Resume"
@@ -338,6 +345,11 @@ params:
content: My inbox is always open. Whether you have a question or just want to say hi, Ill try my best to get back to you!
email: gurusabarisha@gmail.com
btnName: Mail me
# formspree:
# enable: true # `contact.email` value will be ignored
# formId: abcdefgh # Take it from your form's endpoint, like 'https://formspree.io/f/abcdefgh'
# emailCaption: "Enter your email address"
# messageCaption: "Enter your message here"
footer:
recentPosts:
@@ -356,3 +368,24 @@ params:
# List pages like blogs and posts
listPages:
disableFeaturedImage: false
# Single pages like blog and post
singlePages:
readTime:
enable: true
content: "min read"
scrollprogress:
enable: true
# For translations
terms:
read: "Read"
toc: "Table Of Contents"
copyright: "All rights reserved"
pageNotFound: "Page not found"
emailText: "Check out this site"
datesFormat:
article: "Jan 2, 2006"
articleList: "Jan 2, 2006"
articleRecent: "Jan 2, 2006"
+3 -3
View File
@@ -3,12 +3,12 @@
{{ end }}
{{ define "title" }}
{{ .Site.Title }} | 404 page not found
{{ .Site.Title }} | {{ .Site.Params.terms.pageNotFound | default "404 page not found" }}
{{ end }}
{{ define "main" }}
<div class="container py-5 text-center">
<img src="{{ .Site.Params.staticPath }}/404.png" alt="404 page not found" class="img-fluid" width="40%">
<h1>404 Page Not Found</h1>
<img src="{{ .Site.Params.staticPath }}/404.png" alt='{{ .Site.Params.terms.pageNotFound | default "404 page not found" }}' class="img-fluid" width="40%">
<h1>{{ .Site.Params.terms.pageNotFound | default "404 page not found" }}</h1>
</div>
{{ end }}
+1 -1
View File
@@ -9,7 +9,7 @@
</title>
</head>
<body class="light" onload="loading()">
<body class="light">
<!-- javascripts -->
<!-- <script src="/js/jquery-3.6.0.min.js"></script> -->
+2 -2
View File
@@ -31,8 +31,8 @@
</div>
</div>
<div class="mt-auto post-footer bg-transparent py-3">
<span class="float-start bg-transparent">{{ .Date.Format "January 2, 2006" }}</span>
<a href="{{ .RelPermalink }}" class="float-end btn btn-outline-info btn-sm">Read</a>
<span class="float-start bg-transparent">{{ .Date.Format (.Site.Params.datesFormat.articleList | default "January 2, 2006") }}</span>
<a href="{{ .RelPermalink }}" class="float-end btn btn-outline-info btn-sm">{{ .Site.Params.terms.read | default "Read" }}</a>
</div>
</div>
</div>
+22 -5
View File
@@ -21,7 +21,13 @@
<div class="text-center">
{{ .Params.author }}
<small>|</small>
{{ .Date.Format "Jan 2, 2006" }}
{{ .Date.Format (.Site.Params.datesFormat.article | default "Jan 2, 2006") }}
{{ if or (.Site.Params.singlePages.readTime.enable | default true) (.Params.enableReadingTime) }}
<span id="readingTime">
{{ .Site.Params.singlePages.readTime.content | default "min read" }}
</span>
{{ end }}
</div>
</div>
{{ if .Params.image }}
@@ -39,7 +45,7 @@
{{ if .Params.toc | default true}}
<aside class="toc">
<h5>
Table Of Contents
{{ .Site.Params.terms.toc | default "Table Of Contents" }}
</h5>
<div class="toc-content">
{{.TableOfContents}}
@@ -49,7 +55,7 @@
{{ if .Params.tags }}
<aside class="tags">
<h5>Tags</h5>
<h5>{{ .Site.Params.terms.tags | default "Tags" }}</h5>
<ul class="tags-ul list-unstyled list-inline">
{{range .Params.tags}}
<li class="list-inline-item"><a href="{{`tags` | absURL}}/{{.| urlize}}" target="_blank">{{.}}</a></li>
@@ -60,7 +66,7 @@
{{ if .Params.socialShare | default true }}
<aside class="social">
<h5>Social</h5>
<h5>{{ .Site.Params.terms.social | default "Social" }}</h5>
<div class="social-content">
<ul class="list-inline">
<li class="list-inline-item text-center">
@@ -74,7 +80,7 @@
</a>
</li>
<li class="list-inline-item text-center">
<a target="_blank" href="mailto:?subject={{ .Title }}&amp;body=Check out this site {{ .Site.Params.hostName }}{{ .Permalink | absURL }}">
<a target="_blank" href='mailto:?subject={{ .Title }}&amp;body={{ .Site.Params.terms.emailText | default "Check out this site" }} {{ .Site.Params.hostName }}{{ .Permalink | absURL }}'>
<i class="fa fa-envelope"></i>
</a>
</li>
@@ -96,6 +102,13 @@
</button>
</section>
{{ if or (.Site.Params.singlePages.scrollprogress.enable | default true) (.Params.enableScrollProgress) }}
<div class="progress">
<div id="scroll-progress-bar" class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
</div>
<Script src="{{.Site.Params.staticPath}}/js/scrollProgressBar.js"></script>
{{ end }}
<script>
var topScroll = document.getElementById("topScroll");
window.onscroll = function() {scrollFunction()};
@@ -114,4 +127,8 @@
}
</script>
{{ if or (.Site.Params.singlePages.readTime.enable | default true) (.Params.enableReadingTime) }}
<script src="{{.Site.Params.staticPath}}/js/readingTime.js"></script>
{{end}}
{{ end }}
+6 -16
View File
@@ -22,7 +22,7 @@
document.body.classList.add('dark');
} else if (localStorage.getItem("pref-theme") === "light") {
document.body.classList.remove('dark')
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
} else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.body.classList.add('dark');
}
@@ -49,22 +49,8 @@
</script>
{{- end }}
<script>
let loadingIcons;
function loading() {
myVar = setTimeout(showPage, 100);
}
function showPage() {
try{
document.getElementById("loading-icons").style.display = "block";
} catch(err) {}
}
</script>
{{ if not (.Site.Params.navbar.disableSearch | default false) }}
<script src="/js/search.js"></script>
<script src="{{ .Site.Params.staticPath }}/js/search.js"></script>
{{ end }}
@@ -98,3 +84,7 @@
</script>
{{ end }}
{{ if (.Site.Params.contact.formspree.enable | default false) }}
<script src="{{ .Site.Params.staticPath }}/js/contact.js"></script>
{{ end }}
+28 -1
View File
@@ -8,7 +8,21 @@
<div class="text-center">
{{ .Site.Params.contact.content | emojify | markdownify }}
</div>
{{ if .Site.Params.contact.email }}
{{ if .Site.Params.contact.formspree.enable | default false }}
<div class="row justify-content-center">
<form id="contact-form" action="https://formspree.io/f/{{ .Site.Params.contact.formspree.formId }}" onsubmit="handleFormspreeSubmit(event)" method="POST" class="col-md-7">
<div class="form-group pt-3">
<input type="email" class="form-control" name="email" required="true" placeholder='{{ .Site.Params.contact.formspree.emailCaption | emojify | default "Enter your email" }}'>
</div>
<div class="form-group pt-3">
<textarea class="form-control" name="message" required="true" placeholder='{{ .Site.Params.contact.formspree.messageCaption | emojify | default "Enter your message" }}' rows="3"></textarea>
</div>
<div class="form-group text-center pt-3">
<button type="submit" class="btn">{{ .Site.Params.contact.btnName | default "Get in Touch" }}</button>
</div>
</form>
</div>
{{ else if .Site.Params.contact.email }}
<div class="text-center pt-3">
<a href="mailto:{{ .Site.Params.contact.email }}" class="btn">
{{ .Site.Params.contact.btnName | default "Get in Touch" }}
@@ -19,5 +33,18 @@
</div>
</div>
</div>
<div id="contact-form-status"></div>
</section>
{{ end }}
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="check-circle-fill" viewBox="0 0 16 16">
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
</symbol>
<symbol id="info-fill" viewBox="0 0 16 16">
<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
</symbol>
<symbol id="exclamation-triangle-fill" viewBox="0 0 16 16">
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
</symbol>
</svg>
+1 -1
View File
@@ -5,7 +5,7 @@
<div class="row justify-content-center">
<div class="col-sm-12 col-md-8 col-lg-8 py-5">
<div class="experience-container px-3 pt-2">
<ul class="nav nav-pills mb-3 bg-transparent" id="pills-tab" role="tablist">
<ul class="nav nav-pills mb-3 bg-transparent primary-font" id="pills-tab" role="tablist">
{{ range $index, $element := .Site.Params.experience.items }}
{{ if (eq $index 0) }}
<li class="nav-item px-1 bg-transparent" role="presentation">
@@ -7,7 +7,7 @@
height="40px" width="40px">
</a>
</div>
&copy; {{ now.Format "2006"}} {{ .Site.Params.copyright }} All Rights Reserved
&copy; {{ now.Format "2006"}} {{ .Site.Params.copyright }} {{ .Site.Params.terms.copyright | default "All Rights Reserved" }}
<div class="text-secondary">
Made with
<span class="text-danger">
@@ -26,8 +26,8 @@
</div>
</div>
<div class="mt-auto card-footer">
<span class="float-start">{{ .Date.Format "January 2, 2006" }}</span>
<a href="{{ .RelPermalink }}" class="float-end btn btn-outline-info btn-sm">Read</a>
<span class="float-start">{{ .Date.Format (.Site.Params.datesFormat.articleRecent | default "January 2, 2006") }}</span>
<a href="{{ .RelPermalink }}" class="float-end btn btn-outline-info btn-sm">{{ .Site.Params.terms.read | default "Read" }}</a>
</div>
</div>
</div>
+16 -19
View File
@@ -1,25 +1,22 @@
{{- /* theme-toggle is enabled */}}
{{- if (not .Site.Params.theme.disableThemeToggle | default false) }}
{{- /* theme is auto */}}
<script>
if (localStorage.getItem("pref-theme") === "dark") {
let localStorageValue = localStorage.getItem("pref-theme");
let mediaQuery = window.matchMedia('(prefers-color-scheme: dark)').matches;
switch (localStorageValue) {
case "dark":
document.body.classList.add('dark');
} else if (localStorage.getItem("pref-theme") === "light") {
document.body.classList.remove('dark')
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
break;
case "light":
document.body.classList.remove('dark');
break;
default:
if (mediaQuery) {
document.body.classList.add('dark');
}
</script>
{{- /* theme-toggle is disabled and theme is auto */}}
{{- else if (and (ne .Site.Params.theme.defaultTheme "light") (ne .Site.Params.theme.defaultTheme "dark"))}}
<script>
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.body.classList.add('dark');
break;
}
</script>
{{- end }}
<!-- Navbar -->
@@ -28,7 +25,7 @@
<div class="container-fluid mx-xs-2 mx-sm-5 mx-md-5 mx-lg-5">
<!-- navbar brand -->
<a class="navbar-brand primary-font text-wrap" href="{{ .Site.BaseURL | relURL }}">
{{ if or (.Site.Params.favicon) (.Site.Params.navbar.brandLogo) }}
{{ if and (or (.Site.Params.favicon) (.Site.Params.navbar.brandLogo)) .Site.Params.navbar.showBrandLogo | default true }}
<img src="{{ .Site.Params.navbar.brandLogo | default .Site.Params.favicon }}" width="30" height="30"
class="d-inline-block align-top">
{{ .Site.Params.navbar.brandName | default .Site.Params.title }}
@@ -39,7 +36,7 @@
{{ if not (.Site.Params.navbar.disableSearch | default false) }}
<div>
<input id="search" autocomplete="off" class="form-control mr-sm-2 d-none d-md-block" placeholder="Search ..."
<input id="search" autocomplete="off" class="form-control mr-sm-2 d-none d-md-block" placeholder='{{ .Site.Params.navbar.searchPlaceholder | default "Ctrl + k to Search..."}}'
aria-label="Search" oninput="searchOnChange(event)">
</div>
{{ end }}
@@ -58,7 +55,7 @@
{{ if not (.Site.Params.navbar.disableSearch | default false) }}
<li class="nav-item navbar-text d-block d-md-none">
<div class="nav-link">
<input id="search" autocomplete="off" class="form-control mr-sm-2" placeholder="Search ..." aria-label="Search" oninput="searchOnChange(event)">
<input id="search" autocomplete="off" class="form-control mr-sm-2" placeholder='{{ .Site.Params.navbar.searchPlaceholder | default "Ctrl + k to Search..."}}' aria-label="Search" oninput="searchOnChange(event)">
</div>
</li>
{{ end }}
@@ -89,7 +86,7 @@
</li>
{{ end }}
{{ if and (.Site.Params.projects.enable | default false) (not (.Site.Params.navbar.menus.disableEducation | default false)) }}
{{ if and (.Site.Params.projects.enable | default false) (not (.Site.Params.navbar.menus.disableProjects | default false)) }}
<li class="nav-item navbar-text">
<a class="nav-link" href="{{ .Site.BaseURL | relURL }}#projects"
aria-label="projects">
+3 -1
View File
@@ -35,7 +35,7 @@
<div class="row justify-content-center">
<div class="col-sm-12 col-md-9 pt-5 image {{ if .Site.Params.animate }}animate{{ end }} px-5 px-md-5 px-lg-0 text-center">
<img src="{{ .Site.Params.hero.image }}"
class="img-thumbnail mx-auto"
class="img-thumbnail mx-auto{{ if .Site.Params.hero.roundImage }} rounded-circle{{ end }}"
alt=""
>
</div>
@@ -43,6 +43,7 @@
</div>
</div>
</div>
{{ if .Site.Params.hero.bottomImage.enable | default true }}
<div class="hero-bottom-svg d-md-block d-lg-block d-none">
<svg xmlns="http://www.w3.org/2000/svg" width="201" height="201" viewBox="0 0 201 201">
<g id="Group_1168" data-name="Group 1168" transform="translate(-384 -1392)">
@@ -305,5 +306,6 @@
</g>
</svg>
</div>
{{ end }}
</section>
{{ end }}
+1 -1
View File
@@ -1,4 +1,4 @@
<span id="loading-icons" style="display: {{ if .Site.Params.animate }}none{{else}}block{{ end }};">
<span>
{{ range .Site.Params.hero.socialLinks.fontAwesomeIcons }}
<span class="px-1">
<a href="{{ .url }}" target="_blank" class="btn social-icon">
File diff suppressed because one or more lines are too long
+37
View File
@@ -537,3 +537,40 @@ header .navbar.animate {
#contact .btn:focus {
box-shadow: none !important;
}
#contact form .form-control {
background-color: var(--secondary-color);
color: var(--text-color);
border-radius: .7rem;
border: 1px solid var(--text-secondary-color);
box-shadow: 0px 8px 56px rgb(15 80 100 / 5%);
}
#contact-form-status {
position: fixed;
bottom: 10px;
right: 10px;
z-index: 1;
transform: translate3d(0, 0, 0);
}
#contact-form-status svg {
height: 18px;
width: 18px;
}
#contact-form-status button {
border-radius: 50%;
border: none;
background-color: white;
padding: 0.5rem;
margin-left: 0.5rem;
box-shadow: 0px 8px 56px rgb(15 80 100 / 5%);
font-size: .6rem !important;
}
#contact-form-status .alert {
border-radius: 0.5rem;
box-shadow: 0px 8px 56px rgb(15 80 100 / 5%);
padding: .5rem 1rem;
}
+27
View File
@@ -25,6 +25,11 @@
background-color: var(--secondary-color);
}
#single .page-content img {
max-width: 100%;
border-radius: 1rem;
}
#single .page-content a {
display: inline-block;
text-decoration: none;
@@ -282,3 +287,25 @@
transition: .5s;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.2);
}
/* Singlepage scroll progress start */
.progress {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 2px;
background-color: var(--background-color);
z-index: 999;
}
.progress-bar {
position: absolute;
top: 0;
left: 0;
height: 100%;
background-color: var(--primary-color);
transition: width .2s;
}
/* Singlepage scroll progress end */
+45
View File
@@ -0,0 +1,45 @@
async function handleFormspreeSubmit(event) {
event.preventDefault();
var form = document.getElementById("contact-form");
var data = new FormData(event.target);
fetch(event.target.action, {
method: form.method,
body: data,
headers: {
Accept: "application/json",
},
})
.then((response) => {
if (response.ok) {
contactAlert("success", "Thanks for your submission!");
form.reset();
} else {
response.json().then((data) => {
var errMessage = data.errors;
for (var i = 0; i < errMessage.length; i++) {
contactAlert("danger", errMessage[i].message);
}
});
}
})
.catch((error) => {
contactAlert("danger", "Oops! There was a problem submitting your form");
});
}
function contactAlert(type, message) {
var contactFormStatus = document.getElementById("contact-form-status");
var alert = `<div class="alert alert-${type} d-flex align-items-center" role="alert">
<svg class="bi flex-shrink-0 me-2" role="img" aria-label="Success:">
<use xlink:href="#check-circle-fill" />
</svg>
<div>${message}</div>
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>`;
contactFormStatus.innerHTML = alert;
// Remove alert after 3 seconds
setTimeout(function () {
contactFormStatus.innerHTML = "";
}, 3000);
}
+10
View File
@@ -0,0 +1,10 @@
function readingTime() {
const text = document.querySelector("article").innerText;
const wpm = 225;
const words = text.trim().split(/\s+/).length;
const time = Math.ceil(words / wpm);
const timeElement = document.querySelector("span#readingTime");
timeElement.innerHTML = "<small> | </small>" + time + timeElement.innerHTML;
}
readingTime();
+12
View File
@@ -0,0 +1,12 @@
function getScrollPercent() {
const totalHeight = document.body.scrollHeight - window.innerHeight;
const scrolled = window.scrollY;
return (scrolled / totalHeight) * 100;
}
const scrollProgressBar = document.getElementById("scroll-progress-bar");
document.onscroll = function () {
var scrollPercent = Math.round(getScrollPercent());
scrollProgressBar.style.width = scrollPercent + "%";
scrollProgressBar.ariaValueNow = scrollPercent;
};
+79 -14
View File
@@ -1,23 +1,15 @@
async function searchOnChange(evt) {
let searchQuery = evt.target.value;
var inputEle = document.querySelectorAll("input#search");
inputEle.forEach((element) => {
element.value = searchQuery;
});
if (searchQuery !== "") {
const searchButtonEle = document.querySelectorAll("#search");
let searchButtonPosition;
if (window.innerWidth > 768) {
searchButtonPosition = searchButtonEle[0].getBoundingClientRect();
document.getElementById("search-content").style.width = "500px";
} else {
searchButtonPosition = searchButtonEle[1].getBoundingClientRect();
document.getElementById("search-content").style.width = "300px";
if (!window.searchJson) {
window.searchJson = await fetch("/index.json").then((res) => res.json());
}
document.getElementById("search-content").style.top =
searchButtonPosition.top + 50 + "px";
document.getElementById("search-content").style.left =
searchButtonPosition.left + "px";
let searchJson = await fetch("/index.json").then((res) => res.json());
let searchResults = searchJson.filter((item) => {
let res = false;
if (item.title && item.description && item.content) {
@@ -65,9 +57,82 @@ async function searchOnChange(evt) {
let searchResultsHtml = `<p class="text-center py-3">No results found for "${searchQuery}"</p>`;
document.getElementById("search-results").innerHTML = searchResultsHtml;
}
alignSearchContent();
document.getElementById("search-content").style.display = "block";
} else {
document.getElementById("search-content").style.display = "none";
document.getElementById("search-results").innerHTML = "";
}
}
function alignSearchContent() {
const searchButtonEle = document.querySelectorAll("#search");
// check if search value is not empty
for (let i = 0; i < searchButtonEle.length; i++) {
if (searchButtonEle[i].value !== "") {
let searchButtonPosition;
if (window.innerWidth > 768) {
searchButtonPosition = searchButtonEle[0].getBoundingClientRect();
document.getElementById("search-content").style.width = "500px";
} else {
var navbarCollapse = document.querySelector("#navbarContent");
navbarCollapse.classList.add("show");
searchButtonPosition = searchButtonEle[1].getBoundingClientRect();
document.getElementById("search-content").style.width = "300px";
}
document.getElementById("search-content").style.top =
searchButtonPosition.top + 50 + "px";
document.getElementById("search-content").style.left =
searchButtonPosition.left + "px";
}
}
}
function resetSearch(e) {
if (
e.keyCode === 27 ||
(e.target.id !== "search" &&
e.target.closest("section#search-content") === null)
) {
if (document.getElementById("search-results").innerHTML !== "") {
document.getElementById("search-content").style.display = "none";
document.getElementById("search-results").innerHTML = "";
var inputEle = document.querySelectorAll("input#search");
inputEle.forEach((element) => {
element.value = "";
element.blur();
});
}
}
}
document.onkeyup = function () {
switch (event.keyCode) {
// ESC
case 27:
resetSearch(event);
break;
// ctrl + k
case 75:
if (event.ctrlKey) {
document.getElementById("search").focus();
}
break;
}
};
window.addEventListener("keydown", function (e) {
if (e.keyCode === 75 && e.ctrlKey) {
e.preventDefault();
}
});
// Close search on click outside and on resize
document.addEventListener("click", function (e) {
resetSearch(e);
});
window.addEventListener("resize", function (e) {
alignSearchContent();
});