Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions examples/Demo/Shared/Components/SiteSettingsPanel.razor
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<FluentDesignTheme @ref=_theme
@bind-Mode="@Mode"
@bind-OfficeColor="@OfficeColor"
@bind-NeutralBaseColor="@NeutralColor"
Direction="@Direction"
StorageName="theme" />

Expand All @@ -31,6 +32,20 @@
</FluentStack>
</OptionTemplate>
</FluentSelect>
<FluentLabel>Neutral base color</FluentLabel>
<FluentStack VerticalAlignment="VerticalAlignment.Center">
<FluentTextField TextFieldType="TextFieldType.Color" @bind-Value="@NeutralColor" Style="width:150px; margin-bottom: 10px;" />
<FluentIcon Id="neutralinfo" Value="@(new Icons.Regular.Size24.Info())" OnClick="@(() => _popNIVisible = !_popNIVisible)" />
<FluentPopover Style="width: 350px;" AnchorId="neutralinfo" FixedPlacement="true" @bind-Open="@_popNIVisible">
<Header>Neutral base color</Header>
<Body>
<p>
The <code>NeutralBaseColor</code> design token is used to determine the color for layers and other neutral components. It uses a recipe to make sure a high enough value for the contrast is maintained.
</p>
</Body>
</FluentPopover>
</FluentStack>


<FluentSwitch Label="Direction"
Style="margin-bottom: 30px;"
Expand Down
12 changes: 11 additions & 1 deletion examples/Demo/Shared/Components/SiteSettingsPanel.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ namespace FluentUI.Demo.Shared.Components;

public partial class SiteSettingsPanel
{
private const string DEFAULT_NEUTRAL_COLOR = "#808080";

private CookieConsent? _cookie;
private string? _status;
private bool _popVisible;
private bool _popVisible, _popNIVisible;
private bool _ltr = true;
private FluentDesignTheme? _theme;

Expand All @@ -31,6 +33,8 @@ public partial class SiteSettingsPanel

public OfficeColor? OfficeColor { get; set; }

public string? NeutralColor { get; set; }

public LocalizationDirection? Direction { get; set; }

private static IEnumerable<DesignThemeModes> AllModes => Enum.GetValues<DesignThemeModes>();
Expand All @@ -49,7 +53,12 @@ protected override void OnAfterRender(bool firstRender)
{
Direction = GlobalState.Dir;
_ltr = !Direction.HasValue || Direction.Value == LocalizationDirection.LeftToRight;

NeutralColor = GlobalState.NeutralColor;
// Same default values is used for light and dark theme
NeutralColor ??= DEFAULT_NEUTRAL_COLOR;
}

}

protected void HandleDirectionChanged(bool isLeftToRight)
Expand All @@ -71,6 +80,7 @@ private async Task ResetSiteAsync()

OfficeColor = OfficeColorUtilities.GetRandom();
Mode = DesignThemeModes.System;
NeutralColor = DEFAULT_NEUTRAL_COLOR;
}

private async Task ManageCookieSettingsAsync()
Expand Down
6 changes: 4 additions & 2 deletions src/Core.Assets/src/Design/ThemeStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ThemeStorage {
}


