Code/CharacterKit/HairColorPicker.razor

A Blazor-style Razor UI component for selecting hair color in the character editor. It renders a color square, hue slider, and a color swatch/text, tracks hue to preserve it across gray/black values, and notifies a callback when color changes.

File Access
@using Sandbox
@using Sandbox.UI
@using System
@namespace CharacterEditor
@inherits Panel
<root>
 <HairColorSquare Value=@Value Hue=@Hue OnValueChanged=@Pick />
 <div class="shapelabel"><span>Hue</span><span>@MathF.Round(Hue)°</span></div>
 <div class="hue-track"><SliderControl class="shapeslider hair-hue" Value=@Hue OnValueChanged=@SetHue Min=@(0f) Max=@(360f) Step=@(1f) ShowTextEntry=@false /></div>
 <div class="hair-color-value"><div class="colorswatch" style=@($"background-color: rgb({Byte(Value.r)},{Byte(Value.g)},{Byte(Value.b)});")></div><span>@($"#{Byte(Value.r):X2}{Byte(Value.g):X2}{Byte(Value.b):X2}")</span></div>
</root>
@code {
 public Color Value { get; set; }
 public Action<Color> OnValueChanged { get; set; }
 float Hue;
 Color LastColor;
 bool Initialized;
 static int Byte(float value) => (int)MathF.Round(Math.Clamp(value, 0, 1) * 255);

 protected override void OnParametersSet()
 {
  if (!Initialized || Value != LastColor)
  {
   var hsv = Value.ToHsv();
   // Keep the selected hue when passing through gray or black.
   if (!Initialized || (hsv.Saturation > .001f && hsv.Value > .001f)) Hue = hsv.Hue;
   LastColor = Value;
   Initialized = true;
  }
 }
 void Pick(Color color) { LastColor = Value = color; OnValueChanged?.Invoke(color); StateHasChanged(); }
 void SetHue(float hue) { Hue = hue; var hsv = Value.ToHsv(); Pick(new ColorHsv(Hue, hsv.Saturation, hsv.Value, 1).ToColor()); }
 protected override int BuildHash() => HashCode.Combine(Value, Hue);
}