diff --git a/src/dashboard/CloudStreams.Dashboard/Components/Timeline/State.cs b/src/dashboard/CloudStreams.Dashboard/Components/Timeline/State.cs index 5a6e7936..9c3a6b36 100644 --- a/src/dashboard/CloudStreams.Dashboard/Components/Timeline/State.cs +++ b/src/dashboard/CloudStreams.Dashboard/Components/Timeline/State.cs @@ -30,4 +30,8 @@ public record TimelineState /// Gets/sets a boolean value that indicates whether data is currently being gathered /// public bool Loading { get; set; } = false; + /// + /// Gets/sets a boolean value that indicates whether to keep the previous chart's time frame or to redraw it with the new data boundaries + /// + public bool KeepTimeRange { get; set; } = false; } diff --git a/src/dashboard/CloudStreams.Dashboard/Components/Timeline/Store.cs b/src/dashboard/CloudStreams.Dashboard/Components/Timeline/Store.cs index c14e1b1f..32a5d406 100644 --- a/src/dashboard/CloudStreams.Dashboard/Components/Timeline/Store.cs +++ b/src/dashboard/CloudStreams.Dashboard/Components/Timeline/Store.cs @@ -45,6 +45,11 @@ public class TimelineStore(ICloudStreamsCoreApiClient cloudStreamsApi) /// public IObservable Loading => this.Select(state => state.Loading).DistinctUntilChanged(); + /// + /// Gets an used to observe changes + /// + public IObservable KeepTimeRange => this.Select(state => state.KeepTimeRange).DistinctUntilChanged(); + /// public override async Task InitializeAsync() { @@ -108,6 +113,16 @@ public void RemoveStreamsReadOption(int index) }); } + /// + /// Toggles the state's + /// + public void ToggleKeepTimeRange() + { + this.Reduce(state => state with { + KeepTimeRange = !state.KeepTimeRange + }); + } + /// /// Adds a for the provided if none already exists /// diff --git a/src/dashboard/CloudStreams.Dashboard/Components/Timeline/Timeline.razor b/src/dashboard/CloudStreams.Dashboard/Components/Timeline/Timeline.razor index b8a22fa1..95cfb861 100644 --- a/src/dashboard/CloudStreams.Dashboard/Components/Timeline/Timeline.razor +++ b/src/dashboard/CloudStreams.Dashboard/Components/Timeline/Timeline.razor @@ -25,6 +25,12 @@ } +
+ + +
@@ -87,6 +93,10 @@ /// private bool loading = false; /// + /// Indicates whether to keep the previous chart's time frame or to redraw it with the new data boundaries + /// + private bool keepTimeRange = false; + /// /// The style applied to the cloud event tooltip /// private string cloudEventTooltipStyle = ""; @@ -131,8 +141,8 @@ await base.OnInitializedAsync().ConfigureAwait(false); this.Store.StreamsReadOptions.Subscribe(streamsReadOptions => this.OnStateChanged(cmp => cmp.streamsReadOptions = streamsReadOptions), token: this.CancellationTokenSource.Token); this.Store.Loading.Subscribe(processing => this.OnStateChanged(cmp => cmp.loading = processing), token: this.CancellationTokenSource.Token); - this.Store.TimelineLanes.SubscribeAsync(async timelineLanes => await this.RenderTimelineAsync(timelineLanes), null, null, cancellationToken: this.CancellationTokenSource.Token); - + this.Store.KeepTimeRange.Subscribe(keepTimeRange => this.OnStateChanged(cmp => cmp.keepTimeRange = keepTimeRange), token: this.CancellationTokenSource.Token); + this.Store.TimelineLanes.SubscribeAsync(async timelineLanes => await this.RenderTimelineAsync(timelineLanes), null!, null!, cancellationToken: this.CancellationTokenSource.Token); } /// @@ -156,20 +166,24 @@ { return; } - var entries = timelineLanes.SelectMany(lane => lane.Data).OrderBy(data => data.Time!.Value); - if (!entries.Any()) + var timeEntries = timelineLanes + .SelectMany(lane => lane.Data) + .Where(data => data.Time != null) + .Select(data => data.Time!.Value) + .OrderBy(time => time); + if (!timeEntries.Any()) { return; } - var start = entries.First().Time!.Value; - var end = entries.Last().Time!.Value; + var start = timeEntries.First(); + var end = timeEntries.Last(); var delta = end.Subtract(start).TotalMilliseconds; if (delta == 0) { delta = 20; } var margin = delta / 20; - await this.eventDropsInterop.RenderTimelineAsync(this.timeline, this.dotnetReference, timelineLanes, start, end.AddMilliseconds(margin)); + await this.eventDropsInterop.RenderTimelineAsync(this.timeline, this.dotnetReference, timelineLanes, start, end.AddMilliseconds(margin), this.keepTimeRange); } /// diff --git a/src/dashboard/CloudStreams.Dashboard/Services/EventDropsInterop.cs b/src/dashboard/CloudStreams.Dashboard/Services/EventDropsInterop.cs index b2b4280d..37dc3520 100644 --- a/src/dashboard/CloudStreams.Dashboard/Services/EventDropsInterop.cs +++ b/src/dashboard/CloudStreams.Dashboard/Services/EventDropsInterop.cs @@ -45,10 +45,10 @@ public EventDropsInterop(IJSRuntime jsRuntime) /// The event-drops dataset /// The moment the timeline starts /// The moment the timeline starts> - public async ValueTask RenderTimelineAsync(ElementReference domElement, DotNetObjectReference? dotnetReference, IEnumerable dataset, DateTimeOffset start, DateTimeOffset end) + public async ValueTask RenderTimelineAsync(ElementReference domElement, DotNetObjectReference? dotnetReference, IEnumerable dataset, DateTimeOffset start, DateTimeOffset end, bool keepTimeRange) { var module = await moduleTask.Value; - await module.InvokeVoidAsync("renderTimeline", domElement, dotnetReference, dataset, start, end); + await module.InvokeVoidAsync("renderTimeline", domElement, dotnetReference, dataset, start, end, keepTimeRange); } /// diff --git a/src/dashboard/CloudStreams.Dashboard/wwwroot/js/event-drops-interop.js b/src/dashboard/CloudStreams.Dashboard/wwwroot/js/event-drops-interop.js index d9cd8b42..28b8605f 100644 --- a/src/dashboard/CloudStreams.Dashboard/wwwroot/js/event-drops-interop.js +++ b/src/dashboard/CloudStreams.Dashboard/wwwroot/js/event-drops-interop.js @@ -14,7 +14,7 @@ let previousChart; -export function renderTimeline(el, dotnetRef, dataset, start, end) { +export function renderTimeline(el, dotnetRef, dataset, start, end, keepTimeRange) { const chart = eventDrops({ ...baseConfig, range: { @@ -36,9 +36,12 @@ export function renderTimeline(el, dotnetRef, dataset, start, end) { .select(el) .data([dataset]) .call(chart); - if (!!previousChart) { + if (keepTimeRange && !!previousChart) { chart.zoomToDomain(previousChart.scale().domain()); } + else { + chart.zoomToDomain(chart.scale().domain()); + } previousChart = chart; }