Skip to content

Commit e7c6310

Browse files
authored
Merge pull request #446 from serverlessworkflow/feat-dashboard-light-theme
Enabled light theme support in the Dashboard
2 parents 511d103 + c21c20a commit e7c6310

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+962
-117
lines changed

src/dashboard/Synapse.Dashboard/Components/DocumentDetails/Store.cs

+15
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,23 @@ public override Task InitializeAsync()
388388
this.DocumentJson.SubscribeAsync(async (_) => {
389389
await this.SetTextEditorValueAsync();
390390
}, cancellationToken: this.CancellationTokenSource.Token);
391+
this.MonacoEditorHelper.PreferredThemeChanged += OnPreferedThemeChangedAsync;
391392
return base.InitializeAsync();
392393
}
393394

395+
/// <summary>
396+
/// Updates the editor theme
397+
/// </summary>
398+
/// <param name="newTheme"></param>
399+
/// <returns></returns>
400+
protected async Task OnPreferedThemeChangedAsync(string newTheme)
401+
{
402+
if (this.TextEditor != null)
403+
{
404+
await this.TextEditor.UpdateOptions(new EditorUpdateOptions() { Theme = newTheme });
405+
}
406+
}
407+
394408
private bool disposed;
395409
/// <summary>
396410
/// Disposes of the store
@@ -412,6 +426,7 @@ protected override void Dispose(bool disposing)
412426
this.TextEditor.Dispose();
413427
this.TextEditor = null;
414428
}
429+
this.MonacoEditorHelper.PreferredThemeChanged -= OnPreferedThemeChangedAsync;
415430
}
416431
this.disposed = true;
417432
}

src/dashboard/Synapse.Dashboard/Components/MonacoEditor/IMonacoEditorHelper.cs

+18
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ namespace Synapse.Dashboard.Components;
1919
/// <param name="newLanguage">The new preferred language.</param>
2020
/// <returns>A task representing the asynchronous operation of handling the event.</returns>
2121
public delegate Task PreferredLanguageChangedEventHandler(string newLanguage);
22+
/// <summary>
23+
/// Represents a delegate that is used to handle events related to changes in a user's preferred theme.
24+
/// </summary>
25+
/// <param name="newTheme">The new preferred theme.</param>
26+
/// <returns>A task representing the asynchronous operation of handling the event.</returns>
27+
public delegate Task PreferredThemeChangedEventHandler(string newTheme);
2228

2329
/// <summary>
2430
/// Represents a service used to facilitate the Monaco editor configuration
@@ -35,6 +41,11 @@ public interface IMonacoEditorHelper
3541
/// </summary>
3642
event PreferredLanguageChangedEventHandler? PreferredLanguageChanged;
3743

44+
/// <summary>
45+
/// Emits when the editor theme changes
46+
/// </summary>
47+
event PreferredThemeChangedEventHandler? PreferredThemeChanged;
48+
3849
/// <summary>
3950
/// A function used to facilitate the construction of <see cref="StandaloneEditorConstructionOptions"/>
4051
/// </summary>
@@ -58,6 +69,13 @@ public interface IMonacoEditorHelper
5869
/// <returns>A task representing the asynchronous operation</returns>
5970
Task ChangePreferredLanguageAsync(string language);
6071

72+
/// <summary>
73+
/// Changes the preferred editor theme
74+
/// </summary>
75+
/// <param name="theme">The new theme to use</param>
76+
/// <returns>A task representing the asynchronous operation</returns>
77+
Task ChangePreferredThemeAsync(string theme);
78+
6179
/// <summary>
6280
/// Returns the number of <see cref="TextModel"/> created and increases the count
6381
/// </summary>

src/dashboard/Synapse.Dashboard/Components/MonacoEditor/MonacoEditorHelper.cs

