2021-09-07 13:46:34 +08:00
using System.Diagnostics ;
2021-03-01 22:50:22 +08:00
using System.Text.Json ;
2020-03-23 09:31:08 +08:00
using System.Threading.Tasks ;
2020-09-09 00:52:14 +08:00
using AntDesign.JsInterop ;
using Microsoft.AspNetCore.Components ;
2021-03-01 22:50:22 +08:00
using Microsoft.JSInterop ;
2020-03-23 09:31:08 +08:00
2020-05-29 00:33:49 +08:00
namespace AntDesign
2020-03-23 09:31:08 +08:00
{
2020-06-27 01:20:52 +08:00
public partial class TextArea : Input < string >
2020-03-23 09:31:08 +08:00
{
2020-04-16 12:05:59 +08:00
private const uint DEFAULT_MIN_ROWS = 1 ;
2020-04-24 18:32:50 +08:00
2021-04-17 17:42:28 +08:00
protected override string InputType = > "textarea" ;
2021-03-01 22:50:22 +08:00
private uint _minRows = DEFAULT_MIN_ROWS ;
private uint _maxRows = uint . MaxValue ;
private bool _hasMinOrMaxSet ;
2021-09-07 13:46:34 +08:00
private bool _hasMinSet ;
2021-03-01 22:50:22 +08:00
private DotNetObjectReference < TextArea > _reference ;
2020-03-23 09:31:08 +08:00
2021-09-07 13:46:34 +08:00
/// <summary>
2022-05-15 12:45:43 +08:00
/// Will adjust (grow or shrink) the `TextArea` according to content.
2021-10-22 12:45:00 +08:00
/// Can work in connection with `MaxRows` and `MinRows`.
2021-09-07 13:46:34 +08:00
/// Sets resize attribute of the textarea HTML element to: none.
/// </summary>
2020-03-23 09:31:08 +08:00
[Parameter]
2021-09-07 13:46:34 +08:00
public bool AutoSize
{
get = > _autoSize ;
set
{
if ( _hasMinOrMaxSet & & ! value )
{
Debug . WriteLine ( "AntBlazor.TextArea: AutoSize cannot be set to false when either MinRows or MaxRows has been set.AutoSize has been switched to true." ) ;
_autoSize = true ;
}
else
{
_autoSize = value ;
}
}
}
2020-03-23 09:31:08 +08:00
2021-05-27 18:13:26 +08:00
/// <summary>
2022-05-15 12:45:43 +08:00
/// When `false`, value will be set to `null` when content is empty
/// or whitespace. When `true`, value will be set to empty string.
2021-05-27 18:13:26 +08:00
/// </summary>
2021-04-03 15:22:11 +08:00
[Parameter]
public bool DefaultToEmptyString { get ; set ; }
2021-03-01 22:50:22 +08:00
2021-05-27 18:13:26 +08:00
/// <summary>
2022-05-15 12:45:43 +08:00
/// `TextArea` will allow growing, but it will stop when visible
2021-05-27 18:13:26 +08:00
/// rows = MaxRows (will not grow further).
/// Default value = uint.MaxValue
/// </summary>
2020-03-23 09:31:08 +08:00
[Parameter]
2021-05-27 18:13:26 +08:00
public uint MaxRows
2020-03-31 21:23:25 +08:00
{
get
{
2021-05-27 18:13:26 +08:00
return _maxRows ;
2020-03-31 21:23:25 +08:00
}
set
{
2021-03-01 22:50:22 +08:00
_hasMinOrMaxSet = true ;
2021-05-27 18:13:26 +08:00
if ( value > = MinRows )
2020-03-31 21:23:25 +08:00
{
2021-05-27 18:13:26 +08:00
_maxRows = value ;
2021-09-07 13:46:34 +08:00
Debug . WriteLineIf ( ! AutoSize , "AntBlazor.TextArea: AutoSize cannot be set to false when either MinRows or MaxRows has been set.AutoSize has been switched to true." ) ;
2020-03-31 21:23:25 +08:00
AutoSize = true ;
}
else
{
2021-05-27 18:13:26 +08:00
_maxRows = uint . MaxValue ;
2021-09-07 13:46:34 +08:00
Debug . WriteLine ( $"AntBlazor.TextArea: Value of {nameof(MaxRows)}({MaxRows}) has to be between {nameof(MinRows)}({MinRows}) and {uint.MaxValue}" ) ;
2020-03-31 21:23:25 +08:00
}
}
}
2021-05-27 18:13:26 +08:00
/// <summary>
2022-05-15 12:45:43 +08:00
/// `TextArea` will allow shrinking, but it will stop when visible
2021-05-27 18:13:26 +08:00
/// rows = MinRows (will not shrink further).
/// Default value = DEFAULT_MIN_ROWS = 1
/// </summary>
2020-03-31 21:23:25 +08:00
[Parameter]
2021-05-27 18:13:26 +08:00
public uint MinRows
2020-03-31 21:23:25 +08:00
{
get
{
2021-05-27 18:13:26 +08:00
return _minRows ;
2020-03-31 21:23:25 +08:00
}
set
{
2021-03-01 22:50:22 +08:00
_hasMinOrMaxSet = true ;
2021-09-07 13:46:34 +08:00
_hasMinSet = true ;
2021-05-27 18:13:26 +08:00
if ( value > = DEFAULT_MIN_ROWS & & value < = MaxRows )
2020-03-31 21:23:25 +08:00
{
2021-05-27 18:13:26 +08:00
_minRows = value ;
2021-09-07 13:46:34 +08:00
Debug . WriteLineIf ( ! AutoSize , "AntBlazor.TextArea: AutoSize cannot be set to false when either MinRows or MaxRows has been set.AutoSize has been switched to true." ) ;
2020-03-31 21:23:25 +08:00
AutoSize = true ;
}
else
{
2021-05-27 18:13:26 +08:00
_minRows = DEFAULT_MIN_ROWS ;
2021-09-07 13:46:34 +08:00
Debug . WriteLine ( $"AntBlazor.TextArea: Value of {nameof(MinRows)}({MinRows}) has to be between {DEFAULT_MIN_ROWS} and {nameof(MaxRows)}({MaxRows})" ) ;
2020-03-31 21:23:25 +08:00
}
}
}
2021-09-07 13:46:34 +08:00
/// <summary>
/// Sets the height of the TextArea expressed in number of rows.
2023-07-27 08:14:58 +08:00
/// Default value is 2.
2021-09-07 13:46:34 +08:00
/// </summary>
[Parameter]
2023-07-27 08:14:58 +08:00
public uint Rows { get ; set ; } = 2 ;
2021-09-07 13:46:34 +08:00
2021-05-27 18:13:26 +08:00
/// <summary>
/// Callback when the size changes
/// </summary>
2020-03-31 21:23:25 +08:00
[Parameter]
2020-07-07 12:44:10 +08:00
public EventCallback < OnResizeEventArgs > OnResize { get ; set ; }
2020-03-23 09:31:08 +08:00
2021-10-29 13:30:52 +08:00
/// <inheritdoc/>
[Parameter]
public override string Value
{
get = > base . Value ;
set
{
if ( base . Value ! = value )
{
_valueHasChanged = true ;
2023-01-13 23:27:35 +08:00
_inputString = value ;
2021-10-29 13:30:52 +08:00
}
base . Value = value ;
}
}
private uint InnerMinRows = > _hasMinSet ? MinRows : Rows ;
2023-01-13 23:27:35 +08:00
private string Count = > $"{_inputString?.Length ?? 0}{(MaxLength > 0 ? $" / { MaxLength } " : " ")}" ;
2021-10-29 13:30:52 +08:00
2023-01-13 23:27:35 +08:00
private string _inputString ;
2021-05-27 18:13:26 +08:00
2023-01-13 23:27:35 +08:00
private ClassMapper _warpperClassMapper = new ( ) ;
private ClassMapper _textareaClassMapper = new ( ) ;
2021-03-12 17:02:11 +08:00
2023-02-24 14:31:47 +08:00
private bool _afterFirstRender = false ;
2021-03-12 17:02:11 +08:00
protected override void OnInitialized ( )
{
base . OnInitialized ( ) ;
_warpperClassMapper
2023-01-13 23:27:35 +08:00
. Add ( $"{PrefixCls}-affix-wrapper" )
. If ( $"{PrefixCls}-affix-wrapper-textarea-with-clear-btn" , ( ) = > AllowClear )
. If ( $"{PrefixCls}-affix-wrapper-has-feedback" , ( ) = > FormItem ? . HasFeedback = = true )
. GetIf ( ( ) = > $"{PrefixCls}-affix-wrapper-status-{FormItem?.ValidateStatus.ToString().ToLowerInvariant()}" , ( ) = > FormItem is { ValidateStatus : not FormValidateStatus . Default } )
2023-12-07 00:11:13 +08:00
. If ( $"{PrefixCls}-affix-wrapper-rtl" , ( ) = > RTL )
. GetIf ( ( ) = > $"{PrefixCls}-affix-wrapper-disabled" , ( ) = > Disabled )
;
2023-01-13 23:27:35 +08:00
ClassMapper
. Add ( "ant-input-textarea " )
. If ( "ant-input-textarea-show-count" , ( ) = > ShowCount )
. If ( "ant-input-textarea-in-form-item" , ( ) = > FormItem ! = null )
. If ( "ant-input-textarea-has-feedback" , ( ) = > FormItem ? . HasFeedback = = true )
. GetIf ( ( ) = > $"ant-input-textarea-status-{FormItem?.ValidateStatus.ToString().ToLowerInvariant()}" , ( ) = > FormItem is { ValidateStatus : not FormValidateStatus . Default } )
;
_textareaClassMapper
. Add ( "ant-input" )
. GetIf ( ( ) = > $"ant-input-status-{FormItem?.ValidateStatus.ToString().ToLowerInvariant()}" , ( ) = > FormItem is { ValidateStatus : not FormValidateStatus . Default } )
;
}
protected override void SetClasses ( )
{
// override the classmapper setting
2021-03-12 17:02:11 +08:00
}
2021-10-29 13:30:52 +08:00
protected override void OnParametersSet ( )
{
base . OnParametersSet ( ) ;
if ( _oldStyle ! = Style )
{
_styleHasChanged = true ;
_oldStyle = Style ;
}
}
2022-05-15 12:45:43 +08:00
protected override async Task OnFirstAfterRenderAsync ( )
2020-03-31 21:23:25 +08:00
{
await base . OnFirstAfterRenderAsync ( ) ;
if ( AutoSize )
{
2021-09-09 12:56:11 +08:00
DomEventListener . AddShared < JsonElement > ( "window" , "beforeunload" , Reloading ) ;
2020-03-31 21:23:25 +08:00
}
2021-10-29 13:30:52 +08:00
await RegisterResizeEvents ( ) ;
}
protected override async Task OnAfterRenderAsync ( bool firstRender )
{
await base . OnAfterRenderAsync ( firstRender ) ;
2023-02-24 14:31:47 +08:00
if ( firstRender )
{
_afterFirstRender = true ;
}
2023-07-11 23:38:58 +08:00
if ( _afterFirstRender )
2021-10-29 13:30:52 +08:00
{
2023-07-11 23:38:58 +08:00
if ( AutoSize & & _valueHasChanged )
2021-10-29 13:30:52 +08:00
{
2023-07-11 23:38:58 +08:00
_valueHasChanged = false ;
if ( _isInputing )
{
_isInputing = false ;
}
else if ( _afterFirstRender )
{
await JsInvokeAsync ( JSInteropConstants . InputComponentHelper . ResizeTextArea , Ref , InnerMinRows , MaxRows ) ;
}
2021-10-29 13:30:52 +08:00
}
2023-07-11 23:38:58 +08:00
if ( _styleHasChanged )
2021-10-29 13:30:52 +08:00
{
2023-07-11 23:38:58 +08:00
_styleHasChanged = false ;
if ( AutoSize & & ! string . IsNullOrWhiteSpace ( Style ) & & _afterFirstRender )
{
await JsInvokeAsync ( JSInteropConstants . StyleHelper . SetStyle , Ref , Style ) ;
}
2021-10-29 13:30:52 +08:00
}
}
}
/// <inheritdoc/>
2023-01-06 17:01:09 +08:00
protected override async Task OnInputAsync ( ChangeEventArgs args )
2021-10-29 13:30:52 +08:00
{
_isInputing = true ;
2023-01-13 23:27:35 +08:00
_inputString = args . Value . ToString ( ) ;
2023-01-06 17:01:09 +08:00
await base . OnInputAsync ( args ) ;
2020-03-31 21:23:25 +08:00
}
2023-01-13 23:27:35 +08:00
protected override void OnCurrentValueChange ( string value )
{
base . OnCurrentValueChange ( value ) ;
_inputString = value ;
}
2021-04-03 15:22:11 +08:00
protected override bool TryParseValueFromString ( string value , out string result , out string validationErrorMessage )
{
2021-09-07 13:46:34 +08:00
validationErrorMessage = null ;
2021-04-03 15:22:11 +08:00
if ( string . IsNullOrWhiteSpace ( value ) )
{
if ( DefaultToEmptyString )
result = string . Empty ;
else
result = default ;
return true ;
}
2021-09-07 13:46:34 +08:00
result = value ;
return true ;
2021-04-03 15:22:11 +08:00
}
2020-03-23 09:31:08 +08:00
2021-03-01 22:50:22 +08:00
protected override void Dispose ( bool disposing )
{
if ( AutoSize & & ! _isReloading )
2020-03-23 09:31:08 +08:00
{
2021-03-01 22:50:22 +08:00
_reference ? . Dispose ( ) ;
2022-05-15 12:45:43 +08:00
DomEventListener ? . Dispose ( ) ;
2021-03-01 22:50:22 +08:00
_ = InvokeAsync ( async ( ) = >
{
2023-04-06 23:14:27 +08:00
await JsInvokeAsync ( JSInteropConstants . DisposeResizeTextArea , Ref ) ;
2021-03-01 22:50:22 +08:00
} ) ;
2020-03-23 09:31:08 +08:00
}
2021-03-01 22:50:22 +08:00
base . Dispose ( disposing ) ;
}
/// <summary>
2021-03-12 17:02:11 +08:00
/// Indicates that a page is being refreshed
2021-03-01 22:50:22 +08:00
/// </summary>
private bool _isReloading ;
2022-05-15 12:45:43 +08:00
2021-09-07 13:46:34 +08:00
private bool _autoSize ;
2021-10-29 13:30:52 +08:00
private bool _valueHasChanged ;
private bool _isInputing ;
private string _oldStyle ;
private bool _styleHasChanged ;
private string _heightStyle ;
2021-03-12 17:02:11 +08:00
2021-03-01 22:50:22 +08:00
private void Reloading ( JsonElement jsonElement ) = > _isReloading = true ;
[JSInvokable]
public void ChangeSizeAsyncJs ( float width , float height )
{
2021-05-27 18:13:26 +08:00
if ( OnResize . HasDelegate )
OnResize . InvokeAsync ( new OnResizeEventArgs { Width = width , Height = height } ) ;
2020-03-23 09:31:08 +08:00
}
2021-10-29 13:30:52 +08:00
private async Task RegisterResizeEvents ( )
2020-03-23 09:31:08 +08:00
{
2021-03-01 22:50:22 +08:00
if ( _reference = = null )
{
_reference = DotNetObjectReference . Create < TextArea > ( this ) ;
}
2020-03-31 21:23:25 +08:00
2021-09-07 13:46:34 +08:00
if ( AutoSize )
{
2021-10-29 13:30:52 +08:00
await JsInvokeAsync < TextAreaInfo > (
2023-04-06 23:14:27 +08:00
JSInteropConstants . InputComponentHelper . RegisterResizeTextArea , Ref , InnerMinRows , MaxRows , _reference ) ;
2021-09-07 13:46:34 +08:00
}
else
{
2021-10-29 13:30:52 +08:00
var textAreaInfo = await JsInvokeAsync < TextAreaInfo > (
2023-04-06 23:14:27 +08:00
JSInteropConstants . InputComponentHelper . GetTextAreaInfo , Ref ) ;
2021-09-07 13:46:34 +08:00
2021-10-29 13:30:52 +08:00
var rowHeight = textAreaInfo . LineHeight ;
var offsetHeight = textAreaInfo . PaddingTop + textAreaInfo . PaddingBottom
+ textAreaInfo . BorderTop + textAreaInfo . BorderBottom ;
2020-03-31 21:23:25 +08:00
2021-10-29 13:30:52 +08:00
_heightStyle = $"height: {Rows * rowHeight + offsetHeight}px;overflow-y: auto;overflow-x: hidden;" ;
StateHasChanged ( ) ;
2020-03-31 21:23:25 +08:00
}
2020-03-23 09:31:08 +08:00
}
2021-09-07 13:46:34 +08:00
internal class TextAreaInfo
2020-07-07 12:44:10 +08:00
{
2021-03-01 22:50:22 +08:00
public double ScrollHeight { get ; set ; }
public double LineHeight { get ; set ; }
public double PaddingTop { get ; set ; }
public double PaddingBottom { get ; set ; }
public double BorderTop { get ; set ; }
public double BorderBottom { get ; set ; }
2020-07-07 12:44:10 +08:00
}
2020-03-23 09:31:08 +08:00
}
2021-04-17 17:42:28 +08:00
}