Compare commits

..

52 Commits

Author SHA1 Message Date
e843870afd Increased font-weight to make links more readable 2023-10-27 09:51:31 +01:00
74dc1196cd Adapted feedback regarding initial messages not showing for long enough. 2023-10-27 09:25:00 +01:00
306396cce1 Changed min width before content starts dissapearing on smalled devices 2023-10-26 22:02:42 +01:00
a2acc03f7c Updated dependencies to resolve vulnerability 2023-10-26 21:49:13 +01:00
6f0f5f1cf7 Added ability to switch between light and dark mode 2023-10-26 21:41:18 +01:00
278fc640ce Added pulsing effect to timeline elements that are still in progress 2023-10-26 13:19:46 +01:00
2e07de0e71 Adapted themings to allow for light and dark 2023-10-26 10:24:30 +01:00
e0ea53a9d3 Re-ordered experience in json file 2023-10-25 14:18:37 +01:00
632eed6810 Updated timeline data colouring to add contrast 2023-10-25 13:51:18 +01:00
dd4dfdea78 Added author and descriptiom to webpage 2023-10-25 13:04:38 +01:00
377f87ced3 Added meta description tag to website 2023-10-25 11:54:16 +01:00
d8d40ddc30 CHORE: Cleaned up components to make main.svelte easier to maintain. 2023-10-25 11:28:22 +01:00
ee2098e6e6 Added timeline component to website 2023-10-25 10:54:39 +01:00
d2066087ae Added actual content to be displayed on cards. May need corrections to be made. 2023-10-13 14:38:04 +01:00
d187ec70c3 #1 Added 'X' to popup cards, adjusted skills container and added heading 2023-10-04 22:07:53 +01:00
4a734b66f9 Updated flex basis on card to make sure that card don't end up super thin 2023-10-04 21:01:40 +01:00
cbdc81c4ce Completed contact form and improved styling on cards 2023-09-29 14:41:37 +01:00
7bd03be127 Created basis for contact page and updated CV 2023-09-28 21:30:41 +01:00
8d5319ac4a Moved cards out into seperate component to enable re-usability 2023-09-28 19:22:52 +01:00
26357e531d External links now open in a new window. 2023-09-28 07:15:13 +01:00
eac1534497 Updated Favicon 2023-09-27 20:11:16 +01:00
94de27084c Updated docker file to use production environment instead of dev 2023-09-27 19:25:07 +01:00
0a6ede8125 Created dockerfile 2023-09-26 23:26:35 +01:00
ebf9a21478 Created base template for contact and repo pages.
Fixed styling issues on toasts on small screens.
2023-09-26 23:15:42 +01:00
86652a4f09 Updated content, created modal and changed skills card to style nicer on smaller screens 2023-09-26 20:09:17 +01:00
3f171dea3c Removed mastery data, don't think it is a necessary item to include 2023-09-25 22:14:24 +01:00
6b0af2fb3e Created skill cards 2023-09-25 21:38:42 +01:00
1b33bd398d Moved Toast logic into own directory 2023-09-19 14:22:00 +01:00
f31f180687 Fixed toasts not working. 2023-09-18 13:02:55 +01:00
8b9a2ac8d5 Uploading toast code... incomplete 2023-09-18 10:17:02 +01:00
212103ab71 Updated personal content and prepared async fetch to be more expandable and include more content rather than a single div. 2023-09-06 20:42:07 +01:00
7d7012eec6 Made more reactive styling on main page and template 2023-09-05 22:53:56 +01:00
b2b56480b8 Created JS function to fetch static JSON. Now loads data Asynchronously :) 2023-09-05 19:03:59 +01:00
79795bc060 Added addional styling to smaller devices to reduce the padding of main content paragraphs 2023-09-04 22:58:24 +01:00
6961d9c34d Created underline on heading element 2023-09-04 22:51:27 +01:00
0797fd9eff Added json content display and updated styling on main card item. 2023-09-04 22:47:39 +01:00
d69078ff26 Created new main card and created a container for inner element of main page. 2023-08-31 22:27:21 +01:00
ba437de706 Created Navbar and added styling to links 2023-08-30 23:37:44 +01:00
065a6a2d17 Created SvelteKit Application as Base 2023-08-29 20:48:14 +01:00
3eb3b6845b Updated line numbers on flip card 2023-05-19 14:51:25 +01:00
71227d7d78 Updated flip card to use rust syntax 2023-05-19 14:45:28 +01:00
52c1abb0b7 Corrected Typo in metadata 2022-05-09 21:31:13 +01:00
de234ddf8c Added metadata to flipcard site 2022-05-09 21:24:04 +01:00
3769f3edc3 Updated flipcard content 2022-04-28 18:21:38 +01:00
87b2e808f7 Added age.js to allow for automatic age update 2022-04-28 18:18:49 +01:00
189ca5034b Updated main page to follow C# syntax 2022-04-28 18:13:31 +01:00
71405fd5a2 Added CV to assets for later reference 2022-04-25 22:35:55 +02:00
357f0b06f3 Allow card to flip both ways 2022-04-22 22:39:25 +01:00
30a5d53bc0 Tap to flip card - hopefully 2022-04-22 22:22:28 +01:00
bd183bf832 Added Mobile Hover to card 2022-04-22 21:24:37 +01:00
69cfbf1b44 Created Business Card Site 2022-04-22 20:32:38 +01:00
a75c919929 Clear Repo 2022-04-22 20:00:18 +01:00
61 changed files with 3617 additions and 10619 deletions

18
.dockerignore Normal file
View File

@ -0,0 +1,18 @@
# .dockerignore
.vscode
node_modules
.git
.gitattributes
.eslintignore
.eslintrc.cjs
.prettierrc
.pretieriignore
README.md
Dockerfile

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.vscode

2
.npmrc Normal file
View File

@ -0,0 +1,2 @@
engine-strict=true
resolution-mode=highest

13
.prettierignore Normal file
View File

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

9
.prettierrc Normal file
View File

@ -0,0 +1,9 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

11
.vscode/settings.json vendored
View File

@ -1,11 +0,0 @@
{
"spellright.language": [
"en"
],
"spellright.documentTypes": [
"markdown",
"latex",
"plaintext",
"html"
]
}

38
README.md Normal file
View File

@ -0,0 +1,38 @@
# create-svelte
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm create svelte@latest
# create a new project in my-app
npm create svelte@latest my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

View File

@ -1,15 +0,0 @@
*::-webkit-scrollbar,
*::-webkit-scrollbar-thumb {
width: 26px;
border-radius: 13px;
background-clip: padding-box;
border: 10px solid transparent;
color: rgb(108, 117, 125, 0.7);
}
*::-webkit-scrollbar-thumb:hover{
color: rgb(108, 117, 125, 1);
}
*::-webkit-scrollbar-thumb {
box-shadow: inset 0 0 0 10px;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
#type{
display: inline;
border-right: 4px solid #6c757d;
animation: blink 1s step-end infinite;
color: rgba(36, 129, 60, 0.7);
}
@keyframes blink {
50%{
border-right: none;
}
}
body{
opacity: 0;
transition: opacity 0.75s;
}

20
dockerfile Normal file
View File

@ -0,0 +1,20 @@
FROM node:lts-slim as build
WORKDIR /app
COPY package*.json ./
RUN rm -rf node_modules
RUN rm -rf build
COPY . .
RUN npm install
RUN npm run build
FROM node:lts-slim as run
WORKDIR /app
COPY --from=build /app/package.json ./package.json
COPY --from=build /app/build ./build
RUN npm install --omit=dev
EXPOSE 3000
ENTRYPOINT [ "npm", "run", "start" ]

View File

