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

feat(components): introduce Dropdown component #168

Merged
merged 31 commits into from
Feb 19, 2025
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
178360e
initial commit
desmondinho Feb 1, 2025
44446a0
add context
desmondinho Feb 1, 2025
0b077d1
update navigation
desmondinho Feb 1, 2025
006630c
add baseline for the dropdown page
desmondinho Feb 1, 2025
dc3a2e4
build: add LumexUI.Variants
desmondinho Feb 3, 2025
66de3d8
refactor(popover): deprecate `LumexPopoverTrigger`
desmondinho Feb 5, 2025
1838b50
feat(popover): add and expose `TriggerAsync` as a public API from the…
desmondinho Feb 5, 2025
c9ac6d5
remove `LumexDropdownTrigger`
desmondinho Feb 5, 2025
10b8b61
improve usage of the popover within the dropdown
desmondinho Feb 5, 2025
5737d8b
add TwVariant to the DI
desmondinho Feb 10, 2025
0b4f188
refactor(popover): switch styles to TwVariant
desmondinho Feb 11, 2025
a9ddbb2
add Menu and MenuItem components
desmondinho Feb 12, 2025
65811e5
add `Color`, `Variant` and `Disabled` parameters for the Menu and Men…
desmondinho Feb 17, 2025
e5bf7cf
set default popover placement to bottom
desmondinho Feb 18, 2025
31ae613
set default popover shadow to medium
desmondinho Feb 18, 2025
91b9707
adjust semantic light surface1 colors
desmondinho Feb 18, 2025
e643076
add XML summaries
desmondinho Feb 18, 2025
305ddae
docs: add Dropdown page
desmondinho Feb 18, 2025
4a32024
make `Id` parameter of the `MenuItem` component of type `string`
desmondinho Feb 19, 2025
18db493
nits
desmondinho Feb 19, 2025
0b3e954
test: add TwVariant service everywhere
desmondinho Feb 19, 2025
faa10cd
test: replace FluentAssertions with AwesomeAssertions
desmondinho Feb 19, 2025
5d35a94
test: add dropdown tests
desmondinho Feb 19, 2025
fe596e7
docs(popover): update examples to use external trigger
desmondinho Feb 19, 2025
c55d321
chore(menu-item): transition shadows on hover
desmondinho Feb 19, 2025
ebf7072
docs(dropdown): cleanup examples
desmondinho Feb 19, 2025
2d989aa
docs(dropdown): add a callout noting to set unique IDs for each item …
desmondinho Feb 19, 2025
43e5c28
chore: update summary for the `MenuVariant`
desmondinho Feb 19, 2025
e5bf5af
nits
desmondinho Feb 19, 2025
420d921
nits
desmondinho Feb 19, 2025
80945f2
nits
desmondinho Feb 19, 2025
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
7 changes: 7 additions & 0 deletions LumexUI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LumexUI.Docs.Client", "docs
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LumexUI.Motion", "src\LumexUI.Motion\LumexUI.Motion.csproj", "{52657DBF-5D90-4829-9AE9-713E0C39E5CA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LumexUI.Variants", "src\LumexUI.Variants\LumexUI.Variants.csproj", "{BFEFB791-8251-48C3-9240-A4E5D5C2C2C2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -64,6 +66,10 @@ Global
{52657DBF-5D90-4829-9AE9-713E0C39E5CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{52657DBF-5D90-4829-9AE9-713E0C39E5CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{52657DBF-5D90-4829-9AE9-713E0C39E5CA}.Release|Any CPU.Build.0 = Release|Any CPU
{BFEFB791-8251-48C3-9240-A4E5D5C2C2C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BFEFB791-8251-48C3-9240-A4E5D5C2C2C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BFEFB791-8251-48C3-9240-A4E5D5C2C2C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BFEFB791-8251-48C3-9240-A4E5D5C2C2C2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -76,6 +82,7 @@ Global
{2A341376-2409-422C-A7A9-1AE6E86F91D9} = {3F4DE3FA-2636-440C-ACCC-137BAD95BAC4}
{6CE81AB3-399F-44CB-9DBE-9A8D62B07C8A} = {3F4DE3FA-2636-440C-ACCC-137BAD95BAC4}
{52657DBF-5D90-4829-9AE9-713E0C39E5CA} = {5DBC44B8-5B92-4504-8B8F-91A1672251E6}
{BFEFB791-8251-48C3-9240-A4E5D5C2C2C2} = {5DBC44B8-5B92-4504-8B8F-91A1672251E6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1B8CD55B-BEF0-42D1-936A-BC1FB5D02CE2}
Expand Down
166 changes: 86 additions & 80 deletions docs/LumexUI.Docs.Client/Common/Navigation/NavigationStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,90 +6,96 @@ namespace LumexUI.Docs.Client.Common;

public class NavigationStore
{
private static Navigation? _navigation;
private static Navigation? _navigation;

private static NavigationCategory GettingStartedCategory =>
new NavigationCategory( "Getting Started", Icons.Rounded.AutoStories )
.Add( new( "Overview" ) )
.Add( new( "Installation" ) );
private static NavigationCategory GettingStartedCategory =>
new NavigationCategory( "Getting Started", Icons.Rounded.AutoStories )
.Add( new( "Overview" ) )
.Add( new( "Installation" ) );

private static NavigationCategory CustomizationCategory =>
new NavigationCategory( "Customization", Icons.Rounded.DesignServices )
.Add( new( "Theme" ) )
.Add( new( "Layout" ) )
.Add( new( "Colors" ) )
.Add( new( "Customize Theme" ) );
private static NavigationCategory CustomizationCategory =>
new NavigationCategory( "Customization", Icons.Rounded.DesignServices )
.Add( new( "Theme" ) )
.Add( new( "Layout" ) )
.Add( new( "Colors" ) )
.Add( new( "Customize Theme" ) );

private static NavigationCategory ComponentsCategory =>
new NavigationCategory( "Components", Icons.Rounded.Joystick )
.Add( new( nameof( LumexAccordion ) ) )
.Add( new( nameof( LumexButton ) ) )
.Add( new( nameof( LumexCard ) ) )
.Add( new( nameof( LumexCheckbox ) ) )
.Add( new( nameof( LumexCheckboxGroup ) ) )
.Add( new( nameof( LumexCollapse ) ) )
.Add( new( nameof( LumexDataGrid<T> ) ) )
.Add( new( nameof( LumexDivider ) ) )
.Add( new( nameof( LumexIcon ) ) )
.Add( new( nameof( LumexLink ) ) )
.Add( new( nameof( LumexListbox<T> ), ComponentStatus.New ) )
.Add( new( nameof( LumexNavbar ) ) )
.Add( new( nameof( LumexNumbox<T> ) ) )
.Add( new( nameof( LumexPopover ) ) )
.Add( new( nameof( LumexRadioGroup<T> ), ComponentStatus.New ) )
.Add( new( nameof( LumexSelect<T> ), ComponentStatus.New ) )
.Add( new( nameof( LumexSwitch ) ) )
.Add( new( nameof( LumexTabs ), ComponentStatus.New ) )
.Add( new( nameof( LumexTextbox ) ) );
private static NavigationCategory ComponentsCategory =>
new NavigationCategory( "Components", Icons.Rounded.Joystick )
.Add( new( nameof( LumexAccordion ) ) )
.Add( new( nameof( LumexButton ) ) )
.Add( new( nameof( LumexCard ) ) )
.Add( new( nameof( LumexCheckbox ) ) )
.Add( new( nameof( LumexCheckboxGroup ) ) )
.Add( new( nameof( LumexCollapse ) ) )
.Add( new( nameof( LumexDataGrid<T> ) ) )
.Add( new( nameof( LumexDivider ) ) )
.Add( new( nameof( LumexDropdown ), ComponentStatus.New ) )
.Add( new( nameof( LumexIcon ) ) )
.Add( new( nameof( LumexLink ) ) )
.Add( new( nameof( LumexListbox<T> ) ) )
.Add( new( nameof( LumexNavbar ) ) )
.Add( new( nameof( LumexNumbox<T> ) ) )
.Add( new( nameof( LumexPopover ) ) )
.Add( new( nameof( LumexRadioGroup<T> ) ) )
.Add( new( nameof( LumexSelect<T> ) ) )
.Add( new( nameof( LumexSwitch ) ) )
.Add( new( nameof( LumexTabs ), ComponentStatus.New ) )
.Add( new( nameof( LumexTextbox ) ) );

private static NavigationCategory ComponentsApiCategory =>
new NavigationCategory( "Components API", Icons.Rounded.Manufacturing )
.Add( new( nameof( LumexAccordion ) ) )
.Add( new( nameof( LumexAccordionItem ) ) )
//.Add( nameof( LumexBooleanInputBase ) )
.Add( new( nameof( LumexButton ) ) )
.Add( new( nameof( LumexCard ) ) )
.Add( new( nameof( LumexCardBody ) ) )
.Add( new( nameof( LumexCardFooter ) ) )
.Add( new( nameof( LumexCardHeader ) ) )
.Add( new( nameof( LumexCheckbox ) ) )
.Add( new( nameof( LumexCheckboxGroup ) ) )
.Add( new( nameof( LumexCollapse ) ) )
.Add( new( nameof( LumexComponent ) ) )
//.Add( nameof( LumexComponentBase ) )
//.Add( nameof( LumexDebouncedInputBase<T> ) )
.Add( new( nameof( LumexDivider ) ) )
//.Add( nameof( LumexInputBase<T> ) )
//.Add( nameof( LumexInputFieldBase<T> ) )
.Add( new( nameof( LumexIcon ) ) )
.Add( new( nameof( LumexLink ) ) )
.Add( new( nameof( LumexListbox<T> ) ) )
.Add( new( nameof( LumexListboxItem<T> ) ) )
.Add( new( nameof( LumexNavbar ) ) )
.Add( new( nameof( LumexNavbarBrand ) ) )
.Add( new( nameof( LumexNavbarContent ) ) )
.Add( new( nameof( LumexNavbarItem ) ) )
.Add( new( nameof( LumexNavbarMenu ) ) )
.Add( new( nameof( LumexNavbarMenuItem ) ) )
.Add( new( nameof( LumexNavbarMenuToggle ) ) )
.Add( new( nameof( LumexNumbox<T> ) ) )
.Add( new( nameof( LumexPopover ) ) )
.Add( new( nameof( LumexPopoverContent ) ) )
.Add( new( nameof( LumexPopoverTrigger ) ) )
.Add( new( nameof( LumexSelect<T> ) ) )
.Add( new( nameof( LumexSelectItem<T> ) ) )
.Add( new( nameof( LumexSwitch ) ) )
.Add( new( nameof( LumexTextbox ) ) )
.Add( new( nameof( LumexThemeProvider ) ) );
private static NavigationCategory ComponentsApiCategory =>
new NavigationCategory( "Components API", Icons.Rounded.Manufacturing )
.Add( new( nameof( LumexAccordion ) ) )
.Add( new( nameof( LumexAccordionItem ) ) )
//.Add( nameof( LumexBooleanInputBase ) )
.Add( new( nameof( LumexButton ) ) )
.Add( new( nameof( LumexCard ) ) )
.Add( new( nameof( LumexCardBody ) ) )
.Add( new( nameof( LumexCardFooter ) ) )
.Add( new( nameof( LumexCardHeader ) ) )
.Add( new( nameof( LumexCheckbox ) ) )
.Add( new( nameof( LumexCheckboxGroup ) ) )
.Add( new( nameof( LumexCollapse ) ) )
.Add( new( nameof( LumexComponent ) ) )
//.Add( nameof( LumexComponentBase ) )
//.Add( nameof( LumexDebouncedInputBase<T> ) )
.Add( new( nameof( LumexDivider ) ) )
.Add( new( nameof( LumexDropdown ) ) )
.Add( new( nameof( LumexDropdownItem ) ) )
.Add( new( nameof( LumexDropdownMenu ) ) )
//.Add( nameof( LumexInputBase<T> ) )
//.Add( nameof( LumexInputFieldBase<T> ) )
.Add( new( nameof( LumexIcon ) ) )
.Add( new( nameof( LumexLink ) ) )
.Add( new( nameof( LumexListbox<T> ) ) )
.Add( new( nameof( LumexListboxItem<T> ) ) )
.Add( new( nameof( LumexNavbar ) ) )
.Add( new( nameof( LumexNavbarBrand ) ) )
.Add( new( nameof( LumexNavbarContent ) ) )
.Add( new( nameof( LumexNavbarItem ) ) )
.Add( new( nameof( LumexNavbarMenu ) ) )
.Add( new( nameof( LumexNavbarMenuItem ) ) )
.Add( new( nameof( LumexNavbarMenuToggle ) ) )
.Add( new( nameof( LumexNumbox<T> ) ) )
.Add( new( nameof( LumexPopover ) ) )
.Add( new( nameof( LumexPopoverContent ) ) )
.Add( new( nameof( LumexPopoverTrigger ) ) )
.Add( new( nameof( LumexSelect<T> ) ) )
.Add( new( nameof( LumexSelectItem<T> ) ) )
.Add( new( nameof( LumexSwitch ) ) )
.Add( new( nameof( LumexTab ) ) )
.Add( new( nameof( LumexTabs ) ) )
.Add( new( nameof( LumexTextbox ) ) )
.Add( new( nameof( LumexThemeProvider ) ) );

public static Navigation GetNavigation()
{
_navigation ??= new Navigation()
.Add( GettingStartedCategory )
.Add( CustomizationCategory )
.Add( ComponentsCategory )
.Add( ComponentsApiCategory );
public static Navigation GetNavigation()
{
_navigation ??= new Navigation()
.Add( GettingStartedCategory )
.Add( CustomizationCategory )
.Add( ComponentsCategory )
.Add( ComponentsApiCategory );

return _navigation;
}
return _navigation;
}
}
177 changes: 177 additions & 0 deletions docs/LumexUI.Docs.Client/Pages/Components/Dropdown/Dropdown.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
@page "/docs/components/dropdown"
@layout DocsContentLayout

@using LumexUI.Docs.Client.Pages.Components.Dropdown.PreviewCodes

<DocsCompositionSection Components="@_compositionComponents" />

<DocsSection Title="Usage">
<p>
The dropdown provides a menu that expands when triggered,
allowing users to select an action from a list.
</p>
<Usage />

<DocsSection Title="Disabled">
<p>
The dropdown items can be disabled to prevent user interaction.
A disabled dropdown item is faded and does not respond to user clicks.
</p>
<p>
You can achieve this by using the <Code>Disabled</Code> parameter on a <Code>DropdownItem</Code>.
</p>
<Disabled />

<p>
Alternatively, you can achieve this by using the
<Code>DisabledItems</Code> parameter on a <Code>DropdownMenu</Code>.
</p>
<DisabledItems />
<Callout Variant="@CalloutVariant.Warning">
It's important to set a unique <Code>Id</Code> for each item,
otherwise the disabled items will not work.
</Callout>
</DocsSection>

<DocsSection Title="Colors & Variants">
<p>
Customize the dropdown with different visual styles
and color themes to match your application’s design.
</p>
<ColorsVariants />
</DocsSection>

<DocsSection Title="Start & End Content">
<p>
Add custom content, such as icons or additional information,
to the start or end of each item in the dropdown.
</p>
<StartEndContent />
</DocsSection>

<DocsSection Title="Description">
<p>
Add a description to individual dropdown item
to provide additional context or details about the actions.
</p>
<Description />
</DocsSection>

<DocsSection Title="Empty Content">
<p>
Define custom content to display when the dropdown has no items,
providing a better user experience.
</p>
<EmptyContent />
</DocsSection>
</DocsSection>

<DocsSection Title="Custom Styles">
<p>
This component suppots named slots (represented as <code>data-*</code> attributes) that
allow you to apply custom CSS to specific parts of the component.
</p>
@foreach( var (componentName, slots) in _slots )
{
<h4>@componentName</h4>
<ul>
@foreach( var slot in slots )
{
<li>
<strong class="text-orange-500">@slot.Name:</strong> @slot.Description
</li>
}
</ul>
}
<p>
You can customize the component(s) by passing
any Tailwind CSS classes to the following component parameters:
</p>

<div>
<h4 class="font-semibold">Dropdown Menu</h4>
<ul>
<li><Code>Class</Code>: The CSS class names to style the wrapper.</li>
<li><Code>Classes</Code>: The CSS class names to style the slots.</li>
<li><Code>ItemClasses</Code>: The CSS class names to style the items slots.</li>
</ul>

<h4 class="font-semibold">Dropdown Item</h4>
<ul>
<li><Code>Class</Code>: The CSS class names to style the wrapper.</li>
<li><Code>Classes</Code>: The CSS class names to style the slots.</li>
</ul>
</div>
<CustomStyles />
</DocsSection>

@* <DocsSlotsSection Slots="@_slots">
<div>
<h4 class="font-semibold">Dropdown</h4>
<ul>
<li><Code>Class</Code></li>
<li><Code>Classes</Code></li>
</ul>
</div>
</DocsSlotsSection> *@

<DocsApiSection Components="@_apiComponents" />

@code {
[CascadingParameter] private DocsContentLayout Layout { get; set; } = default!;

private readonly CompositionComponent[] _compositionComponents = new CompositionComponent[]
{
new(nameof(LumexDropdown), "A component that represents a dropdown, extending Popover."),
new(nameof(LumexDropdownMenu), "A component that represents a dropdown menu."),
new(nameof(LumexDropdownItem), "A component that represents a dropdown item.")
};

private readonly Heading[] _headings = new Heading[]
{
new("Usage", [
new("Disabled"),
new("Colors & Variants"),
new("Start & End Content"),
new("Description"),
new("Empty Content"),
]),
new("Custom Styles"),
new("API")
};

private readonly Dictionary<string, Slot[]> _slots = new()
{
[nameof( LumexDropdownMenu )] = [
new(nameof(DropdownMenuSlots.Base), "The main container for the entire dropdown menu."),
new(nameof(DropdownMenuSlots.List), "The wrapper for the dropdown items, containing all dropdown items."),
new(nameof(DropdownMenuSlots.EmptyContent), "The area displayed when the dropdown is empty."),
],
[nameof( LumexDropdownItem )] = [
new(nameof(DropdownItemSlots.Base), "The main container for the dropdown item."),
new(nameof(DropdownItemSlots.Wrapper), "The wrapper for the title, description, and icons."),
new(nameof(DropdownItemSlots.Title), "The title of the dropdown item."),
new(nameof(DropdownItemSlots.Description), "The description of the dropdown item."),
]
};

private readonly string[] _apiComponents = new string[]
{
nameof(LumexDropdown),
nameof(LumexDropdownMenu),
nameof(LumexDropdownItem),
nameof(LumexButton),
nameof(LumexIcon)
};

protected override void OnInitialized()
{
Layout.Initialize(
title: "Dropdown",
category: "Components",
description: "Dropdowns display a list of actions in a popover that users can select.",
headings: _headings,
linksProps: new ComponentLinksProps( "Dropdown", isServer: false )
);
}
}
Loading
Loading