Skip to content

Commit d20f222

Browse files
committed
Merge branch 'web-refactor' into 'main'
Add power mode to web example See merge request Wacton/Unicolour!99
2 parents 6065923 + 8958c58 commit d20f222

File tree

10 files changed

+498
-391
lines changed

10 files changed

+498
-391
lines changed

Example.Web/App.razor

Lines changed: 17 additions & 365 deletions
Large diffs are not rendered by default.

Example.Web/App.razor.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using Microsoft.AspNetCore.Components;
2+
3+
namespace Wacton.Unicolour.Example.Web;
4+
5+
public partial class App : ComponentBase
6+
{
7+
private bool conversionError;
8+
private string cssInsideGamut = null!;
9+
private string cssOutsideGamut = null!;
10+
private bool useLightText = true;
11+
private string rgbText = null!;
12+
private string warningEmoji = null!;
13+
private string warningText = null!;
14+
15+
private readonly Unicolour dark = new("404046");
16+
private readonly Unicolour light = new("e8e8ff");
17+
18+
protected override void OnInitialized()
19+
{
20+
UpdateDisplay();
21+
22+
State.OnChange += () =>
23+
{
24+
UpdateDisplay();
25+
StateHasChanged();
26+
};
27+
}
28+
29+
private void UpdateDisplay()
30+
{
31+
var colour = State.Colour;
32+
33+
conversionError = Utils.HasConversionError(colour);
34+
cssInsideGamut = Utils.ToCss(colour, 100);
35+
cssOutsideGamut = Utils.ToCss(colour, colour.IsInRgbGamut ? 100 : 50);
36+
37+
var rgbString = conversionError ? "NaN" : colour.Rgb.ToString();
38+
var hexString = !colour.IsInRgbGamut ? "#------" : colour.Hex;
39+
rgbText = $"RGB {rgbString} · {hexString}";
40+
41+
if (conversionError)
42+
{
43+
useLightText = false;
44+
}
45+
else
46+
{
47+
useLightText = colour.Contrast(light) > colour.Contrast(dark);
48+
}
49+
50+
if (conversionError)
51+
{
52+
warningText = "Cannot be converted";
53+
warningEmoji = "❌";
54+
}
55+
else if (!colour.IsInRgbGamut)
56+
{
57+
var (clippedR, clippedG, clippedB) = colour.Rgb.ConstrainedTriplet;
58+
warningText = $"Out of gamut, clipped to\nRGB {clippedR:F2} {clippedG:F2} {clippedB:F2} · {colour.Rgb.Byte255.ConstrainedHex}";
59+
warningEmoji = "⚠️";
60+
}
61+
else
62+
{
63+
warningText = string.Empty;
64+
warningEmoji = string.Empty;
65+
}
66+
}
67+
}

Example.Web/LightSpaces.razor

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<div class="column gap-small">
2+
<div class="colour-space-row">
3+
<select class="colour-space-select monospace" @onchange="SetColourSpace" title="Colour space selector">
4+
@foreach (var colourSpaceOption in ColourSpaceOptions)
5+
{
6+
<option value="@colourSpaceOption" selected="@(colourSpaceOption == colourSpace)">@colourSpaceOption</option>
7+
}
8+
</select>
9+
</div>
10+
11+
@* these could be componentised but event callbacks and state management is too much hassle for an example *@
12+
@foreach (var slider in sliders)
13+
{
14+
<div class="slider-wrapper">
15+
<input class="slider-gradient @(slider.InRange ? "show-thumb" : "hide-thumb")"
16+
style="background-image: linear-gradient(to right, @slider.CssGradient), linear-gradient(to right, @slider.CssAlphaGradient), var(--dark-stripes)"
17+
type="range" min="@slider.Min" max="@slider.Max" step="@slider.Step" value="@slider.Value" @oninput="args => SetSliderValue(slider, args)"
18+
title="Slider @(slider.AxisText)">
19+
<label class="slider-value-text monospace text-on-dark">@slider.ValueText</label>
20+
<label class="slider-axis-text monospace text-on-dark">@slider.AxisText</label>
21+
</div>
22+
}
23+
</div>
24+
25+
@*
26+
most styles are defined in global app.css
27+
the css here is for elements that are specific to this page
28+
e.g. colour space selection component (the actual colour sliders will be used in different pages in future, so are global)
29+
*@
30+
<style>
31+
.colour-space-row {
32+
display: flex;
33+
flex-direction: row;
34+
align-items: center;
35+
justify-content: space-between;
36+
gap: 1rem;
37+
width: 100%;
38+
}
39+
40+
.colour-space-select {
41+
flex: 0 0 33%;
42+
}
43+
44+
@@media screen and (max-width: 720px) {
45+
.colour-space-select {
46+
flex: 0 0 50%;
47+
}
48+
}
49+
</style>