+24-6
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,23 @@ public class MonacoEditorHelper
1818
: IMonacoEditorHelper
1919
{
2020
private int _modelCount = 0;
21+
private string _preferredTheme = "vs-dark";
2122

2223
/// <inheritdoc />
2324
public string PreferredLanguage { get; protected set; } = "yaml";
2425

26+
2527
/// <inheritdoc />
2628
public event PreferredLanguageChangedEventHandler? PreferredLanguageChanged;
2729

30+
/// <inheritdoc />
31+
public event PreferredThemeChangedEventHandler? PreferredThemeChanged;
32+
2833
/// <inheritdoc />
2934
public Func<StandaloneCodeEditor, StandaloneEditorConstructionOptions> GetStandaloneEditorConstructionOptions(string value = "", bool readOnly = false, string language = "yaml") {
3035
return (StandaloneCodeEditor editor) => new StandaloneEditorConstructionOptions
3136
{
32-
Theme = "vs-dark",
37+
Theme = _preferredTheme,
3338
AutomaticLayout = true,
3439
Minimap = new EditorMinimapOptions { Enabled = false },
3540
Language = language,
@@ -63,18 +68,31 @@ public async Task ChangePreferredLanguageAsync(string language)
6368
if (!string.IsNullOrEmpty(language) && language != this.PreferredLanguage)
6469
{
6570
this.PreferredLanguage = language;
66-
await this.OnPreferredLanguageChangeAsync(language);
71+
if (this.PreferredLanguageChanged != null)
72+
{
73+
await this.PreferredLanguageChanged.Invoke(language);
74+
}
6775
}
6876
}
6977

7078
/// <inheritdoc />
71-
protected async Task OnPreferredLanguageChangeAsync(string language)
79+
public async Task ChangePreferredThemeAsync(string theme)
7280
{
73-
if (this.PreferredLanguageChanged != null)
81+
if (!string.IsNullOrEmpty(theme) && theme != this._preferredTheme)
7482
{
75-
await this.PreferredLanguageChanged.Invoke(language);
83+
if (theme == "dark")
84+
{
85+
theme = "vs-dark";
86+
}
87+
else if (theme == "light") {
88+
theme = "vs";
89+
}
90+
this._preferredTheme = theme;
91+
if (this.PreferredThemeChanged != null)
92+
{
93+
await this.PreferredThemeChanged.Invoke(theme);
94+
}
7695
}
77-
await Task.CompletedTask;
7896
}
7997

8098
/// <inheritdoc />

src/dashboard/Synapse.Dashboard/Components/MonacoEditor/Store.cs

+15
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,23 @@ public override Task InitializeAsync()
310310
{
311311
this.TextEditor?.UpdateOptions(new EditorUpdateOptions() { ReadOnly = isReadOnly });
312312
}, token: this.CancellationTokenSource.Token);
313+
this.MonacoEditorHelper.PreferredThemeChanged += OnPreferedThemeChangedAsync;
313314
return base.InitializeAsync();
314315
}
315316

317+
/// <summary>
318+
/// Updates the editor theme
319+
/// </summary>
320+
/// <param name="newTheme"></param>
321+
/// <returns></returns>
322+
protected async Task OnPreferedThemeChangedAsync(string newTheme)
323+
{
324+
if (this.TextEditor != null)
325+
{
326+
await this.TextEditor.UpdateOptions(new EditorUpdateOptions() { Theme = newTheme });
327+
}
328+
}
329+
316330
private bool disposed;
317331
/// <summary>
318332
/// Disposes of the store
@@ -334,6 +348,7 @@ protected override void Dispose(bool disposing)
334348
this.TextEditor.Dispose();
335349
this.TextEditor = null;
336350
}
351+
this.MonacoEditorHelper.PreferredThemeChanged -= OnPreferedThemeChangedAsync;
337352
}
338353
this.disposed = true;
339354
}

src/dashboard/Synapse.Dashboard/Components/ResourceEditor/ResourceEditor.razor

+15
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@
137137
this.textEditorValue = textEditorValue;
138138
await this.SetTextEditorValueAsync();
139139
}, cancellationToken: this.CancellationTokenSource.Token);
140+
this.MonacoEditorHelper.PreferredThemeChanged += OnPreferedThemeChangedAsync;
140141
}
141142

142143
/// <inheritdoc/>
@@ -155,6 +156,19 @@
155156
return base.OnParametersSetAsync();
156157
}
157158

