Skip to content

Commit 880301e

Browse files
committed
Add VerifyModal with text confirmation
1 parent 79e1d55 commit 880301e

6 files changed

Lines changed: 178 additions & 6 deletions

File tree

Directory.Packages.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.11" />
1313
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.11" />
1414
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.11" />
15-
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.8.1" />
15+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
1616
<PackageVersion Include="MinVer" Version="7.0.0" />
1717
<PackageVersion Include="PublicApiGenerator" Version="11.5.4" />
1818
<PackageVersion Include="System.Linq.Dynamic.Core" Version="1.7.3" />
1919
<PackageVersion Include="System.Net.Http.Json" Version="10.0.0" />
2020
<PackageVersion Include="Verify.XunitV3" Version="31.28.0" />
21-
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
22-
<PackageVersion Include="xunit.v3" Version="3.2.2" />
21+
<PackageVersion Include="xunit.runner.visualstudio" Version="4.0.0" />
22+
<PackageVersion Include="xunit.v3" Version="4.0.0" />
2323
</ItemGroup>
2424
<!-- TFM-specific overrides -->
2525
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">

samples/Sample.Core/Pages/Modals/Examples/Basic.razor

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,30 @@
3838
Delete Item
3939
</button>
4040
</div>
41-
4241
<div>
4342
Result: @_deleteConfirm
4443
</div>
4544

46-
<h4 class="my-3">Prompt Dialog with Custom Buttons</h4>
45+
<h4 class="my-3">Verify Dialog</h4>
46+
<div>
47+
<button type="button"
48+
class="btn btn-secondary"
49+
@onclick="ShowDeleteVerify">
50+
Delete All
51+
</button>
52+
</div>
53+
<div>
54+
Result: @_deleteVerify
55+
</div>
4756

57+
<h4 class="my-3">Prompt Dialog with Custom Buttons</h4>
4858
<div>
4959
<button type="button"
5060
class="btn btn-secondary"
5161
@onclick="ShowPrompt">
5262
Enter Name
5363
</button>
5464
</div>
55-
5665
<div>
5766
Result: @_promptResult
5867
</div>
@@ -62,6 +71,7 @@
6271
public required ModalService ModalService { get; set; }
6372

6473
private bool? _deleteConfirm;
74+
private bool? _deleteVerify;
6575
private string? _promptResult;
6676

6777
private async Task ShowPrimaryAlert()
@@ -120,6 +130,19 @@
120130
StateHasChanged();
121131
}
122132

