diff --git a/README.md b/README.md
index 76aa0a22..5fb63758 100644
--- a/README.md
+++ b/README.md
@@ -696,6 +696,44 @@ How do we solve this ? Developers love having framework overview by examples. It
+
+
+
+ Blazor
+
+
+
+- [x] Reactivity
+ - [x] Declare state
+ - [x] Update state
+ - [x] Computed state
+- [x] Templating
+ - [x] Minimal template
+ - [x] Styling
+ - [x] Loop
+ - [x] Event click
+ - [x] Dom ref
+ - [x] Conditional
+- [x] Lifecycle
+ - [x] On mount
+ - [x] On unmount
+- [x] Component composition
+ - [x] Props
+ - [x] Emit to parent
+ - [x] Slot
+ - [x] Slot fallback
+ - [x] Context
+- [x] Form input
+ - [x] Input text
+ - [x] Checkbox
+ - [x] Radio
+ - [x] Select
+- [ ] Webapp features
+ - [ ] Render app
+ - [ ] Fetch data
+
+
+
## 🤝 Contributing
diff --git a/build/lib/highlighter.ts b/build/lib/highlighter.ts
index 5b66fca9..6816362b 100644
--- a/build/lib/highlighter.ts
+++ b/build/lib/highlighter.ts
@@ -32,9 +32,12 @@ async function getHighlighter(): Promise<
"jsx",
"vue",
"marko",
+ "csharp",
],
langAlias: {
- ripple: "jsx", // until ripple is supported by shiki
+ ripple: "jsx",
+ razor: "csharp", // Csharp might not be ideal, but better? than nothing
+ cs: "csharp",
},
});
}
diff --git a/content/1-reactivity/1-declare-state/blazor/Name.razor b/content/1-reactivity/1-declare-state/blazor/Name.razor
new file mode 100644
index 00000000..76383b90
--- /dev/null
+++ b/content/1-reactivity/1-declare-state/blazor/Name.razor
@@ -0,0 +1,5 @@
+
Hello @name
+
+@code {
+ private string name = "John";
+}
diff --git a/content/1-reactivity/2-update-state/blazor/Name.razor b/content/1-reactivity/2-update-state/blazor/Name.razor
new file mode 100644
index 00000000..1d9d5fa0
--- /dev/null
+++ b/content/1-reactivity/2-update-state/blazor/Name.razor
@@ -0,0 +1,10 @@
+Hello @name
+
+@code {
+ private string name = "John";
+
+ protected override void OnInitialized()
+ {
+ name = "Jane";
+ }
+}
diff --git a/content/1-reactivity/3-computed-state/blazor/DoubleCount.razor b/content/1-reactivity/3-computed-state/blazor/DoubleCount.razor
new file mode 100644
index 00000000..f33f7a87
--- /dev/null
+++ b/content/1-reactivity/3-computed-state/blazor/DoubleCount.razor
@@ -0,0 +1,6 @@
+@doubleCount
+
+@code {
+ private int count = 10;
+ private int doubleCount => count * 2;
+}
diff --git a/content/2-templating/1-minimal-template/blazor/HelloWorld.razor b/content/2-templating/1-minimal-template/blazor/HelloWorld.razor
new file mode 100644
index 00000000..159202e8
--- /dev/null
+++ b/content/2-templating/1-minimal-template/blazor/HelloWorld.razor
@@ -0,0 +1 @@
+Hello world
diff --git a/content/2-templating/2-styling/blazor/CssStyle.razor b/content/2-templating/2-styling/blazor/CssStyle.razor
new file mode 100644
index 00000000..e41c14d0
--- /dev/null
+++ b/content/2-templating/2-styling/blazor/CssStyle.razor
@@ -0,0 +1,8 @@
+I am red
+
+
+
diff --git a/content/2-templating/3-loop/blazor/Colors.razor b/content/2-templating/3-loop/blazor/Colors.razor
new file mode 100644
index 00000000..a3032f15
--- /dev/null
+++ b/content/2-templating/3-loop/blazor/Colors.razor
@@ -0,0 +1,10 @@
+
+ @foreach (var color in colors)
+ {
+ - @color
+ }
+
+
+@code {
+ private string[] colors = { "red", "green", "blue" };
+}
diff --git a/content/2-templating/4-event-click/blazor/Counter.razor b/content/2-templating/4-event-click/blazor/Counter.razor
new file mode 100644
index 00000000..ff37548a
--- /dev/null
+++ b/content/2-templating/4-event-click/blazor/Counter.razor
@@ -0,0 +1,11 @@
+Counter: @count
+
+
+@code {
+ private int count = 0;
+
+ private void IncrementCount()
+ {
+ count++;
+ }
+}
diff --git a/content/2-templating/5-dom-ref/blazor/InputFocused.razor b/content/2-templating/5-dom-ref/blazor/InputFocused.razor
new file mode 100644
index 00000000..f389bbf4
--- /dev/null
+++ b/content/2-templating/5-dom-ref/blazor/InputFocused.razor
@@ -0,0 +1,13 @@
+
+
+@code {
+ private ElementReference inputElement;
+
+ protected override async Task OnAfterRenderAsync(bool firstRender)
+ {
+ if (firstRender)
+ {
+ await inputElement.FocusAsync();
+ }
+ }
+}
diff --git a/content/2-templating/6-conditional/blazor/TrafficLight.razor b/content/2-templating/6-conditional/blazor/TrafficLight.razor
new file mode 100644
index 00000000..a52beb03
--- /dev/null
+++ b/content/2-templating/6-conditional/blazor/TrafficLight.razor
@@ -0,0 +1,28 @@
+
+Light is: @light
+
+ You must
+ @if (light == "red")
+ {
+ STOP
+ }
+ else if (light == "orange")
+ {
+ SLOW DOWN
+ }
+ else if (light == "green")
+ {
+ GO
+ }
+
+
+@code {
+ private readonly string[] trafficLights = { "red", "orange", "green" };
+ private int lightIndex = 0;
+ private string light => trafficLights[lightIndex];
+
+ private void NextLight()
+ {
+ lightIndex = (lightIndex + 1) % trafficLights.Length;
+ }
+}
diff --git a/content/3-lifecycle/1-on-mount/blazor/PageTitle.razor b/content/3-lifecycle/1-on-mount/blazor/PageTitle.razor
new file mode 100644
index 00000000..0d25c19d
--- /dev/null
+++ b/content/3-lifecycle/1-on-mount/blazor/PageTitle.razor
@@ -0,0 +1,12 @@
+Page title: @pageTitle
+
+@inject IJSRuntime JSRuntime
+
+@code {
+ private string pageTitle = string.Empty;
+
+ protected override async Task OnInitializedAsync()
+ {
+ pageTitle = await JSRuntime.InvokeAsync("eval", "document.title");
+ }
+}
diff --git a/content/3-lifecycle/2-on-unmount/blazor/Time.razor b/content/3-lifecycle/2-on-unmount/blazor/Time.razor
new file mode 100644
index 00000000..1b2c2403
--- /dev/null
+++ b/content/3-lifecycle/2-on-unmount/blazor/Time.razor
@@ -0,0 +1,31 @@
+@implements IDisposable
+
+Current time: @currentTime
+
+@code {
+ private string currentTime = string.Empty;
+ private Timer? timer;
+
+ protected override void OnInitialized()
+ {
+ currentTime = DateTime.Now.ToLongTimeString();
+
+ timer = new Timer(
+ callback: _ => UpdateTime(),
+ state: null,
+ dueTime: TimeSpan.Zero,
+ period: TimeSpan.FromSeconds(1)
+ );
+ }
+
+ private void UpdateTime()
+ {
+ currentTime = DateTime.Now.ToLongTimeString();
+ StateHasChanged();
+ }
+
+ public void Dispose()
+ {
+ timer?.Dispose();
+ }
+}
\ No newline at end of file
diff --git a/content/4-component-composition/1-props/blazor/App.razor b/content/4-component-composition/1-props/blazor/App.razor
new file mode 100644
index 00000000..bf92f4a8
--- /dev/null
+++ b/content/4-component-composition/1-props/blazor/App.razor
@@ -0,0 +1,6 @@
+
+
diff --git a/content/4-component-composition/1-props/blazor/UserProfile.razor b/content/4-component-composition/1-props/blazor/UserProfile.razor
new file mode 100644
index 00000000..9733c90c
--- /dev/null
+++ b/content/4-component-composition/1-props/blazor/UserProfile.razor
@@ -0,0 +1,18 @@
+My name is @Name!
+My age is @Age!
+My favourite colors are @string.Join(", ", FavouriteColors)!
+I am @(IsAvailable ? "available" : "not available")
+
+@code {
+ [Parameter]
+ public string Name { get; set; } = "";
+
+ [Parameter]
+ public int Age { get; set; }
+
+ [Parameter]
+ public string[] FavouriteColors { get; set; } = Array.Empty();
+
+ [Parameter]
+ public bool IsAvailable { get; set; }
+}
diff --git a/content/4-component-composition/2-emit-to-parent/blazor/AnswerButton.razor b/content/4-component-composition/2-emit-to-parent/blazor/AnswerButton.razor
new file mode 100644
index 00000000..8d6ef9ff
--- /dev/null
+++ b/content/4-component-composition/2-emit-to-parent/blazor/AnswerButton.razor
@@ -0,0 +1,8 @@
+
+
+@code {
+ [Parameter]
+ public EventCallback OnYes { get; set; }
+ [Parameter]
+ public EventCallback OnNo { get; set; }
+}
\ No newline at end of file
diff --git a/content/4-component-composition/2-emit-to-parent/blazor/App.razor b/content/4-component-composition/2-emit-to-parent/blazor/App.razor
new file mode 100644
index 00000000..03599a71
--- /dev/null
+++ b/content/4-component-composition/2-emit-to-parent/blazor/App.razor
@@ -0,0 +1,17 @@
+Are you happy?
+
+@(isHappy ? "😀" : "😥")
+
+@code {
+ private bool isHappy = true;
+
+ private void OnAnswerNo()
+ {
+ isHappy = false;
+ }
+
+ private void OnAnswerYes()
+ {
+ isHappy = true;
+ }
+}
diff --git a/content/4-component-composition/3-slot/blazor/App.razor b/content/4-component-composition/3-slot/blazor/App.razor
new file mode 100644
index 00000000..63cea083
--- /dev/null
+++ b/content/4-component-composition/3-slot/blazor/App.razor
@@ -0,0 +1 @@
+Click me!
diff --git a/content/4-component-composition/3-slot/blazor/FunnyButton.razor b/content/4-component-composition/3-slot/blazor/FunnyButton.razor
new file mode 100644
index 00000000..61662e44
--- /dev/null
+++ b/content/4-component-composition/3-slot/blazor/FunnyButton.razor
@@ -0,0 +1,8 @@
+
+
+@code {
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+}
diff --git a/content/4-component-composition/4-slot-fallback/blazor/App.razor b/content/4-component-composition/4-slot-fallback/blazor/App.razor
new file mode 100644
index 00000000..385cd230
--- /dev/null
+++ b/content/4-component-composition/4-slot-fallback/blazor/App.razor
@@ -0,0 +1,2 @@
+
+I got content!
diff --git a/content/4-component-composition/4-slot-fallback/blazor/FunnyButton.razor b/content/4-component-composition/4-slot-fallback/blazor/FunnyButton.razor
new file mode 100644
index 00000000..844101d0
--- /dev/null
+++ b/content/4-component-composition/4-slot-fallback/blazor/FunnyButton.razor
@@ -0,0 +1,15 @@
+
+
+@code {
+ [Parameter]
+ public RenderFragment? ChildContent { get; set; }
+}
diff --git a/content/4-component-composition/5-context/blazor/App.razor b/content/4-component-composition/5-context/blazor/App.razor
new file mode 100644
index 00000000..13ddc755
--- /dev/null
+++ b/content/4-component-composition/5-context/blazor/App.razor
@@ -0,0 +1,25 @@
+Welcome back, @user.Username
+
+
+
+
+@code {
+ private UserInfo user = new()
+ {
+ Id = 1,
+ Username = "unicorn42",
+ Email = "unicorn42@example.com"
+ };
+
+ public class UserInfo
+ {
+ public int Id { get; set; }
+ public string Username { get; set; } = "";
+ public string Email { get; set; } = "";
+
+ public void UpdateUsername(string newUsername)
+ {
+ Username = newUsername;
+ }
+ }
+}
diff --git a/content/4-component-composition/5-context/blazor/UserProfile.razor b/content/4-component-composition/5-context/blazor/UserProfile.razor
new file mode 100644
index 00000000..0fcf43e5
--- /dev/null
+++ b/content/4-component-composition/5-context/blazor/UserProfile.razor
@@ -0,0 +1,16 @@
+
+
My Profile
+
Username: @user?.Username
+
Email: @user?.Email
+
+
+
+@code {
+ [CascadingParameter]
+ private App.UserInfo? user { get; set; }
+
+ private void UpdateUsername()
+ {
+ user?.UpdateUsername("Jane");
+ }
+}
diff --git a/content/6-form-input/1-input-text/blazor/InputHello.razor b/content/6-form-input/1-input-text/blazor/InputHello.razor
new file mode 100644
index 00000000..607b1ca0
--- /dev/null
+++ b/content/6-form-input/1-input-text/blazor/InputHello.razor
@@ -0,0 +1,6 @@
+@text
+
+
+@code {
+ private string text = "Hello world";
+}
diff --git a/content/6-form-input/2-checkbox/blazor/IsAvailable.razor b/content/6-form-input/2-checkbox/blazor/IsAvailable.razor
new file mode 100644
index 00000000..9e923fd8
--- /dev/null
+++ b/content/6-form-input/2-checkbox/blazor/IsAvailable.razor
@@ -0,0 +1,6 @@
+
+
+
+@code {
+ private bool isAvailable = false;
+}
diff --git a/content/6-form-input/3-radio/blazor/PickPill.razor b/content/6-form-input/3-radio/blazor/PickPill.razor
new file mode 100644
index 00000000..75f2278e
--- /dev/null
+++ b/content/6-form-input/3-radio/blazor/PickPill.razor
@@ -0,0 +1,19 @@
+Picked: @picked
+
+ picked = "blue")"
+ checked="@(picked == "blue")" />
+
+
+ picked = "red")"
+ checked="@(picked == "red")" />
+
+
+@code {
+ private string picked = "red";
+}
diff --git a/content/6-form-input/4-select/blazor/ColorSelect.razor b/content/6-form-input/4-select/blazor/ColorSelect.razor
new file mode 100644
index 00000000..250d7c07
--- /dev/null
+++ b/content/6-form-input/4-select/blazor/ColorSelect.razor
@@ -0,0 +1,25 @@
+
+
+@code {
+ private class ColorOption
+ {
+ public int Id { get; set; }
+ public string Text { get; set; } = "";
+ public bool IsDisabled { get; set; }
+ }
+
+ private readonly ColorOption[] colors = new[]
+ {
+ new ColorOption { Id = 1, Text = "red" },
+ new ColorOption { Id = 2, Text = "blue" },
+ new ColorOption { Id = 3, Text = "green" },
+ new ColorOption { Id = 4, Text = "gray", IsDisabled = true }
+ };
+
+ private int selectedColorId = 2;
+}
diff --git a/frameworks.ts b/frameworks.ts
index e1389ac1..ee244bd5 100644
--- a/frameworks.ts
+++ b/frameworks.ts
@@ -338,6 +338,23 @@ export const frameworks: Framework[] = [
mainPackageName: "ripple",
releaseDate: "2023-01-01",
},
+ {
+ id: "blazor",
+ title: "Blazor",
+ frameworkName: "Blazor",
+ frameworkNameId: "blazor",
+ isLatestStable: true,
+ img: "framework/blazor.svg",
+ playgroundURL: "https://try.dot.net/",
+ documentationURL:
+ "https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor",
+ filesSorter(files) {
+ return sortAllFilenames(files, ["App.razor", "App.razor.cs"]);
+ },
+ repositoryLink: "https://github.com/dotnet/aspnetcore",
+ mainPackageName: "Microsoft.AspNetCore.Components.Web",
+ releaseDate: "2018-09-01",
+ },
];
export function matchFrameworkId(id: string): Framework | undefined {
diff --git a/public/framework/blazor.svg b/public/framework/blazor.svg
new file mode 100644
index 00000000..2f5a30cf
--- /dev/null
+++ b/public/framework/blazor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file