159+
/// <summary>
160+
/// Updates the editor theme
161+
/// </summary>
162+
/// <param name="newTheme"></param>
163+
/// <returns></returns>
164+
protected async Task OnPreferedThemeChangedAsync(string newTheme)
165+
{
166+
if (this.textBasedEditor != null)
167+
{
168+
await this.textBasedEditor.UpdateOptions(new EditorUpdateOptions() { Theme = newTheme });
169+
}
170+
}
171+
158172
/// <summary>
159173
/// Sets the editor as read-only when saving
160174
/// </summary>
@@ -287,6 +301,7 @@
287301
this.textBasedEditor.Dispose();
288302
this.textBasedEditor = null;
289303
}
304+
this.MonacoEditorHelper.PreferredThemeChanged -= OnPreferedThemeChangedAsync;
290305
}
291306
this.disposed = true;
292307
}

src/dashboard/Synapse.Dashboard/Components/TaskInstanceDetails/TaskInstanceDetails.razor

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
</div>
3232
<div class="col-md-12 col-lg-4">
3333
<div class="label">Status</div>
34-
<span class="badge rounded-pill badge rounded-pill border [email protected]() text-@TaskInstance.Status.GetColorClass()">@(TaskInstance.Status ?? TaskInstanceStatus.Pending)</span>
34+
<span class="badge rounded-pill badge rounded-pill border @TaskInstance.Status.GetColorClass()">@(TaskInstance.Status ?? TaskInstanceStatus.Pending)</span>
3535
@if (!string.IsNullOrWhiteSpace(TaskInstance.StatusReason))
3636
{
3737
<div class="fst-italic">@TaskInstance.StatusReason</div>
@@ -120,7 +120,7 @@
120120
<td>@run.StartedAt.RelativeFormat()</td>
121121
<td class="text-center">@(run.EndedAt?.RelativeFormat() ?? "-")</td>
122122
<td class="text-center">@(run.EndedAt.HasValue ? run.EndedAt.Value.Subtract(run.StartedAt).ToString("hh\\:mm\\:ss\\.fff") : "-")</td>
123-
<td class="text-center"><span class="badge rounded-pill badge rounded-pill border [email protected]() text-@run.Outcome.GetColorClass()">@(run.Outcome ?? TaskInstanceStatus.Pending)</span></td>
123+
<td class="text-center"><span class="badge rounded-pill badge rounded-pill border @run.Outcome.GetColorClass()">@(run.Outcome ?? TaskInstanceStatus.Pending)</span></td>
124124
</tr>
125125
}
126126
</tbody>

src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/CorrelationContextRow.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
<tr @onclick="async _ => await OnToggleRow()" class="cursor-pointer">
2020
<td>@Kvp.Key</td>
21-
<td class="text-center"><span class="badge rounded-pill badge rounded-pill border [email protected]() text-@Kvp.Value.Status.GetColorClass()">@(Kvp.Value?.Status ?? CorrelationContextStatus.Inactive)</span></td>
21+
<td class="text-center"><span class="badge rounded-pill badge rounded-pill border @Kvp.Value.Status.GetColorClass()">@(Kvp.Value?.Status ?? CorrelationContextStatus.Inactive)</span></td>
2222
<td class="text-end"><Icon Name="@(isOpen ? IconName.CaretUp : IconName.CaretDown)" /></td>
2323
</tr>
2424
@if (isOpen)

