Skip to content

Commit fb4474e

Browse files
committed
- Refactored HexagonProgress to NOT use hexagonjs
- Updated seed script (more tests/fixing needed) - Integrated DevIcons into center of hex
1 parent ab80622 commit fb4474e

File tree

7 files changed

+236
-174
lines changed

7 files changed

+236
-174
lines changed

SocialCoder.Web/Client/ExperienceLevelExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,5 @@ public static Level Translate(this ExperienceLevel level)
4242
};
4343

4444
public static string Display(this Level level)
45-
=> $"{level.ToString()} Belt ({level.GetSigmaYears()})";
45+
=> $"{level} Belt ({level.GetSigmaYears()})";
4646
}

SocialCoder.Web/Client/Pages/Account/Profile.razor

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@page "/Account/Profile"
2+
@using MudBlazor.Utilities
23
@using ExperienceLevel = SocialCoder.Web.Shared.Enums.ExperienceLevel
34
@layout AccountLayout
45

@@ -82,19 +83,14 @@
8283
var image = ExperiencePool.FirstOrDefault(x => x.Name == current.Name);
8384
<MudItem md="4" Style="display: flex;">
8485
<HexagonProgress
85-
ImageUrl="@(image?.ImageUrl ?? string.Empty)"
86-
BadgeColor="@Color.Secondary"
87-
BadgeLineColor="Color.Success"
88-
UserLevel="@((int)current.Experience)"
89-
ProgressMin="0"
90-
ProgressMax="10"
91-
Progress="@((int)current.Experience)"
92-
>
93-
<CenterContent>
94-
<MudStack>
95-
<MudIcon Icon="@Icons.Material.Filled.School"/>
96-
</MudStack>
97-
</CenterContent>
86+
Progress="@((int)current.Experience * 10)"
87+
ProgressColor="@Colors.Pink.Accent2"
88+
BackgroundColor="@Colors.Gray.Darken4"
89+
Size="100"
90+
StrokeWidth="10">
91+
<MudTooltip Text="@current.Name">
92+
<DevIcon IconType="@((ExperienceItem)current.ExperiencePoolId)"/>
93+
</MudTooltip>
9894
</HexagonProgress>
9995
</MudItem>
10096
}
@@ -110,7 +106,18 @@
110106
@foreach (var item in ExperiencePool)
111107
{
112108
<MudSelectItem Value="item">
113-
<img alt="@item.Name" src="@item.ImageUrl" height="14" class="mr-1"/>@item.Name
109+
<MudStack Row>
110+
@if (item.Id.IsExperienceItem(out var experienceItem))
111+
{
112+
<DevIcon IconType="experienceItem"/>
113+
}
114+
else
115+
{
116+
<img alt="@item.Name" src="@item.ImageUrl" height="14" class="mr-1"/>
117+
@item.Name
118+
}
119+
<MudText Style="padding-top: 7px">@item.Name</MudText>
120+
</MudStack>
114121
</MudSelectItem>
115122
}
116123
</MudSelect>
@@ -128,7 +135,7 @@
128135
</MudItem>
129136
<MudItem md="1">
130137
<MudIconButton Icon="@Icons.Material.Filled.Add"
131-
OnClick="AddExperiencePool"
138+
OnClick="@AddExperiencePool"
132139
Color="Color.Info"
133140
Style="position: relative; top: 6px; left: 8px;"/>
134141
</MudItem>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@if (IconType != ExperienceItem.None)
2+
{
3+
<i class="@IconType.GetIcon().ToLower()" style="font-size: 36px; color: @Theme.PaletteDark.InfoDarken"></i>
4+
}
5+
6+
@code {
7+
[Parameter] public ExperienceItem IconType { get; set; }
8+
private static MudTheme Theme { get; } = new();
9+
10+
}
Lines changed: 124 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,143 @@
1-
@inject IJSRuntime JsRuntime
2-
3-
<div style="display: flex; justify-content: center;">
4-
<div style="display: inline">
5-
<div style="position: relative; max-width: @(Width)px">
6-
<div class="user-avatar-border">
7-
<div class="hexagon-progress" style="width: @(Width)px; height: @(Height)px; position: relative;"></div>
8-
</div>
9-
10-
<div class="user-avatar-content"
11-
style="position: absolute;
12-
top: @(Width * 0.16)px;
13-
left: @(Height * 0.17)px;">
14-
<div class="hexagon-image"
15-
style="width: @(Width * 0.683)px;
16-
height: @(Height * 0.683);
17-
position: relative;">
18-
@CenterContent
19-
</div>
20-
</div>
21-
22-
<div class="user-avatar-badge" style="display: flex;
23-
flex-wrap: wrap;
24-
align-items: center;
25-
justify-content: center;
26-
position: absolute;
27-
bottom: 25px;
28-
right: -3px;">
29-
30-
<div class="user-avatar-badge-border" style="
31-
display: inline-block;
32-
position: absolute;
33-
z-index: 1">
34-
<div class="hexagon-avatar-badge"
35-
style="width: 32px; height: 36px; position: absolute;"></div>
36-
</div>
37-
38-
<div class="user-avatar-badge-content"
39-
style="
40-
display: inline-block;
41-
z-index: 5;
42-
position: absolute;">
43-
<div class="hexagon-dark" style="width: 26px;
44-
height: 28px;
45-
position: relative;"></div>
46-
</div>
47-
<p class="user-avatar-badge-text" style="
48-
position: absolute;
49-
pointer-events: none;
50-
z-index: 6;
51-
display: inline-block;
52-
font-weight: 700;">
53-
@UserLevel
54-
</p>
55-
</div>
56-
</div>
1+
@using System.Globalization
2+
3+
<div class="hex-progress-bar-container" style="width: @(Size + "px"); height: @(Size + "px");">
4+
<svg viewBox="0 0 @Size @Size" class="hex-progress-bar-svg">
5+
@*Background Hexagon (optional for visual clarity when not filled)*@
6+
<polygon points="@HexagonPoints"
7+
fill="none"
8+
stroke="@BackgroundColor"
9+
stroke-width="@StrokeWidth"
10+
stroke-linejoin="round"
11+
stroke-linecap="round"/>
12+
13+
@*Progress Hexagon*@
14+
<polygon points="@HexagonPoints"
15+
fill="none"
16+
stroke="@ProgressColor"
17+
stroke-width="@StrokeWidth"
18+
stroke-linecap="round"
19+
stroke-linejoin="round"
20+
stroke-dasharray="@StrokeDashArray"
21+
stroke-dashoffset="@StrokeDashOffset"
22+
transform="rotate(-90 @HexCenterX @HexCenterY)"/> @*Rotate to start at the top*@
23+
</svg>
24+
25+
<div class="hex-content-center">
26+
@ChildContent
5727
</div>
5828
</div>
59-
@code {
60-
61-
[Parameter]
62-
public string ImageUrl { get; set; } = string.Empty;
63-
64-
[Parameter]
65-
public int UserLevel { get; set; }
66-
67-
[Parameter]
68-
public int Progress { get; set; }
69-
70-
[Parameter]
71-
public int ProgressMin { get; set; } = 0;
7229

73-
[Parameter]
74-
public int ProgressMax { get; set; } = 100;
7530

76-
[Parameter]
77-
public int Width { get; set; } = 100;
78-
79-
[Parameter]
80-
public int Height { get; set; } = 100;
31+
<style>
32+
.hex-progress-bar-container {
33+
position: relative;
34+
display: flex;
35+
align-items: center;
36+
justify-content: center;
37+
}
38+
39+
.hex-content-center {
40+
position: relative;
41+
z-index: 1;
42+
display: flex;
43+
flex-direction: column;
44+
align-items: center;
45+
justify-content: center;
46+
text-align: center;
47+
width: 100%;
48+
height: 100%;
49+
}
50+
51+
.hex-progress-bar-svg {
52+
position: absolute;
53+
top: 0;
54+
left: 0;
55+
width: 100%;
56+
height: 100%;
57+
transition: stroke-dashoffset 0.5s ease-in-out; /*Smooth transition for progress changes*/
58+
}
59+
</style>
8160

82-
[Parameter]
83-
public int LineWidth { get; set; } = 8;
61+
@code {
62+
[Parameter] public RenderFragment? ChildContent { get; set; }
63+
64+
/// <summary>
65+
/// Value between 0 and 100
66+
/// </summary>
67+
[Parameter] public double Progress { get; set; }
68+
69+
/// <summary>
70+
/// Size of SVG viewbox
71+
/// </summary>
72+
[Parameter] public double Size { get; set; } = 100;
73+
74+
/// <summary>
75+
/// Color to use for ProgressBar
76+
/// </summary>
77+
[Parameter] public string ProgressColor { get; set; } = "#E91E63";
78+
79+
/// <summary>
80+
/// Background Color
81+
/// </summary>
82+
[Parameter] public string BackgroundColor { get; set; } = "#343434";
83+
84+
/// <summary>
85+
/// Thickness of Progress Bar
86+
/// </summary>
87+
[Parameter] public double StrokeWidth { get; set; } = 8;
88+
8489

85-
[Parameter]
86-
public Color ProgressColor { get; set; } = Color.Primary;
90+
/// <summary>
91+
/// Radius of the Hexagon. Adjusted for stroke width and padding
92+
/// </summary>
93+
private double HexRadius => (Size / 2) - (StrokeWidth / 2 + 2);
94+
95+
private const double HexCenterX = 50;
96+
private const double HexCenterY = 50;
8797

88-
[Parameter]
89-
public RenderFragment? CenterContent { get; set; }
98+
private string HexagonPoints { get; set; } = "";
99+
private double HexagonPerimeter { get; set; }
90100

91-
[Parameter]
92-
public Color BadgeLineColor { get; set; } = Color.Secondary;
101+
private string StrokeDashArray => HexagonPerimeter.ToString(CultureInfo.InvariantCulture);
102+
private string StrokeDashOffset => (HexagonPerimeter - (HexagonPerimeter * Progress / 100)).ToString(CultureInfo.InvariantCulture);
93103

94-
[Parameter]
95-
public Color BadgeColor { get; set; } = Color.Tertiary;
104+
protected override void OnInitialized()
105+
{
106+
base.OnInitialized();
107+
CalculateHexagonPoints();
108+
CalculateHexagonPerimeter();
109+
}
96110

97-
readonly MudTheme _theme = new();
98-
99-
protected override async Task OnInitializedAsync()
111+
protected override void OnParametersSet()
100112
{
101-
await base.OnInitializedAsync();
113+
base.OnParametersSet();
114+
CalculateHexagonPoints();
115+
CalculateHexagonPerimeter();
102116
}
103117

104-
protected override async Task OnAfterRenderAsync(bool firstRender)
118+
private void CalculateHexagonPoints()
105119
{
106-
/*
107-
It is worth mentioning that
108-
109-
1. To have the line color appear -- you cannot use the gradient value
110-
2. To see the progress bar you cannot use the fill or clip value -- those are for images
111-
3. These methods are called AFTER render because we want to make sure our
112-
DOM elements are present before executing these scripts
113-
*/
114-
115-
await JsRuntime.InvokeVoidAsync("createHexagon", new
116-
{
117-
width = Width,
118-
height = Height,
119-
container = ".hexagon-progress",
120-
roundedCorners = true,
121-
lineWidth = LineWidth,
122-
lineColor = ColorUtil.GetCssValue(_theme, ProgressColor),
123-
scale = new { start = 0, end = 1, stop = MathUtil.GetNormalizedPercentage(Progress, ProgressMin, ProgressMax) }
124-
});
125-
126-
await JsRuntime.InvokeVoidAsync("createHexagon", new
127-
{
128-
width = 68,
129-
Height = 68,
130-
container = ".hexagon-image",
131-
roundedCorners = true,
132-
clip = true
133-
});
120+
var effectiveRadius = Math.Max(1, HexRadius);
134121

135-
await JsRuntime.InvokeVoidAsync("createHexagon", new
136-
{
137-
container = ".hexagon-dark",
138-
width = 26,
139-
height = 28,
140-
roundedCorners = true,
141-
roundedCornerRadius = 1,
142-
lineColor = ColorUtil.GetCssValue(_theme, BadgeLineColor),
143-
fill = true
144-
});
145-
146-
await JsRuntime.InvokeVoidAsync("createHexagon", new
122+
var points = new List<string>();
123+
124+
for (var i = 0; i < 6; i++)
147125
{
148-
container = ".hexagon-avatar-badge",
149-
width = 32,
150-
height = 36,
151-
fill = true,
152-
roundedCorners = true,
153-
roundedCornerRadius = 1,
154-
lineColor = ColorUtil.GetCssValue(_theme, BadgeColor)
155-
});
126+
var angleDeg = 60 * i;
127+
var angleRad = Math.PI / 180 * angleDeg;
128+
var x = HexCenterX + effectiveRadius * Math.Cos(angleRad);
129+
var y = HexCenterY + effectiveRadius * Math.Sin(angleRad);
130+
points.Add($"{x.ToString(CultureInfo.InvariantCulture)},{y.ToString(CultureInfo.InvariantCulture)}");
131+
}
132+
133+
HexagonPoints = string.Join(" ", points);
134+
HexagonPerimeter = 6 * effectiveRadius;
156135
}
157136

137+
private void CalculateHexagonPerimeter()
138+
{
139+
// For a regular hexagon, perimeter is 6 * side length
140+
// Side length for a hexagon inscribed in a circle with radius R is also R
141+
HexagonPerimeter = 6 * HexRadius;
142+
}
158143
}

0 commit comments

Comments
 (0)