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
1 change: 1 addition & 0 deletions BlazorSlider/Components/App.razor
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<base href="/" />
@DxResourceManager.RegisterTheme(FluentLight)
@DxResourceManager.RegisterScripts()
<link href=@AppendVersion("css/dx.fluent.blue.light.css") rel="stylesheet" />
<link href=@AppendVersion("css/site.css") rel="stylesheet" />
<link href=@AppendVersion("BlazorSlider.styles.css") rel="stylesheet" />
<HeadOutlet />
Expand Down
63 changes: 59 additions & 4 deletions BlazorSlider/Components/Pages/Index.razor
Original file line number Diff line number Diff line change
@@ -1,6 +1,61 @@
@page "/"
<PageTitle>Welcome</PageTitle>
@rendermode InteractiveServer

<div class="main-content">
Welcome to your new DevExpress Blazor Application
</div>
@using BlazorSlider.Components.Slider

<div>
<DxFormLayout CssClass="slider-fl" ItemCaptionAlignment="ItemCaptionAlignment.All">
<DxFormLayoutGroup ColSpanXs="12">
<Items>
<DxFormLayoutItem ColSpanXs="12" Caption="Default mode">
<DxSlider T="int" MinValue="0" MaxValue="100" Value="90" />
</DxFormLayoutItem>
<DxFormLayoutItem ColSpanXs="12" Caption="With labels">
<DxSlider T="int" MinValue="0" MaxValue="100" Value="50">
<DxSliderLabelSettings Visible="true"
Position="VerticalEdge.Top" />
</DxSlider>
</DxFormLayoutItem>
<DxFormLayoutItem ColSpanXs="12" Caption="With tooltip">
<DxSlider T="int" MinValue="0" MaxValue="100" Value="35">
<DxSliderTooltipSettings Enabled="true"
ShowMode="TooltipShowMode.Always"
Position="VerticalEdge.Bottom" />
</DxSlider>
</DxFormLayoutItem>
<DxFormLayoutItem ColSpanXs="12" Caption="Without range highlighting">
<DxSlider T="int" MinValue="0" MaxValue="100"
Value="20" ShowRange="false" />
</DxFormLayoutItem>
<DxFormLayoutItem ColSpanXs="12" Caption="With discrete step">
<DxSlider T="int" MinValue="0" MaxValue="100" Value="10" Step="10">
<DxSliderTooltipSettings Enabled="true" />
</DxSlider>
</DxFormLayoutItem>
<DxFormLayoutItem ColSpanXs="12" Caption="Disabled">
<DxSlider T="int" MinValue="0" MaxValue="100"
Value="50" Enabled="false" />
</DxFormLayoutItem>
</Items>
</DxFormLayoutGroup>
<DxFormLayoutGroup Caption="Process Value Changes" ColSpanXs="12">
<Items>
<DxFormLayoutItem ColSpanXs="12" Caption="On handle movement">
<DxSlider @bind-Value="SliderValue" MinValue="0" MaxValue="100"
ValueChangeMode="SliderValueChangeMode.OnHandleMove" />
</DxFormLayoutItem>
<DxFormLayoutItem ColSpanXs="12" Caption="On handle release">
<DxSlider @bind-Value="SliderValue" MinValue="0" MaxValue="100"
ValueChangeMode="SliderValueChangeMode.OnHandleRelease" />
</DxFormLayoutItem>
<DxFormLayoutItem ColSpanXs="12" Caption="Slider value">
<DxSpinEdit @bind-Value="SliderValue" />
</DxFormLayoutItem>
</Items>
</DxFormLayoutGroup>
</DxFormLayout>
</div>