public updateLocalStorage(mode: string | null, primaryColor: string | null): void {
public updateLocalStorage(mode: string | null, primaryColor: string | null, neutralColor: string | null): void {

// If LocalStorage is not available, do nothing.
if (localStorage == null) {
Expand All @@ -41,10 +41,11 @@ class ThemeStorage {
localStorage.setItem(this.storageName, JSON.stringify({
mode: ThemeStorage.getValueOrNull(mode),
primaryColor: ThemeStorage.getValueOrNull(primaryColor),
neutralColor: ThemeStorage.getValueOrNull(neutralColor),
}));
}

public readLocalStorage(): { mode: string | null, primaryColor: string | null } | null {
public readLocalStorage(): { mode: string | null, primaryColor: string | null, neutralColor: string | null } | null {

// If LocalStorage is not available, do nothing.
if (localStorage == null) {
Expand All @@ -69,6 +70,7 @@ class ThemeStorage {
return {
mode: ThemeStorage.getValueOrNull(storageItems?.mode),
primaryColor: ThemeStorage.getValueOrNull(storageItems?.primaryColor),
neutralColor: ThemeStorage.getValueOrNull(storageItems?.neutralColor),
}
}

Expand Down
42 changes: 40 additions & 2 deletions src/Core.Assets/src/DesignTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,32 @@ class DesignTheme extends HTMLElement {
this._synchronization.synchronizeOtherComponents("primary-color", value);
}

/**
* Gets the current neutral base color attribute value.
*/
get neutralColor(): string | null {
return this.getAttribute("neutral-color");
}

/**
* Sets the current neutral base color attribute value.
*/
set neutralColor(value: string | null) {
this.updateAttribute("neutral-color", value);

// Apply the color
if (value != null) {
const rgb = parseColorHexRGB(value);
if (rgb != null) {
const swatch = SwatchRGB.from(rgb);
neutralBaseColor.withDefault(swatch);
}

// Synchronization
this._synchronization.synchronizeOtherComponents("neutral-color", value);
}
}

/**
* Gets the current storage-name key, to persist the theme/color between sessions.
*/
Expand Down Expand Up @@ -147,6 +173,7 @@ class DesignTheme extends HTMLElement {
if (existingComponent != null) {
const mode = ThemeStorage.getValueOrNull(existingComponent.getAttribute("mode"));
const color = ThemeStorage.getValueOrNull(existingComponent.getAttribute("primary-color"))
const neutralColor = ThemeStorage.getValueOrNull(existingComponent.getAttribute("neutral-color"));

// Mode can be null in other components
this.attributeChangedCallback("mode", this.mode, mode);
Expand All @@ -155,6 +182,11 @@ class DesignTheme extends HTMLElement {
if (color != null) {
this.attributeChangedCallback("primary-color", this.primaryColor, color);
}

// Neutral color cannot be null in other components
if (neutralColor != null) {
this.attributeChangedCallback("neutral-color", this.neutralColor, neutralColor);
}
}

// Load from LocalStorage
Expand All @@ -164,6 +196,7 @@ class DesignTheme extends HTMLElement {
if (theme != null) {
this.attributeChangedCallback("mode", this.mode, theme.mode);
this.attributeChangedCallback("primary-color", this.primaryColor, theme.primaryColor);
this.attributeChangedCallback("neutral-color", this.neutralColor, theme.neutralColor);
}
}

Expand Down Expand Up @@ -202,7 +235,7 @@ class DesignTheme extends HTMLElement {

// Attributes to observe
static get observedAttributes() {
return ["mode", "primary-color", "storage-name"];
return ["mode", "primary-color", "neutral-color", "storage-name"];
}

// Attributes has changed.
Expand Down Expand Up @@ -234,6 +267,11 @@ class DesignTheme extends HTMLElement {
this.primaryColor = newValue;
break;

case "neutral-color":
this.dispatchAttributeChanged(name, oldValue, newValue);
this.neutralColor = newValue;
break;

case "storage-name":
this.storageName = newValue;
break;
Expand Down Expand Up @@ -298,7 +336,7 @@ class DesignTheme extends HTMLElement {
}
}
if (this.storageName != null) {
this._themeStorage.updateLocalStorage(this.mode, this.primaryColor);
this._themeStorage.updateLocalStorage(this.mode, this.primaryColor, this.neutralColor);
}

this._isInternalChange = false;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@namespace Microsoft.FluentUI.AspNetCore.Components
@namespace Microsoft.FluentUI.AspNetCore.Components

<fluent-design-theme id="@Id" mode="@GetMode()" primary-color="@GetColor()" storage-name="@StorageName" />
@ChildContent
<fluent-design-theme id="@Id" mode="@GetMode()" primary-color="@GetColor()" neutral-color="@NeutralBaseColor" storage-name="@StorageName" />
@ChildContent
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ public partial class FluentDesignTheme : ComponentBase
[Parameter]
public EventCallback<OfficeColor?> OfficeColorChanged { get; set; }

[Parameter]
public string? NeutralBaseColor { get; set; }

[Parameter]
public EventCallback<string?> NeutralBaseColorChanged { get; set; }

/// <summary>
/// Gets or sets the local storage name to save and retrieve the <see cref="Mode"/> and the <see cref="OfficeColor"/> / <see cref="CustomColor"/>.
/// </summary>
Expand Down Expand Up @@ -175,6 +181,12 @@ public async Task OnChangeRaisedAsync(string name, string value)
}
}

break;
case "neutral-color":
if (value.StartsWith('#'))
{
GlobalDesign.SetNeutralColor(value);
}
break;
}
}
Expand Down Expand Up @@ -256,13 +268,24 @@ private async Task ApplyLocalStorageValuesAsync(DataLocalStorage? theme)
await OnChangeRaisedAsync("primary-color", theme.PrimaryColor);
}
}

// Neutral base color
if (!string.IsNullOrEmpty(theme?.NeutralBaseColor))
{
if (theme.NeutralBaseColor.StartsWith('#'))
{
GlobalDesign.SetNeutralColor(theme.NeutralBaseColor);
}
await OnChangeRaisedAsync("neutral-base-color", theme.NeutralBaseColor);
}
}

/// <summary />
private class DataLocalStorage
{
public string? Mode { get; set; }
public string? PrimaryColor { get; set; }
public string? NeutralBaseColor { get; set; }
}

/// <summary />
Expand Down
12 changes: 12 additions & 0 deletions src/Core/GlobalState.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

using Microsoft.AspNetCore.Components;

namespace Microsoft.FluentUI.AspNetCore.Components;
Expand All @@ -16,6 +20,8 @@ public class GlobalState

public string? Color { get; set; }

public string? NeutralColor { get; set; }

public event Action? OnChange;

public void SetDirection(LocalizationDirection dir)
Expand All @@ -42,6 +48,12 @@ public void SetColor(string? color)
NotifyStateHasChanged();
}

public void SetNeutralColor(string? neutralColor)
{
NeutralColor = neutralColor;
NotifyStateHasChanged();
}

private void NotifyStateHasChanged()
{
OnChange?.Invoke();
Expand Down
Loading