Skip to content

Commit 4532d82

Browse files
Merge pull request #1 from DevExpress-Examples/25.1.3-feature
25.1.3 feature
2 parents 96bb0fe + 1cbd908 commit 4532d82

10 files changed

Lines changed: 341 additions & 70 deletions

File tree

BlazorSlider/Components/App.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<base href="/" />
1010
@DxResourceManager.RegisterTheme(FluentLight)
1111
@DxResourceManager.RegisterScripts()
12+
<link href=@AppendVersion("css/dx.fluent.blue.light.css") rel="stylesheet" />
1213
<link href=@AppendVersion("css/site.css") rel="stylesheet" />
1314
<link href=@AppendVersion("BlazorSlider.styles.css") rel="stylesheet" />
1415
<HeadOutlet />
Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,61 @@
11
@page "/"
2-
<PageTitle>Welcome</PageTitle>
2+
@rendermode InteractiveServer
33

4-
<div class="main-content">
5-
Welcome to your new DevExpress Blazor Application
6-
</div>
4+
@using BlazorSlider.Components.Slider
5+
6+
<div>
7+
<DxFormLayout CssClass="slider-fl" ItemCaptionAlignment="ItemCaptionAlignment.All">
8+
<DxFormLayoutGroup ColSpanXs="12">
9+
<Items>
10+
<DxFormLayoutItem ColSpanXs="12" Caption="Default mode">
11+
<DxSlider T="int" MinValue="0" MaxValue="100" Value="90" />
12+
</DxFormLayoutItem>
13+
<DxFormLayoutItem ColSpanXs="12" Caption="With labels">
14+
<DxSlider T="int" MinValue="0" MaxValue="100" Value="50">
15+
<DxSliderLabelSettings Visible="true"
16+
Position="VerticalEdge.Top" />
17+
</DxSlider>
18+
</DxFormLayoutItem>
19+
<DxFormLayoutItem ColSpanXs="12" Caption="With tooltip">
20+
<DxSlider T="int" MinValue="0" MaxValue="100" Value="35">
21+
<DxSliderTooltipSettings Enabled="true"
22+
ShowMode="TooltipShowMode.Always"
23+
Position="VerticalEdge.Bottom" />
24+
</DxSlider>
25+
</DxFormLayoutItem>
26+
<DxFormLayoutItem ColSpanXs="12" Caption="Without range highlighting">
27+
<DxSlider T="int" MinValue="0" MaxValue="100"
28+
Value="20" ShowRange="false" />
29+
</DxFormLayoutItem>
30+
<DxFormLayoutItem ColSpanXs="12" Caption="With discrete step">
31+
<DxSlider T="int" MinValue="0" MaxValue="100" Value="10" Step="10">
32+
<DxSliderTooltipSettings Enabled="true" />
33+
</DxSlider>
34+
</DxFormLayoutItem>
35+
<DxFormLayoutItem ColSpanXs="12" Caption="Disabled">
36+
<DxSlider T="int" MinValue="0" MaxValue="100"
37+
Value="50" Enabled="false" />
38+
</DxFormLayoutItem>
39+
</Items>
40+
</DxFormLayoutGroup>
41+
<DxFormLayoutGroup Caption="Process Value Changes" ColSpanXs="12">
42+
<Items>
43+
<DxFormLayoutItem ColSpanXs="12" Caption="On handle movement">
44+
<DxSlider @bind-Value="SliderValue" MinValue="0" MaxValue="100"
45+
ValueChangeMode="SliderValueChangeMode.OnHandleMove" />
46+
</DxFormLayoutItem>
47+
<DxFormLayoutItem ColSpanXs="12" Caption="On handle release">
48+
<DxSlider @bind-Value="SliderValue" MinValue="0" MaxValue="100"
49+
ValueChangeMode="SliderValueChangeMode.OnHandleRelease" />
50+
</DxFormLayoutItem>
51+
<DxFormLayoutItem ColSpanXs="12" Caption="Slider value">
52+
<DxSpinEdit @bind-Value="SliderValue" />
53+
</DxFormLayoutItem>
54+
</Items>
55+
</DxFormLayoutGroup>
56+
</DxFormLayout>
57+
</div>
58+
59+
@code{
60+
double SliderValue { get; set; } = 15;
61+
}
Lines changed: 5 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,6 @@
1-
::deep .welcome-gridlayout {
2-
margin: auto;
3-
width: auto;
4-
height: auto;
1+
::deep .slider-fl {
2+
max-height: 100vh;
3+
overflow-y: auto;
4+
overflow-x: hidden;
5+
padding: 1rem;
56
}
6-
7-
::deep .welcome-gridlayout .dxbl-gridlayout-root {
8-
align-content: center;
9-
justify-content: center;
10-
}
11-
12-
::deep .title {
13-
text-align: center;
14-
}
15-
16-
::deep .welcome-cards {
17-
display: flex;
18-
flex-wrap: wrap;
19-
gap: 1.5rem;
20-
justify-content: center;
21-
}
22-
23-
::deep .welcome-card {
24-
width: 26.25rem;
25-
height: 15rem;
26-
display: flex;
27-
flex-direction: column;
28-
align-items: center;
29-
justify-content: center;
30-
box-shadow: 0px 4px 6px -1px rgba(0, 0, 0, 0.1), 0px 2px 4px -2px rgba(0, 0, 0, 0.1);
31-
transition: box-shadow 0.2s;
32-
border-radius: 1rem;
33-
color: var(--bs-link-color, var(--DS-primary-90));
34-
gap: 1.5rem;
35-
text-decoration: none;
36-
position: relative;
37-
}
38-
39-
::deep .welcome-card:hover {
40-
box-shadow: 0px 20px 25px -5px rgba(0, 0, 0, 0.1), 0px 8px 10px -6px rgba(0, 0, 0, 0.1);
41-
}
42-
43-
::deep .welcome-card .welcome-card-img {
44-
width: 6.5rem;
45-
height: 6.5rem;
46-
}
47-
48-
::deep .welcome-card .welcome-card-text {
49-
font-size: 1.75rem;
50-
font-weight: 600;
51-
letter-spacing: 0em;
52-
text-align: center;
53-
text-decoration: unset;
54-
}
55-
56-
::deep .welcome-card .welcome-card-back {
57-
position: absolute;
58-
top: 0;
59-
left: 0;
60-
right: 0;
61-
bottom: 0;
62-
background: var(--bs-body-color, var(--DS-color-content-neutral-default-rest));
63-
opacity: 0.05;
64-
border-radius: 1rem;
65-
z-index: -2;
66-
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
@inject IJSRuntime JS
2+
@attribute [CascadingTypeParameter(nameof(T))]
3+
@typeparam T where T : struct
4+
@implements IAsyncDisposable
5+
6+
<CascadingValue IsFixed="true" Value="sliderState">
7+
@ChildContent
8+
<div @ref="sliderElement"></div>
9+
</CascadingValue>
10+
11+
@code {
12+
#region Fields
13+
readonly SliderState<T> sliderState = new();
14+
T prevValue;
15+
T internalValue;
16+
ElementReference sliderElement;
17+
DotNetObjectReference<DxSlider<T>>? dotNetRef;
18+
IJSObjectReference? clientModule;
19+
IJSObjectReference? clientSlider;
20+
#endregion
21+
22+
#region Properties
23+
DotNetObjectReference<DxSlider<T>> DotNetRef
24+
=> dotNetRef ??= DotNetObjectReference.Create(this);
25+
#endregion
26+
27+
#region Parameters
28+
[Parameter]
29+
public RenderFragment? ChildContent { get; set; }
30+
31+
[Parameter]
32+
public T MinValue { get; set; }
33+
34+
[Parameter]
35+
public T MaxValue { get; set; }
36+
37+
[Parameter]
38+
public T? Step { get; set; }
39+
40+
[Parameter]
41+
public T Value { get; set; }
42+
43+
[Parameter]
44+
public EventCallback<T> ValueChanged { get; set; }
45+
46+
[Parameter]
47+
public SliderValueChangeMode ValueChangeMode { get; set; }
48+
49+
[Parameter]
50+
public bool ShowRange { get; set; } = true;
51+
52+
[Parameter]
53+
public bool Enabled { get; set; } = true;
54+
#endregion
55+
56+
#region Lifecycle Events
57+
protected override async Task OnParametersSetAsync() {
58+
if(!Value.Equals(prevValue)) {
59+
internalValue = Value;
60+
prevValue = Value;
61+
}
62+
await UpdateServerState();
63+
await UpdateClientState();
64+
}
65+
66+
protected override async Task OnAfterRenderAsync(bool firstRender) {
67+
if(firstRender) {
68+
await JS.LoadDxResources();
69+
clientModule = await LoadSliderModule();
70+
clientSlider = await LoadClientInstance(clientModule);
71+
}
72+
}
73+
74+
async ValueTask IAsyncDisposable.DisposeAsync() {
75+
if(clientSlider != null)
76+
await clientSlider.DisposeAsync();
77+
if(clientModule != null)
78+
await clientModule.DisposeAsync();
79+
DotNetRef?.Dispose();
80+
}
81+
#endregion
82+
83+
#region Methods
84+
[JSInvokable]
85+
public async Task UpdateValueFromClient(T clientValue) {
86+
if(!internalValue.Equals(clientValue)) {
87+
internalValue = clientValue;
88+
await UpdateServerState();
89+
await ValueChanged.InvokeAsync(clientValue);
90+
}
91+
}
92+
93+
ValueTask UpdateServerState() {
94+
sliderState.Value = internalValue;
95+
sliderState.Step = Step;
96+
sliderState.ShowRange = ShowRange;
97+
sliderState.MinValue = MinValue;
98+
sliderState.MaxValue = MaxValue;
99+
sliderState.Enabled = Enabled;
100+
sliderState.ValueChangeMode = ValueChangeMode;
101+
return ValueTask.CompletedTask;
102+
}
103+
104+
ValueTask UpdateClientState() {
105+
if(clientSlider != null && clientModule != null) {
106+
return clientModule.InvokeVoidAsync("updateStateFromServer",
107+
clientSlider, sliderState);
108+
}
109+
return ValueTask.CompletedTask;
110+
}
111+
112+
ValueTask<IJSObjectReference> LoadSliderModule() {
113+
return JS.InvokeAsync<IJSObjectReference>(
114+
"import", "./Components/Slider/DxSlider.razor.js");
115+
}
116+
117+
ValueTask<IJSObjectReference> LoadClientInstance(IJSObjectReference module) {
118+
return module.InvokeAsync<IJSObjectReference>(
119+
"initializeSlider",
120+
sliderElement,
121+
DotNetRef,
122+
sliderState
123+
);
124+
}
125+
#endregion
126+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
let updateInvokedByServer = false;
2+
let serverIsProcessing = false;
3+
let queuedValue = null;
4+
5+
export async function initializeSlider(element, dotNet, sliderState) {
6+
return new DevExpress.ui.dxSlider(element, {
7+
min: sliderState.minValue,
8+
max: sliderState.maxValue,
9+
value: sliderState.value,
10+
step: sliderState.step === null ? 1 : sliderState.step,
11+
showRange: sliderState.showRange,
12+
disabled: !sliderState.enabled,
13+
valueChangeMode: decapitalize(sliderState.valueChangeMode),
14+
label: {
15+
visible: sliderState.labelVisible,
16+
format(value) {
17+
return `${value}%`;
18+
},
19+
position: decapitalize(sliderState.labelPosition)
20+
},
21+
tooltip: {
22+
enabled: sliderState.tooltipEnabled,
23+
format(value) {
24+
return `${value}%`;
25+
},
26+
showMode: decapitalize(sliderState.tooltipShowMode),
27+
position: decapitalize(sliderState.tooltipPosition)
28+
},
29+
onValueChanged({ value }) {
30+
if(!updateInvokedByServer) {
31+
scheduleServerValueUpdate(dotNet, value);
32+
}
33+
},
34+
});
35+
}
36+
37+
export async function updateStateFromServer(slider, state) {
38+
updateInvokedByServer = true;
39+
slider.option("value", state.value);
40+
slider.option("min", state.minValue);
41+
slider.option("max", state.maxValue);
42+
slider.option("step", state.step === null ? 1 : state.step);
43+
slider.option("showRange", state.showRange);
44+
slider.option("disabled", !state.enabled);
45+
slider.option("valueChangeMode", decapitalize(state.valueChangeMode));
46+
updateInvokedByServer = false;
47+
}
48+
49+
function decapitalize(word) {
50+
return word[0].toLowerCase() + word.slice(1);
51+
}
52+
53+
async function scheduleServerValueUpdate(dotNetRef, value) {
54+
if(serverIsProcessing) {
55+
queuedValue = value;
56+
return;
57+
}
58+
59+
serverIsProcessing = true;
60+
61+
try {
62+
await dotNetRef.invokeMethodAsync("UpdateValueFromClient", value);
63+
}
64+
finally {
65+
serverIsProcessing = false;
66+
if(queuedValue !== null) {
67+
const v = queuedValue;
68+
queuedValue = null;
69+
scheduleServerValueUpdate(dotNetRef, v);
70+
}
71+
}
72+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
@typeparam T where T : struct
2+
@code {
3+
[CascadingParameter]
4+
private SliderState<T>? SliderState { get; set; }
5+
6+
[Parameter]
7+
public bool Visible { get; set; }
8+
9+
[Parameter]
10+
public VerticalEdge Position { get; set; }
11+
12+
protected override void OnInitialized()
13+
{
14+
if(SliderState is null)
15+
return;
16+
SliderState.LabelVisible = Visible;
17+
SliderState.LabelPosition = Position;
18+
}
19+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@typeparam T where T : struct
2+
@code {
3+
[CascadingParameter]
4+
private SliderState<T>? SliderState { get; set; }
5+
6+
[Parameter]
7+
public bool Enabled { get; set; }
8+
9+
[Parameter]
10+
public VerticalEdge Position { get; set; }
11+
12+
[Parameter]
13+
public TooltipShowMode ShowMode { get; set; }
14+
15+
protected override void OnInitialized() {
16+
if(SliderState is null)
17+
return;
18+
SliderState.TooltipEnabled = Enabled;
19+
SliderState.TooltipPosition = Position;
20+
SliderState.TooltipShowMode = ShowMode;
21+
}
22+
}

0 commit comments

Comments
 (0)