@ -1,270 +0,0 @@
<!DOCTYPE html>
<html lang="en" loading="lazy">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="Luke Else - Junior Software Developer from Kent. I specialise in C# and dotnet development but also have experience in GoLang and Java Script (+ Node). I've been developing Apps and Backend Web-Systems since Early 2020 and have learned a lot of skills and experience on the way!" />
<meta name="author" content="" />
<title>Luke Else - Junior Software Developer</title>
<link rel="icon" type="image/x-icon" href="assets/img/favicon.ico" />
<!-- Font Awesome icons-->
<script src="https://kit.fontawesome.com/5e5c5ee5fe.js" crossorigin="anonymous"></script>
<!-- Dev Icons-->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.14.0/devicon.min.css">
<!-- Google fonts-->
<link href="https://fonts.googleapis.com/css?family=Saira+Extra+Condensed:500,700" rel="stylesheet" type="text/css" />
<link href="https://fonts.googleapis.com/css?family=Muli:400,400i,800,800i" rel="stylesheet" type="text/css" />
<!-- Core theme CSS (includes Bootstrap)-->
<link href="css/styles.css" rel="stylesheet" />
<link href="css/type.css" rel="stylesheet" />
<link href="css/scroll.css" rel="stylesheet" />
<script src="js/age.js"></script>
</head>
<body id="page-top" onload="document.body.style.opacity='1'">
<!-- Navigation-->
<nav class="navbar navbar-expand-lg navbar-dark bg-secondary fixed-top" id="sideNav">
<a class="navbar-brand js-scroll-trigger" href="#page-top">
<span class="d-block d-lg-none">Luke Else</span>
<span class="d-none d-lg-block"><img class="img-fluid img-profile rounded-circle mx-auto mb-2" src="assets/img/profile.jpg" alt="Luke Else" /></span>
</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#about">About</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#experience">Experience</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#projects">Projects</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#education">Education</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#skills">Skills</a></li>
<li class="nav-item"><a class="nav-link js-scroll-trigger" href="#interests">Interests</a></li>
</ul>
</div>
</nav>
<!-- Page Content-->
<div class="container-fluid p-0">
<!-- About-->
<section class="resume-section" id="about">
<div class="resume-section-content">
<h1 class="mb-0">
<span class="text-secondary">{</span>
Luke
<span class="text-primary">Else</span>
<span class="text-secondary">}</span>
</h1>
<br />
<h2 class="mb-0">
<p id="type"></p>
</h2>
<br />
<div class="subheading mb-5">
<script>
document.write("Age: <span class='text-dark'>" + getAge(new Date("2004-01-12")) + "</span>")
</script>
<br />
Mobile: <span class='text-dark'> +44 7498 289321 </span>
<br />
E-Mail:
<a href="mailto:contact@luke-else.co.uk">contact@luke-else.co.uk</a>
</div>
<p class="lead mb-5">I am a Junior but well-practised Software Engineer currently employed at Thales UK. I have a great passion for learning, especially in topics oriented around Software Development, Networks and Backend Engineering. I am currently in the process of learning <a href="https://www.rust-lang.org">Rust-Lang</a> and hope to use it in my upcoming projects</p>
<div class="social-icons">
<a target="_blank" class="social-icon" href="https://www.linkedin.com/in/luke-else-a7183a205/"><i class="fab fa-linkedin-in"></i></a>
<a target="_blank" class="social-icon" href="https://git.luke-else.co.uk/luke-else/"><i class="fab fa-git-alt"></i></a>
<a target="_blank" class="social-icon" href="https://github.com/luke-else"><i class="fab fa-github"></i></a>
<a target="_blank" class="social-icon" href="assets/Luke Else - CV.pdf"><i class="fas fa-file-download"></i></a>
<a target="_blank" class="social-icon" href="https://info.luke-else.co.uk"><i class="fa-regular fa-address-card"></i></a>
</div>
</div>
</section>
<hr class="m-0" />
<!-- Experience-->
<section class="resume-section" id="experience">
<div class="resume-section-content">
<h2 class="mb-5">Experience</h2>
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">Software Engineer</h3>
<div class="subheading mb-3">Thales UK</div>
<p>As a software engineering apprentice at Thales UK, I find myself partaking in agile / scrum development methodologies in a strong team of 6 other engineers. The team iterates on a pre-existing system designed for the MOD, written in C++ using internal frameworks to assist.</p>
</div>
<div class="flex-shrink-0"><span class="text-primary">September 2022 - Current</span></div>
</div>
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">Trading Assistant</h3>
<div class="subheading mb-3">Sainsbury's</div>
<p>During my 6 months at Sainsbury's, I worked hard to ensure shelves were stocked whilst also assisting with the preparation and execution of promotional strategies through the Spring and Summer seasons.</p>
</div>
<div class="flex-shrink-0"><span class="text-primary">February 2022 - August 2022</span></div>
</div>
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">Car Valeter</h3>
<div class="subheading mb-3">CSM Valeting</div>
<p>This job involved ensuring customer satisfaction was very high! Every car had to be cleaned meticulously to ensure the highest standard was achieved. We often spent a lot of our time in commercial workspace environments and so remaining professional was key to the service we provided. I enjoyed working here a lot because of both the independent and teamworking aspects. On many occasions we would find ourselves working on a car in a group so communication was key to ensure that no part was missed nor any unnecessary work was undertaken!</p>
</div>
<div class="flex-shrink-0"><span class="text-primary">March 2020 - February 2022</span></div>
</div>
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">Site Maintenance</h3>
<div class="subheading mb-3">Ashford Self Storage</div>
<p>Working at Ashford Self Storage was my first taste of working in the real world. I spent a lot of time interacting with the customers throughout the day whilst also maintaining the cleanliness and presentability of the site. I was in charge of making sure the loading bay was clear and safe to use in addition to landscaping out the front and sides of the warehouse building!</p>
</div>
<div class="flex-shrink-0"><span class="text-primary">July 2018 - September 2020</span></div>
</div>
</div>
</section>
<hr class="m-0" />
<!-- Projects-->
<section class="resume-section" id="projects">
<div class="resume-section-content">
<h2 class="mb-5">Projects</h2>
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">snexo.co.uk</h3>
<div class="subheading mb-3">Snexo</div>
<p>This project is a templated website designed for a client that needed a platform to advertise his clearance services. The website is made using Bootstrap and JQuery along with PHP for some of the backend features. I am hosting the site along with its E-Mail accounts on my own web and mail servers which guarantees a near 100% uptime!</p>
<!--<img class="img-fluid mx-auto mb-2" src="assets/img/snexo.jpg" alt="snexo.co.uk" /> -->
</div>
<div class="flex-shrink-0"><span class="text-primary"><a href="https://snexo.co.uk">snexo.co.uk</a></span></div>
</div>
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">Hour Logging System</h3>
<div class="subheading mb-3">Personal Project</div>
<p>This project is a web application designed to make an easy way for employees to log the hours that they are working. All they have to do is clock in at the start of the day and clock back out once they have finished. The app is being developed using ASP.Net 5 with MongoDB acting as a permanent data store for each user's data.</p>
</div>
<div class="flex-shrink-0"><span class="text-primary"><a href="https://github.com/luke-else/Hour-Logging-System">GitHub</a></span></div>
</div>
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">Flight Simulator EFB</h3>
<div class="subheading mb-3">A-Level Project</div>
<p>This project is probably one of the largest so far! It is designed for flight sim users to be able to brief and track their flights as well as giving them the ability to fetch relevant routes and charts with just the click of a button. The project is a part of my A-Level course and thus is heavily documented at all stages of the application development life cycle. It has taught me a lot about the agile development ideology, something that will be really useful to apply to some of my next projects!</p>
</div>
<div class="flex-shrink-0"><span class="text-primary"><a href="https://github.com/Flight-Simulator-EFB">GitHub</a></span></div>
</div>
</div>
</section>
<hr class="m-0" />
<!-- Education-->
<section class="resume-section" id="education">
<div class="resume-section-content">
<h2 class="mb-5">Education</h2>
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">University Of Warwick</h3>
<div class="subheading mb-3">BSc Digital Solutions Level 6 Integrated Degree</div>
<div>Provided through Thales UK.</div>
<div>Grade On Track: (<b>1:1</b>)</div>
</div>
<div class="flex-shrink-0"><span class="text-primary">September 2022 - current</span></div>
</div>
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">The Norton Knatchbull School</h3>
<div class="subheading mb-3">A-Levels</div>
<div>Computer Science (<b>A*</b>), Maths (<b>A</b>), Physics (<b>A</b>)</div>
</div>
<div class="flex-shrink-0"><span class="text-primary">September 2020 - July 2022</span></div>
</div>
<div class="d-flex flex-column flex-md-row justify-content-between mb-5">
<div class="flex-grow-1">
<h3 class="mb-0">The Norton Knatchbull School</h3>
<div class="subheading mb-3">GCSE's</div>
<div>FSMQ (C), Maths (8), Geography (<b>9</b>), Biology (<b>9</b>), Chemistry (<b>9</b>), Physics (<b>9</b>), Spanish (7), English (Literature & Language) (7, 7), Computer Science (<b>9</b>)</div>
</div>
<div class="flex-shrink-0"><span class="text-primary">September 2015 - August 2020</span></div>
</div>
</div>
</section>
<hr class="m-0" />
<!-- Skills-->
<section class="resume-section" id="skills">
<div class="resume-section-content">
<h2 class="mb-5">Skills</h2>
<div class="subheading mb-3">Programming Languages & Tools</div>
<ul class="list-inline dev-icons">
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="GoLang" class="fab fa-rust"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="Bash" class="devicon-c-plain"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="Bash" class="devicon-cplusplus-plain"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="C#" class="devicon-csharp-plain"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="Linux" class="fab fa-linux"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="Git" class="fab fa-git-alt"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="Bash" class="devicon-bash-plain"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="docker" class="devicon-docker-plain"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="NGINX" class="devicon-nginx-plain"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="GoLang" class="fab fa-golang"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="HTML 5" class="fab fa-html5"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="CSS 3" class="fab fa-css3-alt"></i></li>
<li class="list-inline-item"><i data-toggle="tooltip" data-placement="bottom" title="Java Script" class="fab fa-js-square"></i></li>
</ul>
<div class="subheading mb-3">Workflow</div>
<ul class="fa-ul mb-0">
<li>
<span class="fa-li"><i class="fas fa-check"></i></span>
Competent in Agile / Scrum Methodologies and workflows
</li>
<li>
<span class="fa-li"><i class="fas fa-check"></i></span>
Confident using Version Control tools such as Git and Subversion.
</li>
<li>
<span class="fa-li"><i class="fas fa-check"></i></span>
In depth knowledge of programming and computer system theory
</li>
<li>
<span class="fa-li"><i class="fas fa-check"></i></span>
Well practiced in Software Development and Engineering
</li>
</ul>
</div>
</section>
<hr class="m-0" />
<!-- Interests-->
<section class="resume-section" id="interests">
<div class="resume-section-content">
<h2 class="mb-5">Interests</h2>
<p>In my spare time, I will often find myself working on some of my smaller projects, writing small scalable apps in Rust. Check out my <a href="https://git.luke-else.co.uk/luke-else/">remote git server</a> to see what I am working on now.</p>
<p>I am also an avid member of the VATSIM flight simulation community and spend my evenings interacting with the many members that it holds. I operate as a virtual air traffic controller and will also find myself flying on the network in some of the many events they host each week.</p>
<p class="mb-0">From 2015 to 2020 I was in the 'Royal Air Force Air Cadets' which taught me a lot of leadership, communication and problem solving skills. Whilst there, I partook in the Bronze Duke Of Edinburgh award and joined the squadron band, both of which were tough but rewarding experiences. Throughout the year we would attend many parades for which our drill and discipline standard had to be impeccable.</p>
</div>
</section>
<hr class="m-0" />
</div>
<!-- Bootstrap core JS-->
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js"></script>
<!-- Third party plugin JS-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js"></script>
<!-- Core theme JS-->
<script src="js/scripts.js"></script>
<script src="js/type.js"></script>
</body>
</html>

