Skip to content

Commit 2fcdbd7

Browse files
committed
Add resizable option to side dialog.
1 parent 09ff774 commit 2fcdbd7

File tree

5 files changed

+233
-22
lines changed

5 files changed

+233
-22
lines changed

Radzen.Blazor/DialogService.cs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,7 @@ public int CloseTabIndex
848848
}
849849

850850
private RenderFragment<DialogService> titleContent;
851+
private bool resizable;
851852

852853
/// <summary>
853854
/// Gets or sets the title content.
@@ -865,6 +866,23 @@ public RenderFragment<DialogService> TitleContent
865866
}
866867
}
867868
}
869+
870+
/// <summary>
871+
/// Gets or sets a value indicating whether the dialog is resizable. Set to <c>false</c> by default.
872+
/// </summary>
873+
/// <value><c>true</c> if resizable; otherwise, <c>false</c>.</value>
874+
public bool Resizable
875+
{
876+
get => resizable;
877+
set
878+
{
879+
if (resizable != value)
880+
{
881+
resizable = value;
882+
OnPropertyChanged(nameof(Resizable));
883+
}
884+
}
885+
}
868886
}
869887

870888
/// <summary>
@@ -992,24 +1010,6 @@ public class DialogOptions : DialogOptionsBase
9921010
/// </summary>
9931011
public string IconStyle { get; set; } = "margin-right: 0.75rem";
9941012

995-
996-
private bool resizable;
997-
/// <summary>
998-
/// Gets or sets a value indicating whether the dialog is resizable. Set to <c>false</c> by default.
999-
/// </summary>
1000-
/// <value><c>true</c> if resizable; otherwise, <c>false</c>.</value>
1001-
public bool Resizable
1002-
{
1003-
get => resizable;
1004-
set
1005-
{
1006-
if (resizable != value)
1007-
{
1008-
resizable = value;
1009-
OnPropertyChanged(nameof(Resizable));
1010-
}
1011-
}
1012-
}
10131013

10141014
private Action<Size> resize;
10151015