src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/TaskInstanceRow.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
@if (TaskInstance != null) {
2020
<tr @onclick="async _ => await OnToggleRow()" class="cursor-pointer">
2121
<td>@TaskInstance.Reference</td>
22-
<td class="text-center"><span class="badge rounded-pill badge rounded-pill border [email protected]() text-@TaskInstance.Status.GetColorClass()">@(TaskInstance.Status ?? TaskInstanceStatus.Pending)</span></td>
22+
<td class="text-center"><span class="badge rounded-pill badge rounded-pill border @TaskInstance.Status.GetColorClass()">@(TaskInstance.Status ?? TaskInstanceStatus.Pending)</span></td>
2323
<td class="text-center">@(TaskInstance.StartedAt?.RelativeFormat() ?? "-")</td>
2424
<td class="text-center">@(TaskInstance.EndedAt?.RelativeFormat() ?? "-")</td>
2525
<td class="text-center">

src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
</div>
3232
<div class="col-md-12 col-lg-4">
3333
<div class="label">Status</div>
34-
<span class="badge rounded-pill badge rounded-pill border [email protected]?.Phase.GetColorClass() text-@workflowInstance.Status?.Phase.GetColorClass()">@(workflowInstance.Status?.Phase ?? WorkflowInstanceStatusPhase.Pending)</span>
34+
<span class="badge rounded-pill badge rounded-pill border @workflowInstance.Status?.Phase.GetColorClass()">@(workflowInstance.Status?.Phase ?? WorkflowInstanceStatusPhase.Pending)</span>
3535
</div>
3636
</div>
3737
<div class="row mb-3">

src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.css

-3
This file was deleted.

src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.min.css

-1
This file was deleted.

src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceDetails/WorkflowInstanceDetails.razor.scss

-5
This file was deleted.

src/dashboard/Synapse.Dashboard/Components/WorkflowInstanceLogs/WorkflowInstanceLogs.razor

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
@inherits StatefulComponent<WorkflowInstanceLogs, WorkflowInstanceLogsStore, WorkflowInstanceLogsState>
2121

2222
<div class="d-flex justify-content-between cursor-pointer" @onclick="async (_) => await Store.ToggleAsync()">
23-
Logs
23+
<span class="label">Logs</span>
2424
<Icon Name="@(isExpanded ? IconName.CaretUp : IconName.CaretDown)" />
2525
</div>
2626
<Collapse @ref="Store.Collapse" OnShowing="Store.LoadLogsAsync">

src/dashboard/Synapse.Dashboard/Components/WorkflowInstancesList/WorkflowInstancesList.razor

+3-3
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
}
5757
<input type="search" class="form-control rounded my-2 me-2" placeholder="Search" @oninput="OnSearchInputAsync" />
5858
<div class="dropdown d-flex align-content-center">
59-
<button class="btn btn-sm btn-dark" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="" @onclick:stopPropagation="true"><i class="bi bi-three-dots-vertical"></i></button>
59+
<button class="btn btn-sm" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="" @onclick:stopPropagation="true"><i class="bi bi-three-dots-vertical"></i></button>
6060
<ul class="dropdown-menu">
6161
<li><button class="dropdown-item @(selectedInstanceNames.Count() == 0 ? "text-mute" : "")" disabled="@(selectedInstanceNames.Count() == 0)" @onclick="OnSuspendSelectedClickedAsync" @onclick:preventDefault="true" @onclick:stopPropagation="true"><Icon Name="IconName.Pause" /> Suspend selected</button></li>
6262
<li><button class="dropdown-item @(selectedInstanceNames.Count() == 0 ? "text-mute" : "")" disabled="@(selectedInstanceNames.Count() == 0)" @onclick="OnResumeSelectedClickedAsync" @onclick:preventDefault="true" @onclick:stopPropagation="true"><Icon Name="IconName.Play" /> Resume selected</button></li>
@@ -113,7 +113,7 @@
113113
break;
114114
}
115115
case "Status":
116-
<span class="badge rounded-pill badge rounded-pill border border-@instance.Status?.Phase.GetColorClass() [email protected]?.Phase.GetColorClass()">@(instance.Status?.Phase ?? WorkflowInstanceStatusPhase.Pending)</span>
116+
<span class="badge rounded-pill badge rounded-pill border @((instance.Status?.Phase ?? WorkflowInstanceStatusPhase.Pending).GetColorClass())">@(instance.Status?.Phase ?? WorkflowInstanceStatusPhase.Pending)</span>
117117
break;
118118
case "Creation Time":
119119
@instance.Metadata.CreationTimestamp?.RelativeFormat()
@@ -145,7 +145,7 @@
145145
}
146146
case "Actions":
147147
<div class="dropdown">
148-
<button class="btn btn-sm btn-dark" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="" @onclick:stopPropagation="true"><i class="bi bi-three-dots-vertical"></i></button>
148+
<button class="btn btn-sm" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false" title="" @onclick:stopPropagation="true"><i class="bi bi-three-dots-vertical"></i></button>
149149
<ul class="dropdown-menu">
150150
@if (instance.Status?.Phase == WorkflowInstanceStatusPhase.Running)
151151
{

src/dashboard/Synapse.Dashboard/Extensions/StatusExtensions.cs

+1-29
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
using Synapse.Resources;
15-
1614
namespace Synapse.Dashboard.Extensions;
1715

1816
/// <summary>
@@ -25,31 +23,5 @@ public static class StatusExtensions
2523
/// </summary>
2624
/// <param name="status">The status to return the color class for</param>
2725
/// <returns></returns>
28-
public static string GetColorClass(this string? status) => status switch
29-
{
30-
// commented = same as above, which lead to the error "The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match."
31-
WorkflowInstanceStatusPhase.Running => "accent",
32-
//CorrelatorStatusPhase.Running => "accent",
33-
//OperatorStatusPhase.Running => "accent",
34-
CorrelationContextStatus.Active => "accent",
35-
//TaskInstanceStatus.Running => "accent",
36-
WorkflowInstanceStatusPhase.Faulted => "danger",
37-
//TaskInstanceStatus.Faulted => "danger",
38-
WorkflowInstanceStatusPhase.Cancelled => "warning",
39-
//TaskInstanceStatus.Cancelled => "warning",
40-
//CorrelationContextStatus.Cancelled => "warning",
41-
WorkflowInstanceStatusPhase.Completed => "success",
42-
//TaskInstanceStatus.Completed => "success",
43-
//CorrelationContextStatus.Completed => "success",
44-
WorkflowInstanceStatusPhase.Waiting => "cinereous",
45-
TaskInstanceStatus.Suspended => "icterine",
46-
//WorkflowInstanceStatusPhase.Suspended => "icterine",
47-
TaskInstanceStatus.Skipped => "cinereous",
48-
WorkflowInstanceStatusPhase.Pending => "mute",
49-
//TaskInstanceStatus.Pending => "mute",
50-
CorrelationContextStatus.Inactive => "mute",
51-
CorrelatorStatusPhase.Stopped => "secondary",
52-
//OperatorStatusPhase.Stopped => "secondary",
53-
_ => ""
54-
};
26+
public static string GetColorClass(this string? status) => $"status status-{status ?? "pending"}";
5527
}