View File

@ -1,10 +0,0 @@
function getAge(dateString) {
var today = new Date();
var birthDate = dateString;
var age = today.getFullYear() - birthDate.getFullYear();
var m = today.getMonth() - birthDate.getMonth();
if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
}

View File

@ -1,58 +0,0 @@
class carouseltext{
constructor(elements, carousel, period){
this.elements = elements;
this.carousel = carousel;
this.period = parseInt(period, 10) || 2000;
this.loop = 0;
this.isDeleting = true;
this.text;
}
tick(){
var i = this.loop % this.carousel.length;
var fullTxt = this.toRotate[i];
if (this.isDeleting == true) {
this.txt = fullTxt.substring(0, this.txt.length - 1);
}else{
this.txt = fullTxt.substring(0, this.txt.length + 1);
}
this.element.innerHTML = '<span class="wrap">'+this.txt+'</span>';
var that = this;
var delta = 300 - Math.random() * 100;
if (this.isDeleting) { delta /= 2; }
if (!this.isDeleting && this.txt === fullTxt) {
delta = this.period;
this.isDeleting = true;
} else if (this.isDeleting && this.txt === '') {
this.isDeleting = false;
this.loopNum++;
delta = 500;
}
//Hold the function from executing again until delta time has passed.
setTimeout(function() {
that.tick();
}, delta);
}
textLoad() {
var elements = document.getElementsByClassName('txt-rotate');
for (var i=0; i<elements.length; i++) {
var carousel = elements[i].getAttribute('data-rotate');
var period = elements[i].getAttribute('data-period');
if (toRotate) {
new Text(elements[i], JSON.parse(carousel), period);
}
}
}
}

View File

@ -1,40 +0,0 @@
(function ($) {
"use strict"; // Start of use strict
// Smooth scrolling using anime.js
$('a.js-scroll-trigger[href*="#"]:not([href="#"])').on('click', function () {
if (
location.pathname.replace(/^\//, "") ==
this.pathname.replace(/^\//, "") &&
location.hostname == this.hostname
) {
var target = $(this.hash);
target = target.length ?
target :
$("[name=" + this.hash.slice(1) + "]");
if (target.length) {
anime({
targets: 'html, body',
scrollTop: target.offset().top,
duration: 1000,
easing: 'easeInOutExpo'
});
return false;
}
}
});
// Closes responsive menu when a scroll trigger link is clicked
$(".js-scroll-trigger").on('click', function () {
$(".navbar-collapse").collapse("hide");
});
/* Activate scrollspy to add active class to navbar items on scroll
$('body').scrollspy({
target: "#sideNav"
});
*/
})(jQuery); // End of use strict

View File

@ -1,52 +0,0 @@
class typer{
constructor(text, retypeText){
this.i = 0;
this.x = 0;
this.item = document.getElementById("type");
this.text = text;
this.retypeText = retypeText;
}
type(){
this.item.innerHTML += this.text[this.x].charAt(this.i);
this.i++;
if (this.i < this.text[this.x].length) {
setTimeout(() => {
this.type();
}, 150);
}else{
if(this.retypeText == true){
this.i = 0;
setTimeout(() => {
this.retype();
}, 2000);
}
}
}
retype() {
this.item.innerHTML = this.item.innerHTML.substring(0, this.item.innerHTML.length-1);
if (this.item.innerHTML.length > 0) {
setTimeout(() => {
this.retype();
}, 100);
}else{
this.x++;
//this.x > this.text.length -1 || loop
if(this.x >= this.text.length - 1){
//this.x = 0;
this.retypeText = false;
}
this.type();
}
}
}
var pagetyper = new typer(["//Apprentice C++ Software Engineer", "//Aspring Rust Dev"], true);
setTimeout(() =>{
pagetyper.type();
}, 750)

2088
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

29
package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "luke-else.co.uk",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"start": "export PORT=3000 && node ./build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin-search-dir . --check .",
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@rollup/plugin-json": "^6.0.0",
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/adapter-node": "^1.3.1",
"@sveltejs/kit": "^1.20.4",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1",
"svelte": "^4.0.5",
"svelte-check": "^3.4.3",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.4.2"
},
"type": "module"
}

