Skip to content
This repository has been archived by the owner on Jan 5, 2024. It is now read-only.

[新增] Skeleton 骨架屏组件 #161

Merged
merged 3 commits into from
Nov 20, 2022
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
1 change: 1 addition & 0 deletions doc/TDesign.Docs.Shared/Layouts/NavMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<TMenuItem Link="components/table">Table 表格</TMenuItem>
<TMenuItem Link="components/tag">Tag 标签</TMenuItem>
<TMenuItem Link="components/progress">Progress 进度条</TMenuItem>
<TMenuItem Link="components/skeleton">Skeleton 骨架屏</TMenuItem>

</TMenuItemGroup>
<TMenuItemGroup Title="消息提醒">
Expand Down
151 changes: 151 additions & 0 deletions doc/TDesign.Docs.Shared/Pages/Components/SkeletonPage.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
@page "/components/skeleton"

<PageHeader Title="Skeleton 骨架屏">
当网络较慢时,在页面真实数据加载之前,给用户展示出页面的大致结构。
</PageHeader>
<HighlightAlert />
<Example Title="基础骨架屏">
<Description>最简单的骨架屏效果。</Description>
<RunContent>
<TSwitch @bind-Value="Loading" />
<br />
<TSkeleton Loading="Loading">
骨架屏组件,是指当网络较慢时,在页面真实数据加载之前,给用户展示出页面的大致结构。 一方面让用户对页面有一定的心理预期,另一方面可以改善长期停留在空白屏给用户带来的枯燥和不适感。它可以为用户提供更好视觉效果和使用体验。
</TSkeleton>
</RunContent>
<CodeContent>
@Code.Create(@"
```html
<TSkeleton Loading=""Loading"">
骨架屏组件,是指当网络较慢时,在页面真实数据加载之前,给用户展示出页面的大致结构。 一方面让用户对页面有一定的心理预期,另一方面可以改善长期停留在空白屏给用户带来的枯燥和不适感。它可以为用户提供更好视觉效果和使用体验。
</TSkeleton>
```
")
</CodeContent>
</Example>

@code {
bool Loading { get; set; }
}
<Example Title="带动画效果的骨架屏">
<Description>提供渐变和闪烁两种动画效果。</Description>
<RunContent>
<TSkeleton Loading Animation="SkeletonAnimation.Gradient">

骨架屏组件,是指当网络较慢时,在页面真实数据加载之前,给用户展示出页面的大致结构。 一方面让用户对页面有一定的心理预期,另一方面可以改善长期停留在空白屏给用户带来的枯燥和不适感。它可以为用户提供更好视觉效果和使用体验。
骨架屏组件,是指当网络较慢时,在页面真实数据加载之前,给用户展示出页面的大致结构。 一方面让用户对页面有一定的心理预期,另一方面可以改善长期停留在空白屏给用户带来的枯燥和不适感。它可以为用户提供更好视觉效果和使用体验。
</TSkeleton>
<br/>
<TSkeleton Loading Animation="SkeletonAnimation.Flashed">

骨架屏组件,是指当网络较慢时,在页面真实数据加载之前,给用户展示出页面的大致结构。 一方面让用户对页面有一定的心理预期,另一方面可以改善长期停留在空白屏给用户带来的枯燥和不适感。它可以为用户提供更好视觉效果和使用体验。
骨架屏组件,是指当网络较慢时,在页面真实数据加载之前,给用户展示出页面的大致结构。 一方面让用户对页面有一定的心理预期,另一方面可以改善长期停留在空白屏给用户带来的枯燥和不适感。它可以为用户提供更好视觉效果和使用体验。
</TSkeleton>

</RunContent>
<CodeContent>
@Code.Create(@"
```html
<TSkeleton Animation=""SkeletonAnimation.Gradient""/>

<TSkeleton Animation=""SkeletonAnimation.Flashed""/>
```
")
</CodeContent>
</Example>
<Example Title="不同主题的骨架屏">
<Description>可以通过 <code>Theme</code> 参数快速定义不同主题风格的骨架屏。</Description>
<RunContent>
<p>文本</p>
<TSkeleton Loading Theme="SkeletonTheme.Text"/>
<br/>
<p>段落</p>
<TSkeleton Loading Theme="SkeletonTheme.Paragraph" />
<br />
<p>头像</p>
<TSkeleton Loading Theme="SkeletonTheme.Avatar"/>
<br/>
<p>头像描述</p>
<TSkeleton Loading Theme="SkeletonTheme.AvatarText"/>
<br/>
<p>选项卡</p>
<TSkeleton Loading Theme="SkeletonTheme.Tab"/>
<br/>
<p>文章</p>
<TSkeleton Loading Theme="SkeletonTheme.Article"/>
</RunContent>
<CodeContent>
@Code.Create(@"
```html
<p>文本</p>
<TSkeleton Loading Theme=""SkeletonTheme.Text""/>

<p>段落</p>
<TSkeleton Loading Theme=""SkeletonTheme.Paragraph"" />

<p>头像</p>
<TSkeleton Loading Theme=""SkeletonTheme.Avatar""/>

<p>头像描述</p>
<TSkeleton Loading Theme=""SkeletonTheme.AvatarText""/>

<p>选项卡</p>
<TSkeleton Loading Theme=""SkeletonTheme.Tab""/>

<p>文章</p>
<TSkeleton Loading Theme=""SkeletonTheme.Article""/>
```
")
</CodeContent>
</Example>
<Example Title="自定义骨架屏内容">
<Description>显示地设置 <code>LoadingContent</code> 自己定义骨架屏的布局,并显示地设置 <code>ChildContent</code> 加载完后的内容。</Description>
<RunContent>
<TSwitch @bind-Value="Loading"/>
<br />
<TSkeleton Loading="Loading">
<LoadingContent>
<TSkeletonRow>
<TSkeletonColumn/>
<TSkeletonColumn Type="SkeletonColumnType.Circle"/>
</TSkeletonRow>
<TSkeletonRow>
<TSkeletonColumn />
<TSkeletonColumn Type="SkeletonColumnType.Circle" />
<TSkeletonColumn />
</TSkeletonRow>
<TSkeletonRow>
<TSkeletonColumn Type="SkeletonColumnType.Rectangle" style="width:100%"/>
</TSkeletonRow>
</LoadingContent>
<ChildContent>
这里是要显示的内容。
</ChildContent>
</TSkeleton>
</RunContent>
<CodeContent>
@Code.Create(@"
```html
<TSkeleton Loading=""Loading"">
<LoadingContent>
<TSkeletonRow>
<TSkeletonColumn/>
<TSkeletonColumn Type=""SkeletonColumnType.Circle""/>
</TSkeletonRow>
<TSkeletonRow>
<TSkeletonColumn />
<TSkeletonColumn Type=""SkeletonColumnType.Circle"" />
<TSkeletonColumn />
</TSkeletonRow>
<TSkeletonRow>
<TSkeletonColumn Type=""SkeletonColumnType.Rectangle"" style=""width:100%""/>
</TSkeletonRow>
</LoadingContent>
<ChildContent>
这里是要显示的内容。
</ChildContent>
</TSkeleton>
```
")
</CodeContent>
</Example>
3 changes: 3 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
## 🌈 在线示例
[https://achievedowner.github.io/TDesignBlazor/](https://achievedowner.github.io/TDesignBlazor/)

> 由于网络问题,可能导致以上链接不可用,请下载源码并运行 `TDesign.Docs.ServerSide` 或 `TDesign.Docs.WebAssembly` 项目。

## 🖥 支持环境

- ![.NET 6](https://img.shields.io/badge/.NET-v6.0-green)
Expand Down Expand Up @@ -83,6 +85,7 @@
## :pencil: 参与贡献
* 如果你有意向参与贡献,请先阅读[贡献指南](./Contributing.md)
* 有任何问题,欢迎通过 [Github issues](https://github.com/AchievedOwner/TDesignBlazor/issues) 反馈
* 提供目前进度的实时[看板](https://github.com/users/AchievedOwner/projects/4)

**我们的贡献者**
非常感谢每一个项目贡献者的辛勤付出
Expand Down
219 changes: 219 additions & 0 deletions src/TDesign/Components/TSkeleton.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
using Microsoft.AspNetCore.Components.Rendering;

namespace TDesign;
/// <summary>
/// 骨架屏。当网络较慢时,在页面真实数据加载之前,给用户展示出页面的大致结构。
/// </summary>
[ParentComponent]
[CssClass("t-skeleton")]
public class TSkeleton : BlazorComponentBase, IHasChildContent
{
/// <summary>
/// 设置是否显示骨架屏。
/// </summary>
[Parameter][EditorRequired] public bool Loading { get; set; }
/// <summary>
/// 当 <see cref="Loading"/> 是 <c>false</c> 时显示的内容。
/// </summary>
[Parameter] public RenderFragment? ChildContent { get; set; }
/// <summary>
/// 当 <see cref="Loading"/> 是 <c>true</c> 时显示的内容。
/// </summary>
[Parameter] public RenderFragment? LoadingContent { get; set; }

/// <summary>
/// 设置动画效果。
/// </summary>
[Parameter] public SkeletonAnimation? Animation { get; set; }


/// <summary>
/// 设置骨架屏的模式。可以快速设置骨架屏显示的模式。
/// </summary>
[Parameter] public SkeletonTheme? Theme { get; set; } = SkeletonTheme.Paragraph;

/// <inheritdoc/>
protected override void OnParametersSet()
{
base.OnParametersSet();

if (Theme is not null && LoadingContent is null)
{
LoadingContent = GetThemeContent();
}
}

/// <inheritdoc/>
protected override void AddContent(RenderTreeBuilder builder, int sequence)
{
if (Loading)
{
builder.AddContent(sequence, LoadingContent);
}
else
{
base.AddContent(builder, sequence);
}
}

/// <summary>
/// 获取主题对应的骨架屏。
/// </summary>
RenderFragment GetThemeContent()
=> Theme switch
{
SkeletonTheme.Text => builder => builder.CreateComponent<TSkeletonRow>(0, col => col.CreateComponent<TSkeletonColumn>(0)),
SkeletonTheme.Paragraph => builder =>
{
builder.CreateComponent<TSkeletonRow>(0, col => col.CreateComponent<TSkeletonColumn>(0));
builder.CreateComponent<TSkeletonRow>(1, col => col.CreateComponent<TSkeletonColumn>(0));
builder.CreateComponent<TSkeletonRow>(2, col => col.CreateComponent<TSkeletonColumn>(0));
}
,
SkeletonTheme.Avatar => builder => builder.CreateComponent<TSkeletonRow>(0, col => col.CreateComponent<TSkeletonColumn>(0, attributes: new { Type = SkeletonColumnType.Circle, style = "height:56px;width:56px" })),
SkeletonTheme.AvatarText => builder => builder.CreateComponent<TSkeletonRow>(0, col =>
{
col.CreateComponent<TSkeletonColumn>(0, attributes: new { Type = SkeletonColumnType.Circle });
col.CreateComponent<TSkeletonColumn>(1, attributes: new { style = "height:32px" });
}),
SkeletonTheme.Tab => builder =>
{
builder.CreateComponent<TSkeletonRow>(0, col => col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:30px" }));
builder.CreateComponent<TSkeletonRow>(0, col => col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:200px" }));
}
,
SkeletonTheme.Article => builder =>
{
builder.CreateComponent<TSkeletonRow>(0, col => col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "width:100%;height:30px" }));
builder.CreateComponent<TSkeletonRow>(0, col => col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "width:100%;height:200px" }));

builder.CreateComponent<TSkeletonRow>(0, col =>
{
col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:30px" });
col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:30px" });
col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:30px" });
});

builder.CreateComponent<TSkeletonRow>(0, col =>
{
col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:30px" });
col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:30px" });
});

builder.CreateComponent<TSkeletonRow>(0, col =>
{
col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:30px" });
col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:30px" });
});

builder.CreateComponent<TSkeletonRow>(0, col =>
{
col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:30px" });
col.CreateComponent<TSkeletonColumn>(0, attributes: new { style = "height:30px" });
});
}
,
_ => builder => { }
};
}
/// <summary>
/// 表示骨架屏中的行。
/// </summary>
[ChildComponent(typeof(TSkeleton))]
[ParentComponent]
[CssClass("t-skeleton__row")]
public class TSkeletonRow : BlazorComponentBase, IHasChildContent
{

/// <inheritdoc/>
[Parameter] public RenderFragment? ChildContent { get; set; }

}

/// <summary>
/// 表示骨架屏中的行。
/// </summary>
[ChildComponent(typeof(TSkeleton))]
[ChildComponent(typeof(TSkeletonRow))]
[CssClass("t-skeleton__col")]
public class TSkeletonColumn : BlazorComponentBase
{

/// <summary>
/// 获取 <see cref="TSkeleton"/> 组件。
/// </summary>
[CascadingParameter] public TSkeleton? CascadingSkeleton { get; set; }

/// <summary>
/// 设置列的类型。
/// </summary>
[Parameter][CssClass("t-skeleton--type-")] public SkeletonColumnType Type { get; set; } = SkeletonColumnType.Text;

/// <inheritdoc/>
protected override void BuildCssClass(ICssClassBuilder builder)
{
builder.Append($"t-skeleton--animation-{CascadingSkeleton?.Animation?.GetCssClass()}", CascadingSkeleton?.Animation is not null);
}
}
/// <summary>
/// 骨架屏列的类型。
/// </summary>
public enum SkeletonColumnType
{
/// <summary>
/// 长方形。
/// </summary>
[CssClass("rect")] Rectangle,
/// <summary>
/// 圆形。
/// </summary>
Circle,
/// <summary>
/// 文本。
/// </summary>
Text
}
/// <summary>
/// 骨架屏的动画效果。
/// </summary>
public enum SkeletonAnimation
{
/// <summary>
/// 渐变效果。
/// </summary>
Gradient,
/// <summary>
/// 闪烁效果。
/// </summary>
Flashed
}
/// <summary>
/// 骨架屏主题模式。
/// </summary>
public enum SkeletonTheme
{
/// <summary>
/// 文本。
/// </summary>
Text,
/// <summary>
/// 段落。
/// </summary>
Paragraph,
/// <summary>
/// 头像。
/// </summary>
Avatar,
/// <summary>
/// 头像文本。
/// </summary>
AvatarText,
/// <summary>
/// 选项卡。
/// </summary>
Tab,
/// <summary>
/// 长篇文章。
/// </summary>
Article
}