Skip to content

Commit

Permalink
[dev-v5] Add FluentTextArea component (#3341)
Browse files Browse the repository at this point in the history
  • Loading branch information
AClerbois authored Feb 14, 2025
1 parent e4edb61 commit 85fe71c
Show file tree
Hide file tree
Showing 20 changed files with 639 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<FluentStack Orientation="Orientation.Vertical">
<FluentStack HorizontalGap="12px" VerticalAlignment="VerticalAlignment.Bottom" Orientation="Orientation.Horizontal">

<FluentSelect Label="Appearance"
Items="@(Enum.GetValues<TextAreaAppearance>())"
@bind-Value="@appearance" />

<FluentSelect Label="Size"
Items="@(Enum.GetValues<TextAreaSize>())"
@bind-Value="@size" />

<FluentCheckbox Label="Spell check" @bind-Value="spellCheck"></FluentCheckbox>
</FluentStack>

<FluentTextArea Appearance="@appearance"
Size="@size"
Label="Sample"
Spellcheck="@spellCheck"
@bind-Value="@value" />
</FluentStack>

<FluentLabel>Value = @value</FluentLabel>

@code
{
string value = "";
TextAreaAppearance appearance = TextAreaAppearance.Outline;
TextAreaSize size = TextAreaSize.Medium;
bool spellCheck = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<FluentStack Orientation="Orientation.Vertical">
<FluentTextArea Appearance="@TextAreaAppearance.Outline"
Placeholder="Updated after 400ms"
@bind-Value="@value"
@bind-Value:after="@(() => Console.WriteLine($"Textarea updated to '{value}'.") )"
Immediate="true"
ImmediateDelay="400" />

<FluentLabel>Value = @value</FluentLabel>
</FluentStack>

@code
{
string value = "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<FluentStack Orientation="Orientation.Vertical">

<FluentSelect Label="Resize modes"
Items="@(Enum.GetValues<TextAreaResize>())"
@bind-Value="@resizeMode" />

<FluentTextArea Resize="@resizeMode"
@bind-Value="@value" />

</FluentStack>

@code
{
TextAreaResize resizeMode = TextAreaResize.None;
string value = "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<FluentStack HorizontalGap="20px">
<FluentTextArea Required="true" Label="Required" Placeholder="Required" @bind-Value="@Value" />
<FluentTextArea Disabled="true" Label="Disabled" Placeholder="Disabled" @bind-Value="@Value" />
<FluentTextArea ReadOnly="true" Label="ReadOnly" Placeholder="ReadOnly" @bind-Value="@Value" />
</FluentStack>

<FluentLabel>Value = @Value</FluentLabel>

@code
{
string Value = "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
title: TextArea
route: /TextArea
---

# TextArea

A **FluentTextArea** component enables a user to enter multiple lines of text.
The component is a wrapper for the <fluentui-textarea /> web component.

## Appearance

The apparent style of a textarea can be changed by setting the `Appearance` and `Size` properties.

You can also add a label to the text input by setting the `Label` property and a placeholder by setting the `Placeholder` property.
The label will be automatically positioned above the text input, and the placeholder will be displayed inside the text input.

We recommend to use a spacing of 24px between text fields and other components.

{{ TextAreaAppearances }}

## Resize

The `Resize` property allows you to specify how the textarea can be resized by the user.

{{ TextAreaResizes }}

## Binding with ImmediateDelay

In some cases, you may want to bind the value of the text input to a property of a model
and update the model immediately after the user types a character. But you may also want to delay the update.

This can be achieved by setting the `Immediate` and the (optional) `ImmediateDelay` properties.

{{ TextAreaImmediate }}

## States

A text input can be in different states, such as `Disabled`, `ReadOnly`, and `Required`.

{{ TextAreaState }}

## Know restrictions

At this time, it's not possible to define the height and width of the component.

> These features are under investigation.
## API FluentTextArea

{{ API Type=FluentTextArea }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
title: Migrating FluentTextArea
route: /Migration/TextArea
hidden: true
---

### New properties

- `AutoResize`, sets the element’s height should be automatically changed based on the content.
- `Resize`, sets the textarea resize mode. New value : `none`.
- `Size`, sets the textarea size. Values available : `small`, `medium`, `large`.

### New values

- The `TextAreaAppareance` contains two new values : `FilledLighterShadow` and `FilledDarkerShadow`.

### Removed properties💥

- `Cols`
- `Rows`
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,14 @@ component changes (flagged with 🔃) or Breaking Changes (flagged with 💥).

{{ INCLUDE MigrationFluentLabel }}

## FluentTextArea

{{ INCLUDE MigrationFluentTextArea }}

## FluentLayout and FluentMainLayout

{{ INCLUDE MigrationFluentLayout }}

## Migrate to v5 with help from GitHub Copilot chat

{{ INCLUDE CopilotInstructionsContent }}
{{ INCLUDE CopilotInstructionsContent }}
1 change: 1 addition & 0 deletions spelling.dic
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ noattribute
nonfile
rrggbb
tabindex
textarea
sourcecode
summarydata
31 changes: 31 additions & 0 deletions src/Core/Components/TextArea/FluentTextArea.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@namespace Microsoft.FluentUI.AspNetCore.Components
@using Microsoft.FluentUI.AspNetCore.Components.Extensions
@inherits FluentInputImmediateBase<string?>

<FluentField InputComponent="@this" ForId="@Id" Class="@ClassValue" Style="@StyleValue">
<fluent-textarea @ref="@Element"
appearance="@Appearance.ToAttributeValue()"
autocomplete="@AutoComplete"
autofocus="@Autofocus"
auto-resize="@AutoResize"
aria-label="@AriaLabel"
disabled="@Disabled"
display-shadow="@DisplayShadow"
id="@Id"
maxlength="@MaxLength"
minlength="@MinLength"
name="@Name"
placeholder="@Placeholder"
readonly="@ReadOnly"
required="@Required"
size="@Size.ToAttributeValue()"
slot="input"
spellcheck="@SpellCheckValue"
resize="@Resize.ToAttributeValue()"
value="@CurrentValueAsString"
@onfocusout="@FocusOutHandlerAsync"
@onchange="@ChangeHandlerAsync"
@oninput="@InputHandlerAsync"
@attributes="AdditionalAttributes">
</fluent-textarea>
</FluentField>
141 changes: 141 additions & 0 deletions src/Core/Components/TextArea/FluentTextArea.razor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;

namespace Microsoft.FluentUI.AspNetCore.Components;

/// <summary>
/// A textarea component that allows users to enter and edit a single line of text.
/// </summary>
public partial class FluentTextArea : FluentInputImmediateBase<string?>, IFluentComponentElementBase
{

/// <summary>
/// Initializes a new instance of the <see cref="FluentTextInput"/> class.
/// </summary>
public FluentTextArea()
{
// Default conditions for the message
MessageCondition = (field) =>
{
field.MessageIcon = FluentStatus.ErrorIcon;
field.Message = Localizer[Localization.LanguageResource.TextInput_RequiredMessage];

return FocusLost &&
(Required ?? false)
&& !(Disabled ?? false)
&& !ReadOnly
&& string.IsNullOrEmpty(CurrentValueAsString);
};
}

/// <inheritdoc cref="IFluentComponentElementBase.Element" />
[Parameter]
public ElementReference Element { get; set; }

/// <summary>
/// Gets or sets the visual appearance.
/// </summary>
[Parameter]
public TextAreaAppearance? Appearance { get; set; }

/// <summary>
/// Gets or sets the short hint displayed in the textarea before the user enters a value.
/// </summary>
[Parameter]
public string? Placeholder { get; set; }

/// <summary>
/// Gets or sets the maximum number of characters allowed in the textarea
/// </summary>
[Parameter]
public int? MaxLength { get; set; }

/// <summary>
/// Gets or sets the minimum number of characters allowed in the textarea
/// </summary>
[Parameter]
public int? MinLength { get; set; }

/// <summary>
/// Specifies whether a form or an textarea field should have autocomplete "on" or "off" or another value.
/// An Id value must be set to use this property.
/// </summary>
[Parameter]
public string? AutoComplete { get; set; }

/// <summary>
/// Whether the element’s height should be automatically changed based on the content.
/// </summary>
[Parameter]
public bool? AutoResize { get; set; }

/// <summary>
/// Gets or sets the size of the textarea. See <see cref="Components.TextAreaSize"/>
/// </summary>
[Parameter]
public TextAreaSize? Size { get; set; }

/// <summary>
/// Gets or sets the how resize the element. See <see cref="Components.TextAreaResize"/>
/// </summary>
[Parameter]
public TextAreaResize? Resize { get; set; }

/// <summary>
/// Gets or sets a value indicating whether spellcheck should be used.
/// </summary>
[Parameter]
public bool? Spellcheck { get; set; }

/// <inheritdoc cref="ComponentBase.OnAfterRenderAsync(bool)" />
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
await JSRuntime.InvokeVoidAsync("Microsoft.FluentUI.Blazor.Utilities.Attributes.observeAttributeChange", Element, "value");
}
}

/// <summary>
/// Parses a string to create the <see cref="Microsoft.AspNetCore.Components.Forms.InputBase{TValue}.Value"/>.
/// </summary>
/// <param name="value">The string value to be parsed.</param>
/// <param name="result">The result to inject into the Value.</param>
/// <param name="validationErrorMessage">If the value could not be parsed, provides a validation error message.</param>
/// <returns>True if the value could be parsed; otherwise false.</returns>
protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out string? result, [NotNullWhen(false)] out string? validationErrorMessage)
{
result = value;
validationErrorMessage = null;
return true;
}

/// <summary>
/// Handler for the OnFocus event.
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
protected virtual Task FocusOutHandlerAsync(FocusEventArgs e)
{
FocusLost = true;
return Task.CompletedTask;
}

private string? DisplayShadow
=> Appearance == TextAreaAppearance.FilledDarkerShadow || Appearance == TextAreaAppearance.FilledLighterShadow
? "true"
: null;

private string? SpellCheckValue
=> Spellcheck.HasValue
? Spellcheck.Value
? "true"
: "false"
: null;
}
43 changes: 43 additions & 0 deletions src/Core/Enums/TextAreaAppearance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// ------------------------------------------------------------------------
// MIT License - Copyright (c) Microsoft Corporation. All rights reserved.
// ------------------------------------------------------------------------

using System.ComponentModel;

namespace Microsoft.FluentUI.AspNetCore.Components;

/// <summary>
/// The visual appearance of the <see cref="TextAreaAppearance" />.
/// </summary>
public enum TextAreaAppearance
{
/// <summary>
/// The default appearance.
/// </summary>
[Description("outline")]
Outline,

/// <summary>
/// The appearance where the borders are filled with a lighter color.
/// </summary>
[Description("filled-lighter")]
FilledLighter,

/// <summary>
/// The appearance where the borders are filled with a darker color.
/// </summary>
[Description("filled-darker")]
FilledDarker,

/// <summary>
/// The appearance where the borders are filled with a lighter color with shadow.
/// </summary>
[Description("filled-lighter")]
FilledLighterShadow,

/// <summary>
/// The appearance where the borders are filled with a darker color with shadow.
/// </summary>
[Description("filled-darker")]
FilledDarkerShadow,
}
Loading

0 comments on commit 85fe71c

Please sign in to comment.