12
src/app.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}
export {};

129
src/app.html Normal file
View File

@ -0,0 +1,129 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="description" content="Luke Else - Software Developer at Thales UK. I specialise in developing distributed systems in C++ using highly scalable internal frameworks. I also develop backend and system applications in my spare time using both Svelte, Rust and C++. Feel free to check my work out at https://git.luke-else.co.uk." />
<meta name="author" content="Luke Else (mail@luke-else.co.uk)" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.15.1/devicon.min.css">
<meta name="viewport" content="width=device-width" />
%sveltekit.head%
<style>
:root {
--font: Consolas, 'Cascadia Code', Monaco, 'SF Mono', 'DejaVu Sans Mono', 'Roboto Mono';
background: var(--bg);
color: var(--fg);
font-family: var(--font);
font-size: 110%;
margin: 2rem;
transition: all 0.3s;
}
h1, h2, h3 {
color: var(--header);
border: 0;
}
hr {
border: .2em solid var(--bg-grad-3);
border-radius: 5em;
width: 100%;
}
*::-webkit-scrollbar,
*::-webkit-scrollbar-thumb {
width: 26px;
border-radius: 13px;
background-clip: padding-box;
border: 10px solid transparent;
color: var(--fg);
}
*::-webkit-scrollbar-thumb:hover{
color: var(--link);
}
*::-webkit-scrollbar-thumb {
box-shadow: inset 0 0 0 10px;
}
@media (max-width:600px) {
.not-required {
display: none;
}
}
a {
text-decoration: none;
position: relative;
color: var(--link);
white-space: nowrap;
}
a:after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 0%;
border-bottom: 2px solid var(--fg);
transition: 0.4s;
}
a:hover:after {
width: 100%;
color: var(--glow);
}
a:hover {
color: var(--glow);
}
a:active {
color: var(--header);
}
.button {
color: var(--fg);
background-color: var(--bg-grad-1);
box-shadow: .1em .1em .1em var(--link);
transition: all 0.2s;
}
.button:hover {
box-shadow: .3em .3em .3em var(--header);
}
@keyframes animationName {
0% { opacity:0; }
50% { opacity:1; }
100% { opacity:0; }
}
@-o-keyframes animationName{
0% { opacity:0; }
50% { opacity:1; }
100% { opacity:0; }
}
@-moz-keyframes animationName{
0% { opacity:0; }
50% { opacity:1; }
100% { opacity:0; }
}
@-webkit-keyframes animationName{
0% { opacity:0; }
50% { opacity:1; }
100% { opacity:0; }
}
.elementToFadeInAndOut {
-webkit-animation: animationName 1.5s infinite;
-moz-animation: animationName 1.5s infinite;
-o-animation: animationName 1.5s infinite;
animation: animationName 1.5s infinite;
}
</style>
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@ -0,0 +1,66 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
function onClick() {
dispatch('click');
}
</script>
<style>
.card {
display: flex;
flex-direction: column;
justify-content: space-between;
flex-wrap: wrap;
flex: 2 1 15em;
padding: .5em 2.5em 2em 2.5em;
background: var(--bg-secondary);
border-radius: .5em;
scroll-snap-align: start;
transition: all 0.2s;
}
.card:hover {
box-shadow: .5em .5em .5em var(--hover);
}
.card .card-header :global(div) {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0em;
width: 100%;
}
.card .card-content :global(div) {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 100%;
}
.card .card-footer :global(div){
margin-bottom: 1em;
display: flex;
gap: 1em;
max-width: 100%;
justify-content: space-between;
}
</style>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="card" on:click={onClick}>
<div class="card-header">
<slot name="header"></slot>
</div>
<hr />
<div class="card-content">
<slot name="content"></slot>
</div>
<hr class="not-required"/>
<div class="card-footer">
<slot name="footer"></slot>
</div>
</div>

View File

