Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
feat: added LumexUser initial styles
  • Loading branch information
Denny09310 committed Sep 15, 2025
commit f11693d67e5f659c4b79b8a95a6871df50120785
19 changes: 19 additions & 0 deletions src/LumexUI/Components/User/LumexUser.razor
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
@namespace LumexUI
@inherits LumexComponentBase

@using S = UserSlots;

<LumexComponent As="@As"
Class="@GetStyles( nameof(S.Base) )"
Style="@RootStyle"
data-slot="base"
data-focus=""
data-focus-visible=""
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I see on the HeroUI docs that this two attributes are set, I don't know if they can be useful to this library

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

They are setting interaction states programmatically. We don't do this.

These should be covered by Styles.Utils.FocusVisible in the styles. Search for the FocusVisible usages.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Perfect, I was already using that, I was only wandering if this was the right path. So the IsFocusable property, can be used to toggle this behavior or should I remove it directly?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh, I haven't noticed this prop before. According to HeroUI isFocusable means: "Whether the user is focusable. This is useful when using Dropdown or similar components."

I think it's better to keep it to toggle this behaviour.

@attributes="AdditionalAttributes">
<LumexAvatar />
<div class="@GetStyles( nameof( S.Wrapper ) )">
<span class="@GetStyles( nameof( S.Name ) )">
@Name
</span>
<span class="@GetStyles( nameof( S.Description ))">
@Description
</span>
</div>
</LumexComponent>
48 changes: 47 additions & 1 deletion src/LumexUI/Components/User/LumexUser.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,58 @@
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

using System.Diagnostics.CodeAnalysis;

using LumexUI.Common;
using LumexUI.Utilities;

using Microsoft.AspNetCore.Components;

namespace LumexUI;

/// <summary>
/// A component that represents user information, such as an avatar, name, and email.
/// </summary>
public partial class LumexUser : LumexComponentBase
public partial class LumexUser : LumexComponentBase, ISlotComponent<UserSlots>
{
[Parameter] public string? Name { get; set; }

[Parameter] public string? Description { get; set; }

[Parameter] public bool IsFocusable { get; set; }

/// <summary>
/// Gets or sets the CSS class names for the user slots.
/// </summary>
[Parameter] public UserSlots? Classes { get; set; }
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Here is a missing property for the 'Avatar Props', but I'm unsure how to handle the class. Should I use a dedicated class? Or there are already some examples in the codebase?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I need to think about that...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is actually sad :D I see a couple of options:

  • Create the AvatarProps class and pass it as parameter. Flaw: Avatar props duplicates + need to always keep them synced;
  • Create the AvatarProps parameter of type Dictionary<string, object> and pass it via attribute splatting (@attributes=...) to LumexAvatar. Flaw: we lose strict typing and intelliSense;
  • Create the AvatarContent parameter of type RenderFragment to allow consumers define avatars themselves.

I think that the last option is the best here.

I have plans to move to the following structure in the future:

<Alert>
  <Alert.Icon />
  <Alert.Content>
    <Alert.Title>Success</Alert.Title>
    <Alert.Description>Your changes have been saved.</Alert.Description>
  </Alert.Content>
  <Alert.Close />
</Alert>

, where each slot is a separate component that can be styled and arranged independently.
This is something shadcn has been doing for a while alredy, and HeroUI v3 moved to the same concept. With this approach we will gain maximum flexibility as well.

Copy link
Copy Markdown
Contributor

@desmondinho desmondinho Sep 15, 2025

Choose a reason for hiding this comment

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

Actually, I just realized that LumexChip already does that (AvatarContent approach) :D Can't remember own library solutions haha

Copy link
Copy Markdown
Contributor

@desmondinho desmondinho Sep 15, 2025

Choose a reason for hiding this comment

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

But don't wrap AvatarContent with the condition as I did -- it does not make sense.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have plans to move to the following structure in the future, where each slot is a separate component that can be styled and arranged independently.

Oh fantastic! I love this approach 🚀

Ok, so for now I add this 'AvatarContent' as RenderFragment

Copy link
Copy Markdown
Contributor Author

@Denny09310 Denny09310 Sep 15, 2025

Choose a reason for hiding this comment

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

Is it cool for you, if after this component is reviewed I make a branch to try and convert all the components with old styling method to the new? Just to have the project prepared for the architectural shift. I'd like to help, but I don't want to be too overwhelming 😁

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it cool for you, if after this component is reviewed I make a branch to try and convert all the components with old styling method to the new? Just to have the project prepared for the architectural shift. I'd like to help, but I don't want to be too overwhelming 😁

Yes, I’m totally fine with it! I really appreciate your input. I will do my best to make the process as smooth as possible for you.

I have to let you know that the utility for the new styling method is far from ideal, so we’ll most likely need to update it a bit.


private Dictionary<string, ComponentSlot> _slots = [];

/// <inheritdoc/>
protected override void OnParametersSet()
{
var user = Styles.User.Style( TwMerge );
_slots = user( new()
{

} );
}

[ExcludeFromCodeCoverage]
private string? GetStyles( string slot )
{
if( !_slots.TryGetValue( slot, out var styles ) )
{
throw new NotImplementedException();
}

return slot switch
{
nameof( UserSlots.Base ) => styles( Classes?.Base, Class ),
nameof( UserSlots.Wrapper ) => styles( Classes?.Wrapper ),
nameof( UserSlots.Name ) => styles( Classes?.Name ),
nameof( UserSlots.Description ) => styles( Classes?.Description ),
_ => throw new NotImplementedException()
};
}
}
28 changes: 28 additions & 0 deletions src/LumexUI/Components/User/UserSlots.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) LumexUI 2024
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

using LumexUI.Common;

namespace LumexUI;

/// <summary>
/// Represents the set of customizable slots for the <see cref="LumexUser"/> component.
/// </summary>
public class UserSlots : SlotBase
{
/// <summary>
/// Gets or sets the CSS class for the wrapper slot.
/// </summary>
public string? Wrapper { get; set; }

/// <summary>
/// Gets or sets the CSS class for the name slot.
/// </summary>
public string? Name { get; set; }

/// <summary>
/// Gets or sets the CSS class for the description slot.
/// </summary>
public string? Description { get; set; }
}
47 changes: 47 additions & 0 deletions src/LumexUI/Styles/User.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) LumexUI 2024
// LumexUI licenses this file to you under the MIT license
// See the license here https://github.com/LumexUI/lumexui/blob/main/LICENSE

using System.Diagnostics.CodeAnalysis;

using LumexUI.Components.User;
using LumexUI.Utilities;

using TailwindMerge;

namespace LumexUI.Styles;

[ExcludeFromCodeCoverage]
internal static class User
{
private static ComponentVariant? _variant;

public static ComponentVariant Style( TwMerge twMerge )
{
var twVariants = new TwVariants( twMerge );

return _variant ?? twVariants.Create( new VariantConfig()
{
Base = new ElementClass()
.Add( "inline-flex" )
.Add( "items-center" )
.Add( "justify-center" )
.Add( "gap-2" )
.Add( "rounded-small" )
.Add( "outline-solid" )
.Add( "outline-transparent" )
.Add( "outline-solid" )
.Add( "outline-transparent" )
.ToString(),

Slots = new SlotCollection
{
[nameof(UserSlots.Wrapper)] = "inline-flex flex-col items-start",

[nameof(UserSlots.Name)] = "text-small text-inherit",

[nameof(UserSlots.Description)] = "text-tiny text-foreground-400",
}
} );
}
}