src/dashboard/Synapse.Dashboard/Layout/MainLayout.razor

+14-1
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
@inject AuthenticationStateProvider AuthenticationStateProvider
2020
@inject IOptions<ApplicationOptions> Options
2121
@inject NavigationManager NavigationManager
22+
@inject JSInterop JsInterop
23+
@inject ILocalStorage Storage
24+
@inject IMonacoEditorHelper MonacoEditorHelper
2225

2326
<div class="page h-100 d-flex flex-column">
24-
<header class="header navbar navbar-expand-lg flex-row navbar-dark bg-dark-subtle">
27+
<header class="header navbar navbar-expand-lg flex-row">
2528
<a class="navbar-brand d-flex flex-row align-items-center justify-content-center" href="#">
2629
<svg class="logo" viewBox="-10 -10 60 60">
2730
<use href="#logo" />
@@ -92,6 +95,7 @@
9295
</li>
9396
</ul>
9497
</nav>
98+
<button class="btn btn-sm" @onclick="OnThemeClickedAsync" title="@(theme == "dark" ? "light" : "dark")"><Icon Name="@(theme == "dark" ? IconName.Sun : IconName.Moon)" /></button>
9599
<AuthorizeView>
96100
<Authorized>
97101
<Dropdown Class="me-3">
@@ -123,11 +127,14 @@
123127
@code{
124128

125129
ClaimsPrincipal? user;
130+
string theme = "dark";
126131

127132
protected override async Task OnInitializedAsync()
128133
{
129134
user = (await AuthenticationStateProvider.GetAuthenticationStateAsync()).User;
130135
AuthenticationStateProvider.AuthenticationStateChanged += OnAuthenticationStateChanged;
136+
theme = await Storage.GetItemAsync("preferedTheme") ?? "dark";
137+
await MonacoEditorHelper.ChangePreferredThemeAsync(theme);
131138
await base.OnInitializedAsync();
132139
}
133140

@@ -143,4 +150,10 @@
143150
return navLinkMatch == NavLinkMatch.All ? relativePath == href.ToLower() : relativePath.StartsWith(href.ToLower());
144151
}
145152

153+
async Task OnThemeClickedAsync()
154+
{
155+
theme = theme == "dark" ? "light" : "dark";
156+
await JsInterop.SetThemeAsync(theme);
157+
}
158+
146159
}

0 commit comments

Comments
 (0)