@ -0,0 +1,82 @@
<script lang="ts">
export let showModal: boolean;
let dialog: HTMLDialogElement;
$: if (dialog && showModal) dialog.showModal();
import CloseIcon from "./Toasts/CloseIcon.svelte";
</script>
<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-noninteractive-element-interactions -->
<dialog
bind:this={dialog}
on:close={() => (showModal = false)}
on:click|self={() => dialog.close()}
>
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div on:click|stopPropagation>
<slot name="header" />
<hr />
<slot />
<hr />
</div>
<button class="close" on:click={() => dialog.close()}>
<CloseIcon width="0.8em" />
</button>
</dialog>
<style>
dialog {
max-width: 70%;
border-radius: 0.2em;
border: none;
padding: 0em 2em 2em 2em;
border-left: 2em;
border-right: 2em;
background: var(--bg);
color: var(--fg);
box-shadow: .5em .5em .5em var(--header);
}
dialog::backdrop {
background: rgba(0, 0, 0, 0.7);
}
dialog > div {
padding: 1em;
}
dialog[open] {
animation: zoom 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes zoom {
from {
transform: scale(0.95);
}
to {
transform: scale(1);
}
}
dialog[open]::backdrop {
animation: fade 0.3s ease-out;
}
@keyframes fade {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.close {
position: absolute;
top: 1em;
right: 1.5em;
color: var(--fg);
background: transparent;
border: 0 none;
padding: 0;
margin: 0 0 0 auto;
line-height: 1;
font-size: 1.2em;
}
</style>

View File

@ -0,0 +1,102 @@
<script lang="ts">
import { browser } from '$app/environment';
export let darkMode: boolean = true;
function onThemeSwitch() {
darkMode = !darkMode;
localStorage.setItem('theme', darkMode ? 'dark' : 'light');
darkMode
? document.documentElement.classList.add('dark')
: document.documentElement.classList.remove('dark');
}
if (browser) {
if (
localStorage.theme === 'dark' ||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.documentElement.classList.add('dark');
darkMode = true;
} else {
document.documentElement.classList.remove('dark');
darkMode = false;
}
}
</script>
<style>
input {
display: none;
}
.switch {
position: absolute;
top: 0em;
right: 0em;
display: inline-block;
width: 3.75em;
height: 2.125em;
}
.slider {
position: absolute;
cursor: pointer;
background-color: var(--bg-grad-4);
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 80%;
width: 45%;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: var(--bg-grad-1);
}
input:checked + .slider:before {
background: var(--bg);
}
input:checked + .slider:before {
-webkit-transform: translateX(1.625em);
-ms-transform: translateX(1.625em);
transform: translateX(1.625em);
}
.slider.round {
border-radius: 2.125em;
}
.slider.round:before {
border-radius: 50%;
}
</style>
<svelte:head>
<link rel="stylesheet" href={`/themes/${darkMode ? 'dark' : 'light'}.css`} />
</svelte:head>
<div class="toggle-wrapper not-required">
<label class="switch">
<input type="checkbox" checked={darkMode} on:click={onThemeSwitch}>
<span class="slider round"></span>
</label>
</div>

View File

@ -0,0 +1,18 @@
<script>
export let width = "1em"
</script>
<svg
width={width}
style="text-align: center; display: inline-block;"
aria-hidden="true"
focusable="false"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 352 512"
>
<path
fill="currentColor"
d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z"
/>
</svg>

View File

@ -0,0 +1,19 @@
<script>
export let width = "1em"
</script>
<svg
width={width}
style="text-align: center; display: inline-block;"
aria-hidden="true"
focusable="false"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="currentColor"
d="M256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm-11.49 120h22.979c6.823 0 12.274 5.682 11.99 12.5l-7 168c-.268 6.428-5.556 11.5-11.99 11.5h-8.979c-6.433 0-11.722-5.073-11.99-11.5l-7-168c-.283-6.818 5.167-12.5 11.99-12.5zM256 340c-15.464 0-28 12.536-28 28s12.536 28 28 28 28-12.536 28-28-12.536-28-28-28z"
class=""
></path>
</svg>

View File

@ -0,0 +1,18 @@
<script>
export let width = "1em"
</script>
<svg
width={width}
style="text-align: center; display: inline-block;"
aria-hidden="true"
focusable="false"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="currentColor"
d="M256 40c118.621 0 216 96.075 216 216 0 119.291-96.61 216-216 216-119.244 0-216-96.562-216-216 0-119.203 96.602-216 216-216m0-32C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 392.957 8 256 8zm-36 344h12V232h-12c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12h48c6.627 0 12 5.373 12 12v140h12c6.627 0 12 5.373 12 12v8c0 6.627-5.373 12-12 12h-72c-6.627 0-12-5.373-12-12v-8c0-6.627 5.373-12 12-12zm36-240c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32z"
/>
</svg>

View File

@ -0,0 +1,18 @@
<script>
export let width = "1em"
</script>
<svg
width={width}
style="text-align: center; display: inline-block;"
aria-hidden="true"
focusable="false"
role="img"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
fill="currentColor"
d="M256 8C119.033 8 8 119.033 8 256s111.033 248 248 248 248-111.033 248-248S392.967 8 256 8zm0 464c-118.664 0-216-96.055-216-216 0-118.663 96.055-216 216-216 118.664 0 216 96.055 216 216 0 118.663-96.055 216-216 216zm141.63-274.961L217.15 376.071c-4.705 4.667-12.303 4.637-16.97-.068l-85.878-86.572c-4.667-4.705-4.637-12.303.068-16.97l8.52-8.451c4.705-4.667 12.303-4.637 16.97.068l68.976 69.533 163.441-162.13c4.705-4.667 12.303-4.637 16.97.068l8.451 8.52c4.668 4.705 4.637 12.303-.068 16.97z"
/>
</svg>

View File

@ -0,0 +1,67 @@
<script lang="ts">
import { createEventDispatcher } from "svelte";
import { fade } from "svelte/transition";
import { Toast, ToastType } from "$lib/toast";
import SuccessIcon from "./SuccessIcon.svelte";
import ErrorIcon from "./ErrorIcon.svelte";
import InfoIcon from "./InfoIcon.svelte";
import CloseIcon from "./CloseIcon.svelte";
const dispatch = createEventDispatcher();
export let toastData: Toast;
</script>
<article class={toastData.type.toString().toLowerCase()} role="alert" transition:fade>
{#if toastData.type === ToastType.Success}
<SuccessIcon width="1.1em" />
{:else if toastData.type === ToastType.Error}
<ErrorIcon width="1.1em" />
{:else}
<InfoIcon width="1.1em" />
{/if}
<div class="text">
{toastData.text}
</div>
{#if toastData.dismissable}
<button class="close" on:click={() => dispatch("dismiss")}>
<CloseIcon width="0.8em" />
</button>
{/if}
</article>
<style lang="postcss">
article {
color: white;
padding: 0.75rem 1.5rem;
border-radius: 0.2rem;
display: flex;
align-items: center;
margin: 0 auto 0.5rem auto;
width: 20rem;
max-width: 80%;
}
.error {
background: IndianRed;
}
.success {
background: MediumSeaGreen;
}
.info {
background: SkyBlue;
}
.text {
margin-left: 1rem;
}
.close {
color: white;
background: transparent;
border: 0 none;
padding: 0;
margin: 0 0 0 auto;
line-height: 1;
font-size: 1rem;
}
</style>

View File

@ -0,0 +1,28 @@
<script lang="ts">
import Toast from "./Toast.svelte";
import { dismissToast, toasts } from "$lib/store";
</script>
{#if $toasts}
<section>
{#each $toasts as toast (toast.id)}
<Toast toastData = {toast} on:dismiss={() => dismissToast(toast.id)} />
{/each}
</section>
{/if}
<style lang="postcss">
section {
position: fixed;
bottom: 0;
left: 0;
right: 0;
width: 100%;
display: flex;
margin-top: 1rem;
justify-content: center;
flex-direction: column;
z-index: 10;
}
</style>

View File

@ -0,0 +1,21 @@
<script lang="ts">
import { setContext } from 'svelte';
import type { TimelinePosition, TimelineConfig } from '../types';
export let position: TimelinePosition = 'right';
export let style: string = null;
setContext<TimelineConfig>('TimelineConfig', { rootPosition: position });
</script>
<ul class="timeline" {style}>
<slot />
</ul>
<style>
.timeline {
display: flex;
flex-direction: column;
padding: 6px 16px;
flex-grow: 1;
}
</style>

View File

@ -0,0 +1,13 @@
<script lang="ts">
export let style: string = null;
</script>
<span class="timeline-connector" {style} />
<style>
.timeline-connector {
width: 2px;
background-color: #bdbdbd;
flex-grow: 1;
}
</style>

View File

@ -0,0 +1,30 @@
<script lang="ts">
import { getContext } from 'svelte';
import type { TimelineConfig, TimelinePosition } from '../types';
export let style: string = null;
const config = getContext<TimelineConfig>('TimelineConfig');
const parentPosition = getContext<TimelinePosition>('ParentPosition');
const itemPosition = parentPosition ? parentPosition : config.rootPosition;
</script>
<div class={`timeline-content ${itemPosition}`} {style}>
<slot />
</div>
<style>
.timeline-content {
margin: 0;
flex: 1;
margin: 6px 16px;
}
.left {
text-align: right;
}
.right {
text-align: left;
}
</style>

View File

@ -0,0 +1,19 @@
<script lang="ts">
export let style: string = null;
</script>
<span class="timeline-dot" {style}>
<slot />
</span>
<style>
.timeline-dot {
background-color: #121212;
border: solid 2px #121212;
display: flex;
align-self: baseline;
padding: 4px;
border-radius: 50%;
margin: 11.5px 0;
}
</style>

View File

@ -0,0 +1,58 @@
<script lang="ts">
import { getContext, setContext } from 'svelte';
import type { TimelinePosition, ParentPosition, TimelineConfig } from '../types';
export let position: ParentPosition | null = null;
export let style: string = null;
const config = getContext<TimelineConfig>('TimelineConfig');
const itemPosition = position ? position : config.rootPosition;
setContext<TimelinePosition>('ParentPosition', itemPosition);
</script>
<li class={`timeline-item ${itemPosition}`} {style}>
{#if !$$slots['opposite-content']}
<div class="opposite-block" />
{:else}
<slot name="opposite-content" />
{/if}
<slot />
</li>
<style>
:global(.alternate:nth-of-type(even) > .timeline-content) {
text-align: right;
}
:global(.alternate:nth-of-type(odd) > .timeline-opposite-content) {
text-align: right;
}
.opposite-block {
flex: 1;
margin: 6px 16px;
}
.timeline-item {
list-style: none;
display: flex;
position: relative;
min-height: 70px;
}
.left {
flex-direction: row-reverse;
}
.right {
flex-direction: row;
}
.alternate:nth-of-type(even) {
flex-direction: row-reverse;
}
.alternate:nth-of-type(odd) {
flex-direction: row;
}
</style>

View File

@ -0,0 +1,31 @@
<script lang="ts">
import { getContext } from 'svelte';
import type { TimelineConfig, TimelinePosition } from '../types';
export let style: string = null;
const config = getContext<TimelineConfig>('TimelineConfig');
const parentPosition = getContext<TimelinePosition>('ParentPosition');
const itemPosition = parentPosition ? parentPosition : config.rootPosition;
</script>
<div class={`timeline-opposite-content ${itemPosition}`} {style}>
<slot />
</div>
<style>
.timeline-opposite-content {
margin: 0;
flex: 1;
margin-right: auto;
margin: 6px 16px;
}
.left {
text-align: left;
}
.right {
text-align: right;
}
</style>

View File

@ -0,0 +1,16 @@
<script lang="ts">
export let style: string = null;
</script>
<div class="timeline-separator" {style}>
<slot />
</div>
<style>
.timeline-separator {
display: flex;
flex-direction: column;
flex: 0;
align-items: center;
}
</style>

5
src/lib/data.ts Normal file
View File

@ -0,0 +1,5 @@
export async function getJson(path: string) {
let response = await fetch(path);
let users = await response.json();
return users;
}

39
src/lib/index.ts Normal file
View File

@ -0,0 +1,39 @@
// place files you want to import through the `$lib` alias in this folder.
import Timeline from '$lib/components/timeline/Timeline.svelte';
import TimelineItem from '$lib/components/timeline/TimelineItem.svelte';
import TimelineSeparator from '$lib/components/timeline/TimelineSeparator.svelte';
import TimelineDot from '$lib/components/timeline/TimelineDot.svelte';
import TimelineConnector from '$lib/components/timeline/TimelineConnector.svelte';
import TimelineContent from '$lib/components/timeline/TimelineContent.svelte';
import TimelineOppositeContent from '$lib/components/timeline/TimelineOppositeContent.svelte';
import Toasts from '$lib/components/Toasts/Toasts.svelte';
import Toast from '$lib/components/Toasts/Toast.svelte';
import CloseIcon from '$lib/components/Toasts/CloseIcon.svelte';
import InfoIcon from '$lib/components/Toasts/InfoIcon.svelte';
import SuccessIcon from '$lib/components/Toasts/SuccessIcon.svelte';
import ErrorIcon from '$lib/components/Toasts/ErrorIcon.svelte';
import Card from '$lib/components/Card.svelte';
import Modal from '$lib/components/Modal.svelte';
export {
Timeline,
TimelineItem,
TimelineSeparator,
TimelineDot,
TimelineConnector,
TimelineContent,
TimelineOppositeContent,
Toasts,
Toast,
CloseIcon,
InfoIcon,
SuccessIcon,
ErrorIcon,
Card,
Modal
};

28
src/lib/store.ts Normal file
View File

@ -0,0 +1,28 @@
import { ToastType, type Toast } from "$lib/toast";
import { writable, type Writable } from "svelte/store";
export const toasts: Writable<Toast[]> = writable([]);
export const addToast = (toast: Toast) => {
// Create a unique ID so we can easily find/remove it
// if it is dismissible/has a timeout.
toast.id = Math.floor(Math.random() * 10000);
// Setup some sensible defaults for a toast.
const defaults = {
id: toast.id,
type: ToastType.Info,
dismissible: true,
timeout: 3000,
};
// Push the toast to the top of the list of toasts
toasts.update((all) => [{ ...defaults, ...toast }, ...all]);
// If toast is dismissible, dismiss it after "timeout" amount of time.
if (toast.timeout) setTimeout(() => dismissToast(toast.id), toast.timeout);
};
export const dismissToast = (id: number) => {
toasts.update((all) => all.filter((t) => t.id !== id));
};

26
src/lib/toast.ts Normal file
View File

@ -0,0 +1,26 @@
/**
* @enum Used to refer to the type of toast being displayed
*/
export enum ToastType {
Info = "info",
Success = "success",
Error = "error"
}
/**
* @class Toast Notification
*/
export class Toast {
constructor(text: String, type: ToastType, dismissable: boolean, timeout: number ) {
this.text = text;
this.type = type;
this.dismissable = dismissable;
this.timeout = timeout;
}
id: number = 0;
text: String;
type: ToastType;
dismissable: Boolean;
timeout: number;
}

9
src/lib/types.d.ts vendored Normal file
View File

@ -0,0 +1,9 @@
type TimelinePosition = 'right' | 'left' | 'alternate';
type ParentPosition = 'right' | 'left';
type TimelineConfig = {
rootPosition: TimelinePosition;
};
export { TimelinePosition, ParentPosition, TimelineConfig };

125
src/main.svelte Normal file
View File

@ -0,0 +1,125 @@
<script lang="ts">
import { getJson } from "$lib/data";
import { Toast, ToastType } from "$lib/toast";
import { addToast } from "$lib/store";
import Skills from './skills.svelte';
import Timeline from "./timeline.svelte";
</script>
<style>
.main-card {
background-color: var(--bg-secondary);
border-radius: 1em;
padding: .2em 2em 2em 2em;
box-shadow: .5em .5em .5em var(--glow);
}
.flex-container {
display: flex;
align-items: center;
}
.profile {
border-radius: 100%;
height: 8em;
width: 8em;
padding: 1em 1em 1em 1em;
border: .5em solid var(--bg-grad-3);
}
.about {
padding: 0em 5% 0em 5%;
font-size: 125%;
}
@media (max-width: 800px) {
.flex-container {
align-items: center;
flex-direction: column;
padding: 0px;
}
.about {
font-size: 100%;
}
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0em;
}
.card-header h1 {
font-size: 2em;
}
.card-header h3 {
font-size: 1.5em;
}
/* Skills Cards CSS */
.container {
padding-top: 3em;
max-width: 90%;
margin: auto;
}
.cards {
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 3em 3em;
padding: 2em 0em 2em 0em;
transition: all 0.2s;
}
</style>
{#await getJson('/json/me.json')}
<div class="card">
<div class="card-header">
<h1>Loading...</h1>
</div>
</div>
{:then info}
<div class="main-card">
<div class="card-header">
<h1>{info.name}</h1>
<h3 class="not-required">{info.job_title}</h3>
</div>
<hr />
<div class="flex-container">
<img class="profile not-required" src={info.profile_photo} alt="{info.name}'s Profile Photo">
<p class="about">{@html info.about}</p>
</div>
</div>
<div class="container">
<h1>Skills</h1>
<hr />
<div class="cards">
<Skills skills="{info.skills}"></Skills>
</div>
</div>
<div class="container">
<h1>Experience</h1>
<hr />
<!-- https://github.com/K-Sato1995/svelte-vertical-timeline -->
<Timeline timelineData="{info.timeline}"></Timeline>
</div>
<div style="display: none;">{addToast(new Toast("Click on a skill to open a prompt", ToastType.Info, true, 8_000))}</div>
<div style="display: none;">{addToast(new Toast("Welcome!", ToastType.Success, true, 7_000))}</div>
{:catch}
<div class="card">
<div class="card-header">
<h1>Unable to load portfolio overview data</h1>
</div>
</div>
<div style="display: none;">{addToast(new Toast("Unable to load me.json", ToastType.Error, true, 3000))}</div>
{/await}

60
src/routes/+layout.svelte Normal file
View File

@ -0,0 +1,60 @@
<script lang="ts">
import Toasts from "$lib/components/Toasts/Toasts.svelte";
import ThemeSwitcher from "$lib/components/ThemeSwitcher.svelte";
</script>
<style>
.main-container {
margin-left: 10%;
margin-right: 10%;
padding-top: 2em;
}
@media (max-width: 800px) {
.main-container {
margin: 0em;
padding-top: 1em;
}
}
nav {
position: relative;
font-weight: bold;
font-size: 110%;
overflow: visible;
display: flex;
justify-content: center;
gap: 1.5em;
padding: 0em 0em 1.5em 0em;
z-index: 2;
height: 1.5em;
border-radius: 4px;
}
.fade {
-webkit-animation: fadeinout 1s linear forwards;
animation: fadeinout 1s linear forwards;
}
@-webkit-keyframes fadeinout {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes fadeinout {
0% { opacity: 0; }
100% { opacity: 1; }
}
</style>
<nav>
<a href = "/">//Profile</a>
<a href = "/repos">//Repos</a>
<a href = "/contact">//Contact</a>
<ThemeSwitcher />
</nav>
<div class="main-container fade">
<Toasts />
<slot />
</div>

5
src/routes/+page.svelte Normal file
View File

@ -0,0 +1,5 @@
<script>
import Main from '../main.svelte';
</script>
<Main></Main>

View File

@ -0,0 +1,82 @@
<script lang="ts">
import Card from '$lib/components/Card.svelte';
import { Toast, ToastType } from "$lib/toast";
import { addToast } from "$lib/store";
import { page } from '$app/stores';
const sent = $page.url.searchParams.get('sent');
if (sent == "true") {
addToast(new Toast("Thank you! Your E-Mail has been sent. I will reply as soon as possible!", ToastType.Success, true, 5000));
}
// Can't use else otherwise the warning will display on load
if (sent == "false") {
addToast(new Toast("Sorry, your E-Mail could not be sent... Please try again later!", ToastType.Error, true, 5000));
}
</script>
<style>
form {
display: flex;
flex-wrap: wrap;
flex: 2 1;
align-items: center;
margin: 1em;
gap: 1em 3em;
}
input, textarea {
padding: 1em 0em 1em 1em;
background-color: var(--input);
color: var(--fg);
border: 0;
outline: 0;
border-radius: 8px;
resize: none;
min-width: 100%;
transition: all 0.15s;
&:focus, &:hover{
box-shadow: .2em .2em .2em var(--green);
}
}
textarea {
min-height: 10em;
}
.container {
display: flex;
flex: 2 1 5rem;
flex-direction: column;
gap: 1em 1em;
width: 100%;
}
</style>
<Card>
<div slot="header">
<h2>Contact</h2>
</div>
<div slot="content">
<form action="https://api.staticforms.xyz/submit" method="post">
<div class="container">
<input type="hidden" name="accessKey" value="fbb5ec04-506b-448a-a445-a2e47579a966">
<input type="text" name="name" placeholder="Name" required>
<input type="text" name="email" placeholder="Email address" required>
<input type="hidden" name="replyTo" value="@">
<input type="text" name="subject" placeholder="Subject" required>
<input type="text" name="honeypot" style="display: none;">
</div>
<div class="container">
<textarea name="message" id="message" placeholder="Message" required></textarea>
</div>
<input class="button" type="submit" value="Send" />
<input type="hidden" name="redirectTo" value="https://luke-else.co.uk/contact?sent=true">
</form>
</div>
<div slot="footer">
<a href="/Luke Else - CV.pdf" target="_blank" rel="noopener noreferrer">Curriculum Vitae</a>
<a href="mailto:contact@luke-else.co.uk">E-Mail</a>
</div>
</Card>

View File

@ -0,0 +1,3 @@
<h1>Repos</h1>
<p>Stay tuned! This is still in development.</p>
<p>Come back later to find out what projects I'm currently working on!</p>

59
src/skills.svelte Normal file
View File

@ -0,0 +1,59 @@
<script lang="ts">
export let skills: any;
import Card from '$lib/components/Card.svelte';
import Modal from '$lib/components/Modal.svelte';
let showModal: boolean = false;
let activeModal: any = null;
</script>
<style>
.card-footer {
margin-bottom: 1em;
display: flex;
gap: 1.5em;
justify-content: space-between;
}
.logo {
color: var(--fg);
font-size: 3em;
}
</style>
{#each skills as skill}
<Card on:click={() => {showModal = true; activeModal = skill}}>
<div slot="header">
<h2>{skill.skill}</h2>
<i class="{skill.logo} logo"></i>
</div>
<div slot="content">
<p class="not-required">{@html skill.usage}</p>
</div>
<div slot="footer">
<!-- svelte-ignore a11y-invalid-attribute -->
<a href="#">View More</a>
<a href="/repos">Repos</a>
</div>
</Card>
{/each}
<!--Modal to be displayed on click-->
{#if activeModal != null}
<Modal bind:showModal>
<h2 slot="header" class="card-header">
{activeModal.skill}
<i class="{activeModal.logo} logo"></i>
</h2>
<p>
{activeModal.about}
</p>
<div class="card-footer">
<a href="{activeModal.link}" target="_blank" rel="noopener noreferrer">Learn More</a>
<a href="/repos">Repos</a>
</div>
</Modal>
{/if}

63
src/timeline.svelte Normal file
View File

@ -0,0 +1,63 @@
<script lang="ts">
export let timelineData: any;
import Timeline from '$lib/components/timeline/Timeline.svelte';
import TimelineItem from '$lib/components/timeline/TimelineItem.svelte';
import TimelineSeparator from '$lib/components/timeline/TimelineSeparator.svelte';
import TimelineDot from '$lib/components/timeline/TimelineDot.svelte';
import TimelineConnector from '$lib/components/timeline/TimelineConnector.svelte';
import TimelineContent from '$lib/components/timeline/TimelineContent.svelte';
import TimelineOppositeContent from '$lib/components/timeline/TimelineOppositeContent.svelte';
</script>
<Timeline
position="alternate"
style={`
border-radius: 3%;
padding: 1rem;
`}
>
{#each timelineData as item}
<TimelineItem>
<TimelineOppositeContent slot="opposite-content">
<p class="oposite-content-title">{item.duration}</p>
</TimelineOppositeContent>
<TimelineSeparator>
{#if item.duration.includes('Present') || !item.duration.includes('-')}
<div class="elementToFadeInAndOut">
<TimelineDot style={`background-color: var(--link); border-color: var(--bg-grad-2);`} />
</div>
{:else}
<TimelineDot style={`background-color: var(--link); border-color: var(--bg-grad-2);`} />
{/if}
<TimelineConnector />
</TimelineSeparator>
<TimelineContent>
<h3 class="content-title">{item.title}</h3>
<p class="content-description">{@html item.description}</p>
</TimelineContent>
</TimelineItem>
{/each}
</Timeline>
<style>
.oposite-content-title {
margin: 0;
padding: 0;
color: var(--bg-grad-2);
}
.content-title {
margin: 0;
padding: 0;
}
.content-description {
margin: 0;
padding: 0;
margin-top: 1rem;
color: var(--fg);
font-weight: lighter;
padding: 0.5rem 0;
}
</style>

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 738 B

60
static/json/me.json Normal file
View File

@ -0,0 +1,60 @@
{
"name": "Luke Else",
"job_title": "Software Engineer",
"profile_photo": "/profile.jpg",
"skills" : [
{
"skill": "Rust",
"logo": "devicon-rust-plain",
"link": "https://rust-lang.org",
"usage": "Rust is a memory safe programming language that relies on a borrow checker to keep track of who owns memory. This makes it versatile in embedded software becuase of the zero cost abstraction and lack of requirement for a garbage colllector. Becuase of this, I opted to use Rust for one of my own projects in which I created a <a href='https://git@git.luke-else.co.uk/luke-else/esp32_gps_display'>GPS fueled speedometer</a> and position tracker to act as a form of telematics for my car. I have also used Rust when creating some software to assist with university studies. See here for my <a href='https://git@git.luke-else.co.uk/luke-else/subnet_calculator'>Subnet Calculator</a>.",
"about": "Rust is a remarkable programming language that combines the best of both high-level and low-level programming. It prioritizes safety and efficiency through strict memory management and concurrent programming features, all while maintaining clean and readable code. The active Rust community makes it a valuable choice for building reliable software."
},
{
"skill": "C++",
"logo": "devicon-cplusplus-plain",
"link": "https://cplusplus.com/",
"usage": "Opposing Rust, C++, has been my primary language since joining Thales in late 2022. I have predominantly been working on a distributed simulation system that relies upon <a href='https://en.wikipedia.org/wiki/Inter-process_communication'>IPC</a> through the use of internal tools to orchestrate itself and the platform's sessions. I've had a lot of experience using both <a href='https://www.qt.io'>QT</a>, as well as the lesser known <a href='https://github.com/ocornut/imgui'>ImGui</a>, both of which have given me the platform to build readily deployable apps to customers at Thales UK.",
"about": "C++, a versatile and powerful programming language, has stood the test of time as a cornerstone of software development. Its combination of high-level abstractions and low-level control allows developers to tackle a wide range of projects efficiently. With a rich standard library and a massive ecosystem of libraries and frameworks; C++ empowers programmers to create efficient and scalable software solutions."
},
{
"skill": "Git",
"logo": "devicon-git-plain",
"link": "https://git-scm.com",
"usage": "With git being pretty much the 'Defaqto standard' when it comes to version control systems, I have had a lot of experience with its use, including more advanced features such as <a href='https://www.atlassian.com/git/tutorials/advanced-overview'>'Branching / Merging'</a>, <a href='https://www.atlassian.com/git/tutorials/advanced-overview'>'Hooks'</a>, <a href='https://www.atlassian.com/git/tutorials/advanced-overview'>'Stashing'</a> etc... I've further setup my own <a href='https://git.luke-else.co.uk/luke-else/'>services</a> for hosting remote Git reporitories to allow for complete access and control over my work. Appending to this, I have setup my own form of actions to aid in the actioning of <a href='https://www.redhat.com/en/topics/devops/what-is-ci-cd'>CI/CD</a> on my own projects.",
"about": "Git, a fundamental tool in version control, streamlines collaboration and code management. Its simplicity and robustness empower developers to track changes, collaborate seamlessly, and maintain code efficiently. With Git, managing and tracking code changes becomes a breeze, making it an essential tool for software development."
},
{
"skill": "Docker",
"logo": "devicon-docker-plain",
"link": "https://docker.com",
"usage": "With docker being so versatile when it comes to deploying software to end users, I decided to pick it up as a skill in order to improve the quality of applications and services I can offer to individuals. I use docker alongside its child, 'docker-compose' to control the <a href='https://git.luke-else.co.uk/luke-else/server'>provisioning of containers</a> on my main home-lab server which delivers content like <a href='https://git.luke-else.co.uk/luke-else/luke-else.co.uk'>this website</a> as well as database and <a href='https://git.luke-else.co.uk/luke-else/'>remote git services</a>.",
"about": "Docker, a transformative containerization platform, simplifies application deployment and management. It encapsulates applications and their dependencies in lightweight containers, providing consistency across different environments. Docker's user-friendly approach makes it accessible to developers and DevOps professionals, streamlining the software development and deployment process. Its efficiency, scalability, and support for microservices architecture have made it a go-to tool for creating, deploying, and scaling applications. Docker has revolutionized the way we think about software packaging and distribution, making it an essential component of modern software development workflows."
},
{
"skill": "Svelte",
"logo": "devicon-svelte-plain",
"link": "https://svelte.dev",
"usage": "Svelte is a front and backend web framework that allows for stylised components to be reused in an efficient manor. This <a href='https://git.luke-else.co.uk/luke-else/luke-else.co.uk'>website</a> was made using svelte and has given me a great opportunity to learn about Svelte's power and usage. I further want to expand this in the future by using <a href='https://github.com/tauri-apps/tauri'>Tauri</a> to create rendered web application in containers that can be deployed as desktop apps.",
"about": "Svelte is an impressive web framework that stands out in the world of front-end development. It's known for its unique approach, compiling components to highly optimized vanilla JavaScript, resulting in blazing-fast web applications. Svelte's simplicity and declarative syntax make it easy to learn and use, and it encourages efficient, maintainable code. "
}
],
"about": "Hello! I'm an enthusiastic, dedicated software engineer passionate about backend development, networking, and embedded systems. I am currently employed at <a href='https://www.thalesgroup.com/en'>Thales UK</a> and thrive on architecting robust backend solutions, optimizing data transmission, and crafting efficient embedded software. I love tackling complex challenges, collaborating with fellow professionals, and staying up-to-date with tech trends such as my current venture in learning <a href='https://rust-lang.org'>Rust-Lang</a>.",
"timeline" : [
{
"duration" : "September 2022 - Present",
"title" : "Thales UK - Software Engineer",
"description" : "As a software engineering apprentice at Thales UK, I find myself partaking in agile / scrum development methodologies in a strong team of 6 other engineers. The team iterates on a pre-existing system designed for the MOD, written in C++, using internal frameworks to assist. <br /><br /> To extend this, the apprenticeship includes allocated time for studying a Digital and Thechnology Solutions degree with the University of Warwick, including modules relevant to business management, devlopment processes and data integrity etc..."
},
{
"duration" : "September 2020 - July 2022",
"title" : "The Norton Knatchbull School (A-Levels)",
"description" : "Computer Science (<b>A*</b>) <br /> Mathematics (<b>A</b>) <br /> Physics (<b>A</b>)"
},
{
"duration" : "September 2015 - July 2020",
"title" : "The Norton Knatchbull School (GCSEs)",
"description" : "FSMQ (C) <br /> Maths (8) <br /> Geography (<b>9</b>) <br /> Biology (<b>9</b>) <br /> Chemistry (<b>9</b>) <br /> Physics (<b>9</b>) <br /> Spanish (7) <br /> English (Literature & Language) (7, 7) <br /> Computer Science (<b>9</b>)"
}
]
}

View File

Before

Width:  |  Height:  |  Size: 425 KiB

After

Width:  |  Height:  |  Size: 425 KiB

18
static/themes/dark.css Normal file
View File

@ -0,0 +1,18 @@
:root {
--bg: #282c34;
--bg-secondary: #474d57;
--bg-grad-1: #484e58;
--bg-grad-2: #4e5560;
--bg-grad-3: #59616d;
--bg-grad-4: #606a7b;
--bg-grad-5: #606978;
--input: #4e5560;
--fg: #ABB2BF;
--header: #E06C75;
--link: #98C379;
--hover: #56B6C2;
--glow: #C678DD;
--green: #98C379;
--red: #E06C75;
}

18
static/themes/light.css Normal file
View File

@ -0,0 +1,18 @@
:root {
--bg: #fff;
--bg-secondary: #ebebeb;
--bg-grad-1: #c1c1c1;
--bg-grad-2: #a1a1a1;
--bg-grad-3: #858585;
--bg-grad-4: #616161;
--bg-grad-5: #484848;
--input: #a9a9a9;
--fg: #2f2f2f;
--header: #514a4a;
--link: #df0000;
--hover: #4f4b489b;
--glow: #545454;
--green: #98C379;
--red: #E06C75;
}

19
svelte.config.js Normal file
View File

@ -0,0 +1,19 @@
// import adapter from '@sveltejs/adapter-auto';
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
};
export default config;

17
tsconfig.json Normal file
View File

@ -0,0 +1,17 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

6
vite.config.ts Normal file
View File

@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});