Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dev-v5] Add FluentTextArea component #3341

Merged
Merged
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
@@ -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
Loading