@code{
double SliderValue { get; set; } = 15;
}
70 changes: 5 additions & 65 deletions BlazorSlider/Components/Pages/Index.razor.css
Original file line number Diff line number Diff line change
@@ -1,66 +1,6 @@
::deep .welcome-gridlayout {
margin: auto;
width: auto;
height: auto;
::deep .slider-fl {
max-height: 100vh;
overflow-y: auto;
overflow-x: hidden;
padding: 1rem;
}

::deep .welcome-gridlayout .dxbl-gridlayout-root {
align-content: center;
justify-content: center;
}

::deep .title {
text-align: center;
}

::deep .welcome-cards {
display: flex;
flex-wrap: wrap;
gap: 1.5rem;
justify-content: center;
}

::deep .welcome-card {
width: 26.25rem;
height: 15rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
box-shadow: 0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.2s;
border-radius: 1rem;
color: var(--bs-link-color, var(--DS-primary-90));
gap: 1.5rem;
text-decoration: none;
position: relative;
}

::deep .welcome-card:hover {
box-shadow: 0px 20px 25px -5px rgba(0, 0, 0, 0.1), 0px 8px 10px -6px rgba(0, 0, 0, 0.1);
}

::deep .welcome-card .welcome-card-img {
width: 6.5rem;
height: 6.5rem;
}

::deep .welcome-card .welcome-card-text {
font-size: 1.75rem;
font-weight: 600;
letter-spacing: 0em;
text-align: center;
text-decoration: unset;
}

::deep .welcome-card .welcome-card-back {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--bs-body-color, var(--DS-color-content-neutral-default-rest));
opacity: 0.05;
border-radius: 1rem;
z-index: -2;
}
126 changes: 126 additions & 0 deletions BlazorSlider/Components/Slider/DxSlider.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
@inject IJSRuntime JS
@attribute [CascadingTypeParameter(nameof(T))]
@typeparam T where T : struct
@implements IAsyncDisposable

<CascadingValue IsFixed="true" Value="sliderState">
@ChildContent
<div @ref="sliderElement"></div>
</CascadingValue>

@code {
#region Fields
readonly SliderState<T> sliderState = new();
T prevValue;
T internalValue;
ElementReference sliderElement;
DotNetObjectReference<DxSlider<T>>? dotNetRef;
IJSObjectReference? clientModule;
IJSObjectReference? clientSlider;
#endregion

#region Properties
DotNetObjectReference<DxSlider<T>> DotNetRef
=> dotNetRef ??= DotNetObjectReference.Create(this);
#endregion

#region Parameters
[Parameter]
public RenderFragment? ChildContent { get; set; }

[Parameter]
public T MinValue { get; set; }

[Parameter]
public T MaxValue { get; set; }

[Parameter]
public T? Step { get; set; }

[Parameter]
public T Value { get; set; }

[Parameter]
public EventCallback<T> ValueChanged { get; set; }

[Parameter]
public SliderValueChangeMode ValueChangeMode { get; set; }

[Parameter]
public bool ShowRange { get; set; } = true;

[Parameter]
public bool Enabled { get; set; } = true;
#endregion

#region Lifecycle Events
protected override async Task OnParametersSetAsync() {
if(!Value.Equals(prevValue)) {
internalValue = Value;
prevValue = Value;
}
await UpdateServerState();
await UpdateClientState();
}

protected override async Task OnAfterRenderAsync(bool firstRender) {
if(firstRender) {
await JS.LoadDxResources();
clientModule = await LoadSliderModule();
clientSlider = await LoadClientInstance(clientModule);
}
}

async ValueTask IAsyncDisposable.DisposeAsync() {
if(clientSlider != null)
await clientSlider.DisposeAsync();
if(clientModule != null)
await clientModule.DisposeAsync();
DotNetRef?.Dispose();
}
#endregion

#region Methods
[JSInvokable]
public async Task UpdateValueFromClient(T clientValue) {
if(!internalValue.Equals(clientValue)) {
internalValue = clientValue;
await UpdateServerState();
await ValueChanged.InvokeAsync(clientValue);
}
}

ValueTask UpdateServerState() {
sliderState.Value = internalValue;
sliderState.Step = Step;
sliderState.ShowRange = ShowRange;
sliderState.MinValue = MinValue;
sliderState.MaxValue = MaxValue;
sliderState.Enabled = Enabled;
sliderState.ValueChangeMode = ValueChangeMode;
return ValueTask.CompletedTask;
}

ValueTask UpdateClientState() {
if(clientSlider != null && clientModule != null) {
return clientModule.InvokeVoidAsync("updateStateFromServer",
clientSlider, sliderState);
}
return ValueTask.CompletedTask;
}

ValueTask<IJSObjectReference> LoadSliderModule() {
return JS.InvokeAsync<IJSObjectReference>(
"import", "./Components/Slider/DxSlider.razor.js");
}

ValueTask<IJSObjectReference> LoadClientInstance(IJSObjectReference module) {
return module.InvokeAsync<IJSObjectReference>(
"initializeSlider",
sliderElement,
DotNetRef,
sliderState
);
}
#endregion
}
72 changes: 72 additions & 0 deletions BlazorSlider/Components/Slider/DxSlider.razor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
let updateInvokedByServer = false;
let serverIsProcessing = false;
let queuedValue = null;

