Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ namespace Bit.BlazorUI;
/// </summary>
public partial class BitPhoneInput : BitInputBase<string?>
{
private readonly BitInputRateLimiter<ChangeEventArgs> _rateLimiter = new();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if BitInputRateLimiter implements IDisposable or IAsyncDisposable

ast-grep --pattern $'class BitInputRateLimiter$$$: $$$IDisposable$$$'

ast-grep --pattern $'class BitInputRateLimiter$$$: $$$IAsyncDisposable$$$'

# Also check for any Dispose methods
rg -nP --type=cs -A5 'class\s+BitInputRateLimiter' | rg -P '(Dispose|IDisposable|IAsyncDisposable)'

Repository: bitfoundation/bitplatform

Length of output: 51


🏁 Script executed:

# Find BitInputRateLimiter class definition
rg -n 'class BitInputRateLimiter' --type=cs

# Also search for the file that contains this class
fd -i 'bitinputratelimiter' --type=f

Repository: bitfoundation/bitplatform

Length of output: 222


🏁 Script executed:

# Read the BitInputRateLimiter.cs file
cat -n src/BlazorUI/Bit.BlazorUI/Utils/BitInputRateLimiter.cs

Repository: bitfoundation/bitplatform

Length of output: 2668


🏁 Script executed:

# Find BitDebouncer and BitThrottler classes
rg -n 'class BitDebouncer|class BitThrottler' --type=cs -A 1

Repository: bitfoundation/bitplatform

Length of output: 328


🏁 Script executed:

# Read BitDebouncer.cs
echo "=== BitDebouncer.cs ===" && cat -n src/BlazorUI/Bit.BlazorUI/Utils/BitDebouncer.cs

# Read BitThrottler.cs
echo "=== BitThrottler.cs ===" && cat -n src/BlazorUI/Bit.BlazorUI/Utils/BitThrottler.cs

Repository: bitfoundation/bitplatform

Length of output: 2410


🏁 Script executed:

# Check BitPhoneInput DisposeAsync method around lines 515-533
sed -n '515,533p' src/BlazorUI/Bit.BlazorUI.Extras/Components/PhoneInput/BitPhoneInput.razor.cs

Repository: bitfoundation/bitplatform

Length of output: 661


🏁 Script executed:

# Search for BitInputRateLimiter usage in component files
rg -n 'BitInputRateLimiter' --type=cs -B 2 -A 2 | head -80

Repository: bitfoundation/bitplatform

Length of output: 2249


🏁 Script executed:

# Check BitDropdown DisposeAsync method
rg -n 'DisposeAsync' src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs -A 20

# Check BitTextInputBase DisposeAsync method
rg -n 'DisposeAsync' src/BlazorUI/Bit.BlazorUI/Components/Inputs/BitTextInputBase.cs -A 20

Repository: bitfoundation/bitplatform

Length of output: 735


🏁 Script executed:

# Get the line numbers where DisposeAsync appears in BitDropdown
grep -n 'DisposeAsync' src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs

# Then check if _rateLimiter.Reset() is called
rg -n '_rateLimiter\.Reset' src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs

Repository: bitfoundation/bitplatform

Length of output: 331


🏁 Script executed:

# Check if BitDropdown's DisposeAsync calls _rateLimiter.Reset()
sed -n '1767,1790p' src/BlazorUI/Bit.BlazorUI/Components/Inputs/Dropdown/BitDropdown.razor.cs

Repository: bitfoundation/bitplatform

Length of output: 625


Call _rateLimiter.Reset() in the DisposeAsync method.

The _rateLimiter field holds a BitInputRateLimiter<ChangeEventArgs> instance that manages internal BitDebouncer and BitThrottler objects. Both of these classes hold CancellationTokenSource instances that must be disposed. The Reset() method properly disposes these resources, but BitPhoneInput.DisposeAsync does not call it. Other components using BitInputRateLimiter (such as BitDropdown and BitTextInputBase) correctly call _rateLimiter.Reset() during disposal. Add this call to prevent resource leaks.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/BlazorUI/Bit.BlazorUI.Extras/Components/PhoneInput/BitPhoneInput.razor.cs`
at line 11, The BitPhoneInput component has a _rateLimiter field of type
BitInputRateLimiter<ChangeEventArgs> that holds internal disposable resources
(BitDebouncer and BitThrottler objects with CancellationTokenSource instances).
You need to ensure these resources are properly cleaned up by calling
_rateLimiter.Reset() in the DisposeAsync method of BitPhoneInput. Add this
method call to the DisposeAsync method following the same pattern used in other
components like BitDropdown and BitTextInputBase, which already implement this
cleanup to prevent resource leaks.


private bool _isOpen;
private bool _hasFocus;
private int _activeIndex = -1;
Expand Down Expand Up @@ -64,6 +66,11 @@ public partial class BitPhoneInput : BitInputBase<string?>
[Parameter, TwoWayBound]
public BitCountry? Country { get; set; }

/// <summary>
/// The debounce time in milliseconds for the number input (applied when Immediate is enabled).
/// </summary>
[Parameter] public int DebounceTime { get; set; }

/// <summary>
/// The default selected country to be initially used when the Country parameter is not set.
/// </summary>
Expand Down Expand Up @@ -141,6 +148,11 @@ public partial class BitPhoneInput : BitInputBase<string?>
/// </summary>
[Parameter] public BitPhoneInputClassStyles? Styles { get; set; }

/// <summary>
/// The throttle time in milliseconds for the number input (applied when Immediate is enabled).
/// </summary>
[Parameter] public int ThrottleTime { get; set; }



/// <summary>
Expand Down Expand Up @@ -478,7 +490,8 @@ private async Task HandleOnNumberInput(ChangeEventArgs e)

if (Immediate is false) return;

await SetCurrentValueAsStringAsync(e.Value?.ToString());
await _rateLimiter.Run(e, DebounceTime, ThrottleTime, async args =>
await InvokeAsync(async () => await HandleOnNumberChange(args)));
}

private void HandleOnInputFocusIn()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,39 @@
<BitPhoneInput FullWidth DefaultCountry="BitCountries.Italy" Placeholder="Enter your number" />
</DemoExample>

<DemoExample Title="Disabled & ReadOnly" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7">
<DemoExample Title="Immediate" RazorCode="@example7RazorCode" CsharpCode="@example7CsharpCode" Id="example7">
<div>
By default the value updates on the change event (when the input loses focus). Enable the
<b>Immediate</b> parameter to update the value on every keystroke (the 'oninput' event). To
rate-limit how often the value updates while typing, combine it with <b>DebounceTime</b> (waits
until typing pauses) or <b>ThrottleTime</b> (updates at most once per interval).
</div>
<br />
<div>Immediate:</div>
<BitPhoneInput Immediate
DefaultCountry="BitCountries.UnitedStates"
Placeholder="Enter your number"
@bind-Value="immediateNumber" />
<div>Value: [@immediateNumber]</div>
<br />
<div>Immediate + DebounceTime (500ms):</div>
<BitPhoneInput Immediate
DebounceTime="500"
DefaultCountry="BitCountries.UnitedStates"
Placeholder="Enter your number"
@bind-Value="debouncedNumber" />
<div>Value: [@debouncedNumber]</div>
<br />
<div>Immediate + ThrottleTime (500ms):</div>
<BitPhoneInput Immediate
ThrottleTime="500"
DefaultCountry="BitCountries.UnitedStates"
Placeholder="Enter your number"
@bind-Value="throttledNumber" />
<div>Value: [@throttledNumber]</div>
</DemoExample>

<DemoExample Title="Disabled & ReadOnly" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8">
<div>Disabled:</div>
<br />
<BitPhoneInput IsEnabled="false" DefaultCountry="BitCountries.Spain" Value="1234567" />
Expand All @@ -82,7 +114,7 @@
<BitPhoneInput ReadOnly DefaultCountry="BitCountries.Spain" Value="1234567" />
</DemoExample>

<DemoExample Title="Events" RazorCode="@example8RazorCode" CsharpCode="@example8CsharpCode" Id="example8">
<DemoExample Title="Events" RazorCode="@example9RazorCode" CsharpCode="@example9CsharpCode" Id="example9">
<div>Use the OnCountryChange callback to react to the selected country changes.</div>
<br />
<BitPhoneInput DefaultCountry="BitCountries.UnitedStates"
Expand All @@ -92,7 +124,7 @@
<div>Selected country: @changedCountry?.Name</div>
</DemoExample>

<DemoExample Title="Style & Class" RazorCode="@example9RazorCode" CsharpCode="@example9CsharpCode" Id="example9">
<DemoExample Title="Style & Class" RazorCode="@example10RazorCode" CsharpCode="@example10CsharpCode" Id="example10">
<div>Customize the appearance of the BitPhoneInput using Style, Class, Styles, and Classes.</div>
<br />
<BitPhoneInput DefaultCountry="BitCountries.Netherlands"
Expand All @@ -103,7 +135,7 @@
Classes="@(new() { Input = "custom-input" })" />
</DemoExample>

<DemoExample Title="RTL" RazorCode="@example10RazorCode" Id="example10">
<DemoExample Title="RTL" RazorCode="@example11RazorCode" Id="example11">
<div>Use BitPhoneInput in right-to-left (RTL).</div>
<br />
<div dir="rtl">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ public partial class BitPhoneInputDemo
Href = "#country",
},
new()
{
Name = "DebounceTime",
Type = "int",
DefaultValue = "0",
Description = "The debounce time in milliseconds for the number input (applied when Immediate is enabled).",
},
new()
{
Name = "DropDirection",
Type = "BitDropDirection",
Expand Down Expand Up @@ -149,6 +156,13 @@ public partial class BitPhoneInputDemo
LinkType = LinkType.Link,
Href = "#class-styles",
},
new()
{
Name = "ThrottleTime",
Type = "int",
DefaultValue = "0",
Description = "The throttle time in milliseconds for the number input (applied when Immediate is enabled).",
},
];

private readonly List<ComponentSubClass> componentSubClasses =
Expand Down Expand Up @@ -214,6 +228,9 @@ public partial class BitPhoneInputDemo
private string? bindingNumber;
private BitCountry? bindingCountry;
private BitCountry? changedCountry;
private string? immediateNumber;
private string? debouncedNumber;
private string? throttledNumber;
private readonly List<BitCountry> customCountries =
[
BitCountries.UnitedStates,
Expand Down Expand Up @@ -267,30 +284,55 @@ public partial class BitPhoneInputDemo
<BitPhoneInput FullWidth DefaultCountry=""BitCountries.Italy"" Placeholder=""Enter your number"" />";

private readonly string example7RazorCode = @"
<BitPhoneInput Immediate
DefaultCountry=""BitCountries.UnitedStates""
Placeholder=""Enter your number""
@bind-Value=""immediateNumber"" />
<div>Value: [@immediateNumber]</div>

<BitPhoneInput Immediate
DebounceTime=""500""
DefaultCountry=""BitCountries.UnitedStates""
Placeholder=""Enter your number""
@bind-Value=""debouncedNumber"" />
<div>Value: [@debouncedNumber]</div>

<BitPhoneInput Immediate
ThrottleTime=""500""
DefaultCountry=""BitCountries.UnitedStates""
Placeholder=""Enter your number""
@bind-Value=""throttledNumber"" />
<div>Value: [@throttledNumber]</div>";
private readonly string example7CsharpCode = @"
private string? immediateNumber;
private string? debouncedNumber;
private string? throttledNumber;";

private readonly string example8RazorCode = @"
<BitPhoneInput IsEnabled=""false"" DefaultCountry=""BitCountries.Spain"" Value=""1234567"" />

<BitPhoneInput ReadOnly DefaultCountry=""BitCountries.Spain"" Value=""1234567"" />";
private readonly string example7CsharpCode = @"";
private readonly string example8CsharpCode = @"";

private readonly string example8RazorCode = @"
private readonly string example9RazorCode = @"
<BitPhoneInput DefaultCountry=""BitCountries.UnitedStates""
OnCountryChange=""c => changedCountry = c""
Placeholder=""Enter your number"" />

<div>Selected country: @changedCountry?.Name</div>";
private readonly string example8CsharpCode = @"
private readonly string example9CsharpCode = @"
private BitCountry? changedCountry;";

private readonly string example9RazorCode = @"
private readonly string example10RazorCode = @"
<BitPhoneInput DefaultCountry=""BitCountries.Netherlands""
Placeholder=""Enter your number""
Style=""width: 300px;""
Class=""custom-class""
Styles=""@(new() { FieldGroup = ""border-color: blueviolet;"" })""
Classes=""@(new() { Input = ""custom-input"" })"" />";
private readonly string example9CsharpCode = @"";
private readonly string example10CsharpCode = @"";

private readonly string example10RazorCode = @"
private readonly string example11RazorCode = @"
<div dir=""rtl"">
<BitPhoneInput Dir=""BitDir.Rtl"" DefaultCountry=""BitCountries.Iran"" Placeholder=""شماره خود را وارد کنید"" />
</div>";
Expand Down
Loading