Example.Web/LightSpaces.razor.cs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using Microsoft.AspNetCore.Components;
2+
3+
namespace Wacton.Unicolour.Example.Web;
4+
5+
public partial class LightSpaces : ComponentBase
6+
{
7+
private ColourSpace colourSpace = ColourSpace.Rgb255;
8+
private readonly Slider[] sliders = [new(0), new(1), new(2)];
9+
10+
private static ColourSpace[] ColourSpaceOptions = ColourLookup.RangeLookup.Keys.OrderBy(x => x.ToString()).ToArray();
11+
12+
protected override void OnInitialized()
13+
{
14+
SetAllSliders();
15+
16+
State.OnChange += () =>
17+
{
18+
SetAllSliders();
19+
StateHasChanged();
20+
};
21+
}
22+
23+
private void SetAllSliders()
24+
{
25+
var (first, second, third) = State.Colour.GetRepresentation(colourSpace);
26+
sliders[0].Value = first;
27+
sliders[1].Value = second;
28+
sliders[2].Value = third;
29+
UpdateSliders();
30+
}
31+
32+
private void SetColourSpace(ChangeEventArgs args)
33+
{
34+
var colourSpaceName = (args.Value == null ? string.Empty : args.Value.ToString()) ?? string.Empty;
35+
_ = Enum.TryParse(colourSpaceName, out colourSpace);
36+
37+
SetAllSliders();
38+
SetColour();
39+
}
40+
41+
private static double ParseValue(ChangeEventArgs args) => double.Parse((args.Value == null ? string.Empty : args.Value.ToString()) ?? string.Empty);
42+
private void SetSliderValue(Slider slider, ChangeEventArgs args) => SetSliderValue(slider, ParseValue(args));
43+
private void SetSliderValue(Slider slider, double value)
44+
{
45+
slider.Value = value;
46+
UpdateSliders();
47+
SetColour();
48+
}
49+
50+
private void UpdateSliders()
51+
{
52+
for (var i = 0; i < sliders.Length; i++)
53+
{
54+
sliders[i].ColourSpace = colourSpace;
55+
var startColour = new Unicolour(colourSpace, GetStartValue(0), GetStartValue(1), GetStartValue(2));
56+
var endColour = new Unicolour(colourSpace, GetEndValue(0), GetEndValue(1), GetEndValue(2));
57+
58+
sliders[i].Stops = GetColourStops(startColour, endColour);
59+
continue;
60+
61+
double GetStartValue(int index) => index == i ? sliders[index].Min : sliders[index].Value;
62+
double GetEndValue(int index) => index == i ? sliders[index].Max : sliders[index].Value;
63+
}
64+
}
65+
66+
private List<Unicolour> GetColourStops(Unicolour start, Unicolour end)
67+
{
68+
var sections = colourSpace is ColourSpace.Hct or ColourSpace.Munsell ? 8 : 16;
69+
var stops = new List<Unicolour> { start };
70+
for (var i = 1; i < sections; i++)
71+
{
72+
var distance = 1.0 / sections * i;
73+
stops.Add(start.Mix(end, colourSpace, distance, HueSpan.Increasing));
74+
}
75+
76+
stops.Add(end);
77+
return stops;
78+
}
79+
80+
private void SetColour()
81+
{
82+
State.Colour = new Unicolour(colourSpace, sliders[0].Value, sliders[1].Value, sliders[2].Value);
83+
}
84+
}

Example.Web/Pages/Light.razor

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@* @page "/Light" TODO: decide if want to route via layout *@
2+
3+
<div class="column gap-large dark-background power-button-reference-point">
4+
<LightSpaces />
5+
6+
@if (powerMode)
7+
{
8+
<LightSpaces />
9+
}
10+
11+
<button class="power-button monospace" @onclick="TogglePower">
12+
<span>&#x23FB;</span> @**@
13+
</button>
14+
</div>
15+
16+
<style>
17+
.power-button {
18+
/* easiest way to centre button content it seems... */
19+
display: flex;
20+
align-items: center;
21+
justify-content: center;
22+
height: 3rem;
23+
width: 3rem;
24+
25+
/* and it's also absolute positioned to be top-right inside the dark background area */
26+
position: absolute;
27+
top: 1.5rem;
28+
right: 1.5rem;
29+
}
30+
31+
.power-button-reference-point {
32+
position: relative;
33+
}
34+
</style>

Example.Web/Pages/Light.razor.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.AspNetCore.Components;
2+
3+
namespace Wacton.Unicolour.Example.Web.Pages;
4+
5+
public partial class Light : ComponentBase
6+
{
7+
private bool powerMode;
8+
9+
private void TogglePower()
10+
{
11+
powerMode = !powerMode;
12+
}
13+
}

Example.Web/Slider.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ internal class Slider
55
private readonly int index;
66
internal double Value { get; set; }
77
internal string ValueText => GetValueText();
8-
internal string AxisText => ColourLookup.AxisLookup[ColourSpace][index];
9-
8+
109
internal ColourSpace ColourSpace { get; set; }
10+
internal string AxisText => ColourLookup.AxisLookup[ColourSpace][index];
1111
private Range Range => ColourLookup.RangeLookup[ColourSpace][index];
12+
1213
internal double Min => Range.Min;
1314
internal double Max => Range.Max;
1415
internal bool InRange => Value >= Min && Value <= Max;
@@ -18,10 +19,9 @@ internal class Slider
1819
internal string CssGradient => string.Join(",", Stops.Select(x => Utils.ToCss(x, 100)));
1920
internal string CssAlphaGradient => string.Join(",", Stops.Select(x => Utils.ToCss(x, x.IsInRgbGamut ? 100 : 50)));
2021

21-
internal Slider(int index, double value)
22+
internal Slider(int index)
2223
{
2324
this.index = index;
24-
Value = value;
2525
}
2626

2727
private string GetValueText()

Example.Web/State.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace Wacton.Unicolour.Example.Web;
2+
3+
// arguably unpleasant but seems fine for a small app with 1 item of shared state
4+
// not worth the effort of state injection or cascading callbacks
5+
public static class State
6+
{
7+
private static Unicolour colour = new("ff1493");
8+
public static Unicolour Colour
9+
{
10+
get => colour;
11+
set
12+
{
13+
colour = value;
14+
OnChange?.Invoke();
15+
}
16+
}
17+
18+
public static event Action? OnChange;
19+
}

0 commit comments

Comments
 (0)