Radzen.Blazor/RadzenDialog.razor

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,26 @@
1212
{
1313
<aside
1414
class="@GetSideDialogCssClass()"
15+
@ref="sideDialogHandle"
1516
tabindex="@(isSideDialogOpen ? "0" : "-1")"
1617
style="@GetSideDialogStyle()"
17-
aria-labelledby="rz-dialog-side-label"
18-
>
18+
aria-labelledby="rz-dialog-side-label">
19+
@if (sideDialogOptions.Resizable)
20+
{
21+
<div @ref="sideDialogResizeHandle"
22+
class="@GetResizBarCssClass()"
23+
data-dir="@(sideDialogOptions.Position switch
24+
{
25+
DialogPosition.Left => "left",
26+
DialogPosition.Top => "top",
27+
DialogPosition.Bottom => "bottom",
28+
_ => "right"
29+
})"
30+
title="Drag to resize" aria-label="Resize side dialog">
31+
<span class="rz-resize" aria-hidden="true"></span>
32+
</div>
33+
34+
}
1935
@if (sideDialogOptions.ShowTitle)
2036
{
2137
<div class="rz-dialog-side-titlebar">
@@ -83,6 +99,9 @@
8399
RenderFragment sideDialogContent;
84100
SideDialogOptions sideDialogOptions;
85101
Dialog sideDialog;
102+
ElementReference? sideDialogResizeHandle;
103+
ElementReference? sideDialogHandle;
104+
IJSObjectReference sideDialogResizeHandleJsModule;
86105

87106
public async Task Open(string title, Type type, Dictionary<string, object> parameters, DialogOptions options)
88107
{
@@ -219,6 +238,9 @@
219238
.Add("rz-close", sideDialogClosing)
220239
.ToString();
221240

241+
string GetResizBarCssClass() => ClassList.Create("rz-dialog-resize-bar")
242+
.ToString();
243+
222244
string GetSideDialogStyle()
223245
{
224246
string widthStyle = string.IsNullOrEmpty(sideDialogOptions.Width) ? string.Empty : $"width: {sideDialogOptions.Width};";
@@ -233,7 +255,28 @@
233255

234256
if (isSideDialogOpen)
235257
{
236-
await JSRuntime.InvokeAsync<string>("Radzen.openSideDialog", sideDialogOptions);
258+
await JSRuntime.InvokeAsync<string>("Radzen.openSideDialog", sideDialogOptions);
259+
260+
if (sideDialogOptions.Resizable && sideDialogResizeHandle.HasValue)
261+
{
262+
sideDialogResizeHandleJsModule = await JSRuntime.InvokeAsync<IJSObjectReference>("Radzen.initSideDialogResize", sideDialogResizeHandle, sideDialogHandle);
263+
}
237264
}
238265
}
266+
267+
public async ValueTask DisposeAsync()
268+
{
269+
try
270+
{
271+
if (sideDialogResizeHandleJsModule != null)
272+
{
273+
await sideDialogResizeHandleJsModule.InvokeVoidAsync("dispose");
274+
}
275+
}
276+
catch
277+
{
278+
/* Ignore */
279+
}
280+
}
281+
239282
}

Radzen.Blazor/themes/components/blazor/_dialog.scss

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ $dialog-border-radius: var(--rz-border-radius) !default;
1919
$dialog-mask-zindex: 1000 !default;
2020
$dialog-zindex: 1001 !default;
2121
$dialog-transition: .1s ease-in-out !default;
22+
$dialog-side-resize-bar-color: var(--rz-splitter-bar-color);
23+
$dialog-side-resize-bar-background-color: var(--rz-splitter-bar-background-color);
24+
$dialog-side-resize-bar-hover-opacity: var(--rz-splitter-bar-hover-opacity);
25+
$dialog-side-resize-bar-thickness: 8px;
26+
$dialog-side-resize-bar-length: 100%;
27+
$dialog-side-resize-bar-handle-size: 16px;
2228

2329
.rz-dialog-wrapper {
2430
box-sizing: border-box;
@@ -56,6 +62,80 @@ $dialog-transition: .1s ease-in-out !default;
5662
box-shadow: var(--rz-dialog-shadow);
5763
border-radius: var(--rz-dialog-border-radius);
5864
overflow-y: auto;
65+
66+
> .rz-dialog-resize-bar {
67+
flex: 0 0 auto;
68+
position: absolute;
69+
text-align: center;
70+
display: inline-flex;
71+
align-items: center;
72+
justify-content: center;
73+
color: $dialog-side-resize-bar-color;
74+
background-color: $dialog-side-resize-bar-background-color;
75+
opacity: 0.4;
76+
user-select: none;
77+
78+
> .rz-resize {
79+
pointer-events: none;
80+
border: 1px solid $dialog-side-resize-bar-color;
81+
border-radius: 1px;
82+
}
83+
84+
&:hover {
85+
background-color: $dialog-side-resize-bar-background-color;
86+
opacity: $dialog-side-resize-bar-hover-opacity;
87+
}
88+
}
89+
90+
&-position-right,
91+
&-position-left {
92+
flex-direction: row;
93+
94+
> .rz-dialog-resize-bar {
95+
width: $dialog-side-resize-bar-thickness;
96+
height: $dialog-side-resize-bar-length;
97+
cursor: col-resize;
98+
flex-direction: column;
99+
100+
> .rz-resize {
101+
height: $dialog-side-resize-bar-handle-size;
102+
margin: 2px 0;
103+
}
104+
}
105+
}
106+
107+
&-position-top,
108+
&-position-bottom {
109+
flex-direction: column;
110+
111+
> .rz-dialog-resize-bar {
112+
width: $dialog-side-resize-bar-length;
113+
height: $dialog-side-resize-bar-thickness;
114+
cursor: row-resize;
115+
flex-direction: row;
116+
117+
> .rz-resize {
118+
width: $dialog-side-resize-bar-handle-size;
119+
margin: 0 2px;
120+
}
121+
}
122+
}
123+
124+
&-position-right > .rz-dialog-resize-bar {
125+
left: 0;
126+
}
127+
128+
&-position-left > .rz-dialog-resize-bar {
129+
right: 0;
130+
}
131+
132+
&-position-bottom > .rz-dialog-resize-bar {
133+
top: 0;
134+
}
135+
136+
&-position-top > .rz-dialog-resize-bar {
137+
bottom: 0;
138+
}
59139
}
60140

61141
.rz-dialog-titlebar,

Radzen.Blazor/wwwroot/Radzen.Blazor.js

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,6 +1452,89 @@ window.Radzen = {
14521452
}
14531453
}, 500);
14541454
},
1455+
initSideDialogResize: function(handle, sideDialog){
1456+
const dir = (handle.dataset.dir || 'right').toLowerCase();
1457+
const cs = window.getComputedStyle(sideDialog);
1458+
const parent = sideDialog.parentNode;
1459+
const parentIsFlex = parent && getComputedStyle(parent).display.includes('flex');
1460+
const toPixels = (v, axis) => {
1461+
if (!v || v === 'none') return NaN;
1462+
if (v.endsWith && v.endsWith('px')) return parseFloat(v);
1463+
if (v.endsWith && v.endsWith('%')) {
1464+
const base = axis === 'y' ? window.innerHeight : window.innerWidth;
1465+
const p = parseFloat(v);
1466+
return Number.isFinite(p) ? (base * p / 100) : NaN;
1467+
}
1468+
const n = parseFloat(v);
1469+
return Number.isFinite(n) ? n : NaN;
1470+
};
1471+
1472+
let MIN_W = toPixels(cs.minWidth, 'x') || 300;
1473+
let MAX_W = toPixels(cs.maxWidth, 'x') || Infinity;
1474+
let MIN_H = toPixels(cs.minHeight, 'y') || 200;
1475+
let MAX_H = toPixels(cs.maxHeight, 'y') || Infinity;
1476+
1477+
// Guard against invalid ranges caused by percentage max being smaller than min
1478+
if (Number.isFinite(MIN_W) && Number.isFinite(MAX_W) && MAX_W < MIN_W) MAX_W = Infinity;
1479+
if (Number.isFinite(MIN_H) && Number.isFinite(MAX_H) && MAX_H < MIN_H) MAX_H = Infinity;
1480+
1481+
let start = null;
1482+
1483+
const onDown = (e) => {
1484+
e.preventDefault();
1485+
handle.setPointerCapture?.(e.pointerId);
1486+
1487+
const rect = sideDialog.getBoundingClientRect();
1488+
start = { x: e.clientX, y: e.clientY, w: rect.width, h: rect.height };
1489+
1490+
document.addEventListener('pointermove', onMove);
1491+
document.addEventListener('pointerup', onUp, { once: true });
1492+
document.body.classList.add('dragging');
1493+
};
1494+
1495+
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
1496+
1497+
const applyWidth = (w) => {
1498+
if (parentIsFlex)
1499+
sideDialog.style.flexBasis = Math.round(w) + 'px';
1500+
else
1501+
sideDialog.style.width = Math.round(w) + 'px';
1502+
};
1503+
const applyHeight = (h) => {
1504+
sideDialog.style.height = `${Math.round(h)}px`;
1505+
};
1506+
1507+
const onMove = (e) => {
1508+
if (!start) return;
1509+
1510+
const dx = e.clientX - start.x;
1511+
const dy = e.clientY - start.y;
1512+
1513+
switch (dir) {
1514+
case 'right': applyWidth(clamp(start.w - dx, MIN_W, MAX_W)); break;
1515+
case 'left': applyWidth(clamp(start.w + dx, MIN_W, MAX_W)); break;
1516+
case 'bottom': applyHeight(clamp(start.h - dy, MIN_H, MAX_H)); break;
1517+
case 'top': applyHeight(clamp(start.h + dy, MIN_H, MAX_H)); break;
1518+
}
1519+
};
1520+
1521+
const onUp = (e) => {
1522+
handle.releasePointerCapture?.(e.pointerId);
1523+
start = null;
1524+
document.removeEventListener('pointermove', onMove);
1525+
document.body.classList.remove('dragging');
1526+
};
1527+
1528+
handle.addEventListener('pointerdown', onDown);
1529+
1530+
return {
1531+
dispose() {
1532+
handle.removeEventListener('pointerdown', onDown);
1533+
document.removeEventListener('pointermove', onMove);
1534+
document.body.classList.remove('dragging');
1535+
}
1536+
};
1537+
},
14551538
openDialog: function (options, dialogService, dialog) {
14561539
if (Radzen.closeAllPopups) {
14571540
Radzen.closeAllPopups();

RadzenBlazorDemos/Pages/DialogSide.razor

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
<RadzenLabel Text="Close on overlay click:" Component="Close" />
1616
<RadzenSwitch @bind-Value="@closeDialogOnOverlayClick" Disabled=@(!showMask) Name="Close" />
1717
</RadzenStack>
18+
<RadzenStack Orientation="Orientation.Horizontal" Gap="0.5rem" AlignItems="AlignItems.Center">
19+
<RadzenLabel Text="Resizable:" Component="Resizable" />
20+
<RadzenSwitch @bind-Value="@resizable" Name="Resizable" />
21+
</RadzenStack>
1822
</RadzenStack>
1923
<RadzenButton Text="Dialog on Side" ButtonStyle="ButtonStyle.Secondary" Click="@OpenSideDialog" />
2024
</div>
@@ -23,9 +27,10 @@
2327
DialogPosition position;
2428
bool closeDialogOnOverlayClick;
2529
bool showMask;
30+
bool resizable;
2631

2732
async Task OpenSideDialog()
2833
{
29-
await DialogService.OpenSideAsync<DialogSideContent>("Side Panel", options: new SideDialogOptions { CloseDialogOnOverlayClick = closeDialogOnOverlayClick, Position = position, ShowMask = showMask });
34+
await DialogService.OpenSideAsync<DialogSideContent>("Side Panel", options: new SideDialogOptions { CloseDialogOnOverlayClick = closeDialogOnOverlayClick, Resizable = resizable, Position = position, ShowMask = showMask });
3035
}
3136
}

0 commit comments

Comments
 (0)