133+
private async Task ShowDeleteVerify()
134+
{
135+
_deleteVerify = await ModalService.Verify(
136+
"Are you sure you want to delete this item? This action cannot be undone.",
137+
"Confirm Delete",
138+
"DELETE",
139+
ModalVariant.Danger,
140+
primaryAction: "Delete"
141+
);
142+
143+
StateHasChanged();
144+
}
145+
123146
private async Task ShowPrompt()
124147
{
125148
var modal = await ModalService.PromptModal(

src/LoreSoft.Blazor.Controls/Modals/Common/PromptModal.razor

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
class="dialog-input"
1818
required
1919
autocomplete="off"
20+
autofocus
2021
@bind="InputValue"
2122
@bind:event="oninput" />
2223
</div>
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
@namespace LoreSoft.Blazor.Controls
2+
@inherits ModalComponentBase
3+
4+
<div class="dialog-content @VariantClass">
5+
<form @onsubmit="SubmitAsync">
6+
<div class="dialog-header">
7+
<h3 class="dialog-title">@Title</h3>
8+
<button type="button"
9+
class="dialog-close-button"
10+
aria-label="Close"
11+
@onclick="CancelAsync" />
12+
</div>
13+
<div class="dialog-body">
14+
<p>@Message</p>
15+
<p>Type "@VerifyValue" to confirm.</p>
16+
<input type="text"
17+
name="verify-input"
18+
class="dialog-input"
19+
required
20+
autocomplete="off"
21+
autofocus
22+
@bind="InputValue"
23+
@bind:event="oninput" />
24+
</div>
25+
<div class="dialog-footer">
26+
<button type="button"
27+
class="dialog-secondary-button"
28+
@onclick="CancelAsync">
29+
@SecondaryAction
30+
</button>
31+
<button type="submit"
32+
class="dialog-primary-button"
33+
disabled="@IsDisabled">
34+
@PrimaryAction
35+
</button>
36+
</div>
37+
</form>
38+
</div>
39+
40+
@code {
41+
[Parameter]
42+
public string VerifyValue { get; set; } = "APPROVE";
43+
44+
protected string InputValue { get; set; } = string.Empty;
45+
46+
protected bool IsMatch => InputValue.Equals(VerifyValue, StringComparison.Ordinal);
47+
48+
protected bool IsDisabled => string.IsNullOrWhiteSpace(InputValue) || !IsMatch;
49+
50+
private async Task SubmitAsync()
51+
{
52+
await CloseAsync(InputValue);
53+
}
54+
}

src/LoreSoft.Blazor.Controls/Modals/ModalServiceExtensions.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,4 +264,89 @@ public static async Task<IModalReference> PromptModal(
264264

265265
return result.Data as string;
266266
}
267+
268+
/// <summary>
269+
/// Shows a verification modal dialog that requires the user to type a confirmation value before proceeding.
270+
/// </summary>
271+
/// <param name="modalService">The modal service instance.</param>
272+
/// <param name="message">The message text to display in the verification dialog.</param>
273+
/// <param name="title">The title of the dialog. Defaults to "Verify".</param>
274+
/// <param name="verifyValue">The text the user must type to enable the primary action. Defaults to "APPROVE".</param>
275+
/// <param name="type">The visual variant of the dialog. Defaults to <see cref="ModalVariant.Danger"/>.</param>
276+
/// <param name="primaryAction">The text for the primary action button. Defaults to "OK".</param>
277+
/// <param name="secondaryAction">The text for the secondary action button. Defaults to "Cancel".</param>
278+
/// <returns>
279+
/// A task that returns an <see cref="IModalReference"/> which can be used to interact with the modal and await its result.
280+
/// </returns>
281+
/// <remarks>
282+
/// This method displays a <see cref="VerifyModal"/> component with the specified parameters,
283+
/// including the value the user must type to confirm the action.
284+
/// </remarks>
285+
public static async Task<IModalReference> VerifyModal(
286+
this ModalService modalService,
287+
string message,
288+
string title = "Verify",
289+
string verifyValue = "APPROVE",
290+
ModalVariant type = ModalVariant.Danger,
291+
string primaryAction = "OK",
292+
string secondaryAction = "Cancel")
293+
{
294+
var parameters = ModalService.CreateParameters(
295+
message: message,
296+
title: title,
297+
type: type,
298+
primaryAction: primaryAction,
299+
secondaryAction: secondaryAction);
300+
301+
parameters[nameof(Controls.VerifyModal.VerifyValue)] = verifyValue;
302+
303+
return await modalService.Show<VerifyModal>(parameters);
304+
}
305+
306+
/// <summary>
307+
/// Shows a verification modal dialog and returns whether the user completed the verification.
308+
/// </summary>
309+
/// <param name="modalService">The modal service instance.</param>
310+
/// <param name="message">The message text to display in the verification dialog.</param>
311+
/// <param name="title">The title of the dialog. Defaults to "Verify".</param>
312+
/// <param name="verifyValue">The text the user must type to enable the primary action. Defaults to "APPROVE".</param>
313+
/// <param name="type">The visual variant of the dialog. Defaults to <see cref="ModalVariant.Danger"/>.</param>
314+
/// <param name="primaryAction">The text for the primary action button. Defaults to "OK".</param>
315+
/// <param name="secondaryAction">The text for the secondary action button. Defaults to "Cancel".</param>
316+
/// <returns>
317+
/// A task that returns <c>true</c> if the user verified and confirmed the action; otherwise, <c>false</c> if cancelled.
318+
/// </returns>
319+
/// <remarks>
320+
/// This is a convenience method that shows a verification dialog and automatically awaits the result,
321+
/// returning a boolean indicating whether the dialog was confirmed or cancelled.
322+
/// </remarks>
323+
/// <example>
324+
/// <code>
325+
/// if (await modalService.Verify("Are you sure you want to delete all records?"))
326+
/// {
327+
/// // User verified, proceed with the destructive action
328+
/// }
329+
/// </code>
330+
/// </example>
331+
public static async Task<bool> Verify(
332+
this ModalService modalService,
333+
string message,
334+
string title = "Verify",
335+
string verifyValue = "APPROVE",
336+
ModalVariant type = ModalVariant.Danger,
337+
string primaryAction = "OK",
338+
string secondaryAction = "Cancel")
339+
{
340+
var modal = await modalService.VerifyModal(
341+
message: message,
342+
title: title,
343+
verifyValue: verifyValue,
344+
type: type,
345+
primaryAction: primaryAction,
346+
secondaryAction: secondaryAction
347+
);
348+
349+
var result = await modal.Result;
350+
return !result.Cancelled;
351+
}
267352
}

src/LoreSoft.Blazor.Controls/wwwroot/css/modal-dialog.css

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@
7575
/* Primary button variables */
7676
--md-primary-button-color: white;
7777
--md-button-min-width: 4rem;
78+
/* Disabled variables */
79+
--md-disabled-opacity: 0.6;
7880
}
7981

8082
.dialog-container {
@@ -278,6 +280,13 @@
278280
background-color: var(--md-button-secondary-hover);
279281
}
280282

283+
/* Disabled States */
284+
.dialog-primary-button:disabled,
285+
.dialog-secondary-button:disabled {
286+
cursor: not-allowed;
287+
opacity: var(--md-disabled-opacity);
288+
}
289+
281290
/* Dialog Open/Close Animations */
282291
dialog {
283292
transition: display 300ms allow-discrete, overlay 1s allow-discrete;

0 commit comments

Comments
 (0)