export async function initializeSlider(element, dotNet, sliderState) {
return new DevExpress.ui.dxSlider(element, {
min: sliderState.minValue,
max: sliderState.maxValue,
value: sliderState.value,
step: sliderState.step === null ? 1 : sliderState.step,
showRange: sliderState.showRange,
disabled: !sliderState.enabled,
valueChangeMode: decapitalize(sliderState.valueChangeMode),
label: {
visible: sliderState.labelVisible,
format(value) {
return `${value}%`;
},
position: decapitalize(sliderState.labelPosition)
},
tooltip: {
enabled: sliderState.tooltipEnabled,
format(value) {
return `${value}%`;
},
showMode: decapitalize(sliderState.tooltipShowMode),
position: decapitalize(sliderState.tooltipPosition)
},
onValueChanged({ value }) {
if(!updateInvokedByServer) {
scheduleServerValueUpdate(dotNet, value);
}
},
});
}

export async function updateStateFromServer(slider, state) {
updateInvokedByServer = true;
slider.option("value", state.value);
slider.option("min", state.minValue);
slider.option("max", state.maxValue);
slider.option("step", state.step === null ? 1 : state.step);
slider.option("showRange", state.showRange);
slider.option("disabled", !state.enabled);
slider.option("valueChangeMode", decapitalize(state.valueChangeMode));
updateInvokedByServer = false;
}

function decapitalize(word) {
return word[0].toLowerCase() + word.slice(1);
}

async function scheduleServerValueUpdate(dotNetRef, value) {
if(serverIsProcessing) {
queuedValue = value;
return;
}

serverIsProcessing = true;

try {
await dotNetRef.invokeMethodAsync("UpdateValueFromClient", value);
}
finally {
serverIsProcessing = false;
if(queuedValue !== null) {
const v = queuedValue;
queuedValue = null;
scheduleServerValueUpdate(dotNetRef, v);
}
}
}
19 changes: 19 additions & 0 deletions BlazorSlider/Components/Slider/DxSliderLabelSettings.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
@typeparam T where T : struct
@code {
[CascadingParameter]
private SliderState<T>? SliderState { get; set; }

[Parameter]
public bool Visible { get; set; }

[Parameter]
public VerticalEdge Position { get; set; }

protected override void OnInitialized()
{
if(SliderState is null)
return;
SliderState.LabelVisible = Visible;
SliderState.LabelPosition = Position;
}
}
22 changes: 22 additions & 0 deletions BlazorSlider/Components/Slider/DxSliderTooltipSettings.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@typeparam T where T : struct
@code {
[CascadingParameter]
private SliderState<T>? SliderState { get; set; }

[Parameter]
public bool Enabled { get; set; }

[Parameter]
public VerticalEdge Position { get; set; }

[Parameter]
public TooltipShowMode ShowMode { get; set; }

protected override void OnInitialized() {
if(SliderState is null)
return;
SliderState.TooltipEnabled = Enabled;
SliderState.TooltipPosition = Position;
SliderState.TooltipShowMode = ShowMode;
}
}
Loading
Loading