diff --git a/BeauFindlay/BeauFindlay/Components/Typewriter/ITypewriterNotificationService.cs b/BeauFindlay/BeauFindlay/Components/Typewriter/ITypewriterNotificationService.cs new file mode 100644 index 0000000..87ddda9 --- /dev/null +++ b/BeauFindlay/BeauFindlay/Components/Typewriter/ITypewriterNotificationService.cs @@ -0,0 +1,7 @@ +namespace BeauFindlay.Components.Typewriter; + +public interface ITypewriterNotificationService +{ + event EventHandler? TypingCompleted; + void NotifyTypingCompleted(TypingCompletedEventArgs args); +} \ No newline at end of file diff --git a/BeauFindlay/BeauFindlay/Components/Typewriter/Typewriter.razor b/BeauFindlay/BeauFindlay/Components/Typewriter/Typewriter.razor index 6ee47ac..a85104f 100644 --- a/BeauFindlay/BeauFindlay/Components/Typewriter/Typewriter.razor +++ b/BeauFindlay/BeauFindlay/Components/Typewriter/Typewriter.razor @@ -1,5 +1,7 @@ @using System.Timers +@inject ITypewriterNotificationService NotificationService + @if (DisplayCursor) { @currentText| @@ -10,7 +12,7 @@ else } @code { - private const int typingDelayMilliseconds = 80; + private const int typingDelayMilliseconds = 50; private const int lineEndDelayMilliseconds = 1000; private static List instances = []; @@ -23,6 +25,9 @@ else [Parameter] public string Text { get; set; } = ""; + [Parameter] + public string? Name { get; set; } + public static event Action? OnAllTypingCompleted; protected override void OnInitialized() @@ -75,6 +80,11 @@ else delayTimer.Dispose(); UpdateCursorVisibility(); StartNextInstanceTyping(); + + if (!string.IsNullOrWhiteSpace(Name)) + { + NotificationService.NotifyTypingCompleted(new TypingCompletedEventArgs(Name)); + } if (!instances.Any()) { diff --git a/BeauFindlay/BeauFindlay/Components/Typewriter/TypewriterConstants.cs b/BeauFindlay/BeauFindlay/Components/Typewriter/TypewriterConstants.cs new file mode 100644 index 0000000..6153ff0 --- /dev/null +++ b/BeauFindlay/BeauFindlay/Components/Typewriter/TypewriterConstants.cs @@ -0,0 +1,9 @@ +namespace BeauFindlay.Components.Typewriter; + +public static class TypewriterConstants +{ + public static class Name + { + public const string IntroComplete = nameof(IntroComplete); + } +} \ No newline at end of file diff --git a/BeauFindlay/BeauFindlay/Components/Typewriter/TypewriterNotificationService.cs b/BeauFindlay/BeauFindlay/Components/Typewriter/TypewriterNotificationService.cs new file mode 100644 index 0000000..95c6511 --- /dev/null +++ b/BeauFindlay/BeauFindlay/Components/Typewriter/TypewriterNotificationService.cs @@ -0,0 +1,13 @@ +namespace BeauFindlay.Components.Typewriter; + +public class TypewriterNotificationService : ITypewriterNotificationService +{ + public event EventHandler? TypingCompleted; + + public void NotifyTypingCompleted(TypingCompletedEventArgs args) => TypingCompleted?.Invoke(this, args); +} + +public class TypingCompletedEventArgs(string typewriterInstanceId) : EventArgs +{ + public string TypewriterInstanceId { get; set; } = typewriterInstanceId; +} \ No newline at end of file diff --git a/BeauFindlay/BeauFindlay/Layout/Footer.razor b/BeauFindlay/BeauFindlay/Layout/Footer.razor new file mode 100644 index 0000000..906da2b --- /dev/null +++ b/BeauFindlay/BeauFindlay/Layout/Footer.razor @@ -0,0 +1,19 @@ + \ No newline at end of file diff --git a/BeauFindlay/BeauFindlay/Layout/MainLayout.razor b/BeauFindlay/BeauFindlay/Layout/MainLayout.razor index e1a9a75..5141b4d 100644 --- a/BeauFindlay/BeauFindlay/Layout/MainLayout.razor +++ b/BeauFindlay/BeauFindlay/Layout/MainLayout.razor @@ -1,3 +1,11 @@ @inherits LayoutComponentBase -@Body +
+ + +
+ @Body +
+ +
+
diff --git a/BeauFindlay/BeauFindlay/Layout/NavBar.razor b/BeauFindlay/BeauFindlay/Layout/NavBar.razor new file mode 100644 index 0000000..bc88f61 --- /dev/null +++ b/BeauFindlay/BeauFindlay/Layout/NavBar.razor @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/BeauFindlay/BeauFindlay/Pages/Home.razor b/BeauFindlay/BeauFindlay/Pages/Home.razor index 71f4631..9fe35ec 100644 --- a/BeauFindlay/BeauFindlay/Pages/Home.razor +++ b/BeauFindlay/BeauFindlay/Pages/Home.razor @@ -2,35 +2,71 @@ @implements IDisposable +@inject IJSRuntime JSRuntime + Home - Beau Findlay -
-

- +@if (!hasPreviouslyRendered) +{ +

+

-

- +

+

-

+

- +

-

+} +else +{ +

Hi, I'm Beau.

+ +

I'm a UK-based software engineer and I love building cool stuff.

+ +

A bit about me

+ +

I mostly specialise in back-end C#/.NET development and I've built systems that scale for hundreds-of-thousands of global users.

+ +

I'm currently heading up the tech as CTO at a cool startup called un:hurd.

+} + @code { - - protected override void OnInitialized() + private const string ComponentKey = "ComponentRendered_Home"; + private bool hasPreviouslyRendered; + + protected override async Task OnAfterRenderAsync(bool firstRender) { - Typewriter.OnAllTypingCompleted += HandleTypingCompleted; + if (firstRender) + { + Typewriter.OnAllTypingCompleted += HandleTypingCompleted; + + var renderedBeforeAsString = await JSRuntime.InvokeAsync("localStorage.getItem", ComponentKey); + + var previousValue = hasPreviouslyRendered; + hasPreviouslyRendered = !string.IsNullOrEmpty(renderedBeforeAsString) && bool.Parse(renderedBeforeAsString); + + if (!hasPreviouslyRendered) + { + await JSRuntime.InvokeVoidAsync("localStorage.setItem", ComponentKey, "true"); + } + + if (previousValue != hasPreviouslyRendered) + { + StateHasChanged(); + } + } } private static void HandleTypingCompleted() diff --git a/BeauFindlay/BeauFindlay/Pages/ThisApp.razor b/BeauFindlay/BeauFindlay/Pages/ThisApp.razor new file mode 100644 index 0000000..920be09 --- /dev/null +++ b/BeauFindlay/BeauFindlay/Pages/ThisApp.razor @@ -0,0 +1,7 @@ +@page "/this" + +

ThisApp

+ +@code { + +} \ No newline at end of file diff --git a/BeauFindlay/BeauFindlay/Program.cs b/BeauFindlay/BeauFindlay/Program.cs index ee6f99b..1151e4b 100644 --- a/BeauFindlay/BeauFindlay/Program.cs +++ b/BeauFindlay/BeauFindlay/Program.cs @@ -1,4 +1,5 @@ using BeauFindlay; +using BeauFindlay.Components.Typewriter; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; @@ -8,4 +9,6 @@ builder.RootComponents.Add("head::after"); builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) }); +builder.Services.AddSingleton(); + await builder.Build().RunAsync(); diff --git a/BeauFindlay/BeauFindlay/wwwroot/css/app.css b/BeauFindlay/BeauFindlay/wwwroot/css/app.css index 1475925..9879179 100644 --- a/BeauFindlay/BeauFindlay/wwwroot/css/app.css +++ b/BeauFindlay/BeauFindlay/wwwroot/css/app.css @@ -9,4 +9,15 @@ .blinking-cursor { animation: blink 1s step-end infinite; +} + +.fade-in { + animation: fadeInAnimation ease 6s; + animation-iteration-count: 1; + animation-fill-mode: forwards; +} + +@keyframes fadeInAnimation { + from { opacity: 0; } + to { opacity: 1; } } \ No newline at end of file diff --git a/BeauFindlay/BeauFindlay/wwwroot/css/app.min.css b/BeauFindlay/BeauFindlay/wwwroot/css/app.min.css index 8db0cbb..8bd5637 100644 --- a/BeauFindlay/BeauFindlay/wwwroot/css/app.min.css +++ b/BeauFindlay/BeauFindlay/wwwroot/css/app.min.css @@ -544,12 +544,29 @@ video { --tw-backdrop-sepia: ; } +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + .static { position: static; } -.mt-10 { - margin-top: 2.5rem; +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.mt-16 { + margin-top: 4rem; } .mt-4 { @@ -560,14 +577,34 @@ video { margin-top: 2rem; } +.mt-auto { + margin-top: auto; +} + +.mt-10 { + margin-top: 2.5rem; +} + .flex { display: flex; } +.h-20 { + height: 5rem; +} + +.h-full { + height: 100%; +} + .min-h-screen { min-height: 100vh; } +.flex-col { + flex-direction: column; +} + .items-center { align-items: center; } @@ -576,6 +613,30 @@ video { justify-content: center; } +.space-x-6 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(1.5rem * var(--tw-space-x-reverse)); + margin-left: calc(1.5rem * calc(1 - var(--tw-space-x-reverse))); +} + +.space-x-8 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(2rem * var(--tw-space-x-reverse)); + margin-left: calc(2rem * calc(1 - var(--tw-space-x-reverse))); +} + +.rounded { + border-radius: 0.25rem; +} + +.border-l-2 { + border-left-width: 2px; +} + +.border-r-2 { + border-right-width: 2px; +} + .bg-black { --tw-bg-opacity: 1; background-color: rgb(0 0 0 / var(--tw-bg-opacity)); @@ -585,6 +646,36 @@ video { padding: 2rem; } +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} + +.py-10 { + padding-top: 2.5rem; + padding-bottom: 2.5rem; +} + +.py-28 { + padding-top: 7rem; + padding-bottom: 7rem; +} + +.py-20 { + padding-top: 5rem; + padding-bottom: 5rem; +} + +.py-12 { + padding-top: 3rem; + padding-bottom: 3rem; +} + .font-mono { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; } @@ -594,14 +685,9 @@ video { line-height: 2rem; } -.text-3xl { - font-size: 1.875rem; - line-height: 2.25rem; -} - -.text-lg { - font-size: 1.125rem; - line-height: 1.75rem; +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; } .text-xl { @@ -609,10 +695,34 @@ video { line-height: 1.75rem; } +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} + .font-semibold { font-weight: 600; } +.leading-5 { + line-height: 1.25rem; +} + +.text-slate-100 { + --tw-text-opacity: 1; + color: rgb(241 245 249 / var(--tw-text-opacity)); +} + +.text-slate-200 { + --tw-text-opacity: 1; + color: rgb(226 232 240 / var(--tw-text-opacity)); +} + .text-slate-50 { --tw-text-opacity: 1; color: rgb(248 250 252 / var(--tw-text-opacity)); @@ -622,17 +732,13 @@ video { text-decoration-line: underline; } -.underline-offset-2 { - text-underline-offset: 2px; -} - .underline-offset-4 { text-underline-offset: 4px; } -.subpixel-antialiased { - -webkit-font-smoothing: auto; - -moz-osx-font-smoothing: auto; +.antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } @keyframes blink { @@ -647,4 +753,62 @@ video { .blinking-cursor { animation: blink 1s step-end infinite; +} + +.fade-in { + animation: fadeInAnimation ease 6s; + animation-iteration-count: 1; + animation-fill-mode: forwards; +} + +@keyframes fadeInAnimation { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +.hover\:text-slate-500:hover { + --tw-text-opacity: 1; + color: rgb(100 116 139 / var(--tw-text-opacity)); +} + +@media (min-width: 768px) { + .md\:order-1 { + order: 1; + } + + .md\:order-2 { + order: 2; + } + + .md\:mt-0 { + margin-top: 0px; + } + + .md\:flex { + display: flex; + } + + .md\:items-center { + align-items: center; + } + + .md\:justify-between { + justify-content: space-between; + } + + .md\:px-8 { + padding-left: 2rem; + padding-right: 2rem; + } +} + +@media (min-width: 1024px) { + .lg\:p-8 { + padding: 2rem; + } } \ No newline at end of file diff --git a/BeauFindlay/BeauFindlay/wwwroot/index.html b/BeauFindlay/BeauFindlay/wwwroot/index.html index 9360eef..44827c5 100644 --- a/BeauFindlay/BeauFindlay/wwwroot/index.html +++ b/BeauFindlay/BeauFindlay/wwwroot/index.html @@ -2,22 +2,29 @@ - - + + Beau Findlay - - - + + + - -
-
-

Loading beaufindlay.com|

-
+ +
+
+

+ Loading beaufindlay.com| +

- +
+