diff --git a/examples/Demo/Shared/Infrastructure/ServiceCollectionExtensions.cs b/examples/Demo/Shared/Infrastructure/ServiceCollectionExtensions.cs index 396b52ff33..50d5286431 100644 --- a/examples/Demo/Shared/Infrastructure/ServiceCollectionExtensions.cs +++ b/examples/Demo/Shared/Infrastructure/ServiceCollectionExtensions.cs @@ -13,6 +13,7 @@ public static IServiceCollection AddFluentUIDemoServices(this IServiceCollection { services.AddScoped(); services.AddHttpClient(); + services.AddSingleton(); return services; } diff --git a/examples/Demo/Shared/Shared/DemoMainLayout.razor b/examples/Demo/Shared/Shared/DemoMainLayout.razor index b7d4b4f713..63de3874f4 100644 --- a/examples/Demo/Shared/Shared/DemoMainLayout.razor +++ b/examples/Demo/Shared/Shared/DemoMainLayout.razor @@ -13,7 +13,7 @@ + + + \ No newline at end of file + diff --git a/examples/Demo/Shared/Shared/DemoNavMenu.razor b/examples/Demo/Shared/Shared/DemoNavMenu.razor index 1098b71e83..5404c51c68 100644 --- a/examples/Demo/Shared/Shared/DemoNavMenu.razor +++ b/examples/Demo/Shared/Shared/DemoNavMenu.razor @@ -1,132 +1,14 @@ +@inject DemoNavProvider NavProvider + \ No newline at end of file + diff --git a/examples/Demo/Shared/Shared/DemoNavMenuItem.razor b/examples/Demo/Shared/Shared/DemoNavMenuItem.razor new file mode 100644 index 0000000000..530078a240 --- /dev/null +++ b/examples/Demo/Shared/Shared/DemoNavMenuItem.razor @@ -0,0 +1,34 @@ +@using NavLink = FluentUI.Demo.Shared.NavLink + +@switch(Value) +{ + case NavGroup group: + + +

@group.Title

+ + @foreach(var item in group.Children) + { + + } + +
+ break; + case NavLink: + + @if (Value.Match is NavLinkMatch.All) + { +

@Value.Title

+ } + else + { + @Value.Title + } +
+ break; +} + +@code { + [Parameter, EditorRequired] + public required NavItem Value { get; set; } +} diff --git a/examples/Demo/Shared/Shared/DemoNavProvider.cs b/examples/Demo/Shared/Shared/DemoNavProvider.cs new file mode 100644 index 0000000000..d79902d36a --- /dev/null +++ b/examples/Demo/Shared/Shared/DemoNavProvider.cs @@ -0,0 +1,562 @@ +// ------------------------------------------------------------------------ +// MIT License - Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------------------ + +using Microsoft.AspNetCore.Components.Routing; +using Microsoft.FluentUI.AspNetCore.Components; + +namespace FluentUI.Demo.Shared; + +public class DemoNavProvider +{ + public IReadOnlyList NavMenuItems { get; init; } + + public IReadOnlyList FlattenedMenuItems { get; init; } + + public DemoNavProvider() + { + NavMenuItems = + [ + new NavLink( + href: "/", + match: NavLinkMatch.All, + icon: new Icons.Regular.Size20.Home(), + title: "Home" + ), + + new NavGroup( + icon: new Icons.Regular.Size20.PersonRunning(), + title: "Getting Started", + expanded: true, + gap: "10px", + children: + [ + new NavLink( + href: "/WhatsNew", + icon: new Icons.Regular.Size20.Info(), + title: "What's new" + ), + + new NavLink( + href: "/UpgradeGuide", + icon: new Icons.Regular.Size20.ArrowUp(), + title: "Upgrade guide" + ), + + new NavLink( + href: "/CodeSetup", + icon: new Icons.Regular.Size20.DocumentOnePageSparkle(), + title: "Code setup" + ), + + new NavLink( + href: "/Templates", + icon: new Icons.Regular.Size20.Classification(), + title: "Project templates" + ), + + new NavLink( + href: "/DesignTheme", + icon: new Icons.Regular.Size20.DarkTheme(), + title: "Themes" + ), + + new NavLink( + href: "/DesignTokens", + icon: new Icons.Regular.Size20.DesignIdeas(), + title: "Design tokens" + ), + + new NavLink( + href: "/Reboot", + icon: new Icons.Regular.Size20.ArrowReset(), + title: "Reboot" + ), + + new NavLink( + href: "/IconsAndEmoji", + icon: new Icons.Regular.Size20.Symbols(), + title: "Icons and Emoji" + ), + + new NavGroup( + icon: new Icons.Regular.Size20.SettingsCogMultiple(), + title: "Services", + expanded: false, + gap: "10px", + children: + [ + new NavLink( + href: "/DialogService", + icon: new Icons.Regular.Size20.AppGeneric(), + title: "DialogService" + ), + + new NavLink( + href: "/MessageService", + icon: new Icons.Regular.Size20.WindowHeaderHorizontal(), + title: "MessageService" + ), + + new NavLink( + href: "/ToastService", + icon: new Icons.Regular.Size20.FoodToast(), + title: "ToastService" + ) + ] + ) + ] + ), + new NavGroup( + icon: new Icons.Regular.Size20.ContentViewGallery(), + title: "Layout", + expanded: false, + gap: "10px", + children: + [ + new NavLink( + href: "/Header", + icon: new Icons.Regular.Size20.DocumentHeader(), + title: "Header" + ), + new NavLink( + href: "/Footer", + icon: new Icons.Regular.Size20.DocumentFooter(), + title: "Footer" + ), + new NavLink( + href: "/BodyContent", + icon: new Icons.Regular.Size20.ContentViewGallery(), + title: "BodyContent" + ), + new NavLink( + href: "/Grid", + icon: new Icons.Regular.Size20.Grid(), + title: "Grid" + ), + new NavLink( + href: "/Layout", + icon: new Icons.Regular.Size20.SlideLayout(), + title: "Layout" + ), + new NavLink( + href: "/MainLayout", + icon: new Icons.Regular.Size20.MatchAppLayout(), + title: "MainLayout" + ), + new NavLink( + href: "/Spacer", + icon: new Icons.Regular.Size20.Spacebar(), + title: "Spacer" + ), + new NavLink( + href: "/Splitter", + icon: new Icons.Regular.Size20.SplitVertical(), + title: "Splitter" + ), + new NavLink( + href: "/Stack", + icon: new Icons.Regular.Size20.Stack(), + title: "Stack" + ) + ]), + new NavGroup( + icon: new Icons.Regular.Size20.Form(), + title: "Form & Inputs", + expanded: false, + gap: "10px", + children: + [ + new NavLink( + href: "/Forms", + icon: new Icons.Regular.Size20.Form(), + title: "Overview" + ), + new NavLink( + href: "/Checkbox", + icon: new Icons.Regular.Size20.CheckboxChecked(), + title: "Checkbox" + ), + new NavLink( + href: "/InputFile", + icon: new Icons.Regular.Size20.ArrowUpload(), + title: "InputFile" + ), + new NavLink( + href: "/NumberField", + icon: new Icons.Regular.Size20.NumberSymbolSquare(), + title: "Number Field" + ), + new NavLink( + href: "/Radio", + icon: new Icons.Regular.Size20.RadioButton(), + title: "Radio" + ), + new NavLink( + href: "/RadioGroup", + icon: new Icons.Regular.Size20.RadioButton(), + title: "Radio Group" + ), + new NavLink( + href: "/Search", + icon: new Icons.Regular.Size20.SearchSquare(), + title: "Search" + ), + new NavLink( + href: "/Slider", + icon: new Icons.Regular.Size20.Options(), + title: "Slider" + ), + new NavLink( + href: "/Switch", + icon: new Icons.Regular.Size20.ToggleLeft(), + title: "Switch" + ), + new NavLink( + href: "/TextArea", + icon: new Icons.Regular.Size20.TextboxMore(), + title: "TextArea" + ), + new NavLink( + href: "/TextField", + icon: new Icons.Regular.Size20.Textbox(), + title: "Text Field" + ), + new NavLink( + href: "/DateTime#fluenttimepicker-class", + icon: new Icons.Regular.Size20.Clock(), + title: "Time picker" + ) + ] + ), + + new NavGroup( + icon: new Icons.Regular.Size20.PuzzleCubePiece(), + title: "Components", + expanded: false, + gap: "10px", + children: + [ + new NavLink( + href: "/FluentComponentBase", + icon: new Icons.Regular.Size20.PuzzleCubePiece(), + title: "Overview" + ), + new NavLink( + href: "/Accordion", + icon: new Icons.Regular.Size20.TextCollapse(), + title: "Accordion" + ), + new NavLink( + href: "/Anchor", + icon: new Icons.Regular.Size20.Link(), + title: "Anchor" + ), + new NavLink( + href: "/AnchoredRegion", + icon: new Icons.Regular.Size20.LinkSquare(), + title: "Anchored Region" + ), + new NavGroup( + title: "Badge", + expanded: true, + gap: "10px", + icon: new Icons.Regular.Size20.Tag(), + children: + [ + new NavLink( + href: "/Badge", + icon: new Icons.Regular.Size20.Badge(), + title: "Badge" + ), + new NavLink( + href: "/CounterBadge", + icon: new Icons.Regular.Size20.NumberCircle1(), + title: "CounterBadge" + ), + new NavLink( + href: "/PresenceBadge", + icon: new Icons.Regular.Size20.PresenceAvailable(), + title: "PresenceBadge" + ) + ] + ), + new NavLink( + href: "/Breadcrumb", + icon: new Icons.Regular.Size20.DocumentChevronDouble(), + title: "Breadcrumb" + ), + new NavLink( + href: "/Button", + icon: new Icons.Regular.Size20.ControlButton(), + title: "Button" + ), + new NavLink( + href: "/Card", + icon: new Icons.Regular.Size20.ContactCardGroup(), + title: "Card" + ), + new NavLink( + href: "/DataGrid", + icon: new Icons.Regular.Size20.Grid(), + title: "Data grid" + ), + new NavLink( + href: "/DateTime", + icon: new Icons.Regular.Size20.CalendarLtr(), + title: "Date & Time" + ), + new NavLink( + href: "/Dialog", + icon: new Icons.Regular.Size20.AppGeneric(), + title: "Dialog" + ), + new NavLink( + href: "/Divider", + icon: new Icons.Regular.Size20.DividerShort(), + title: "Divider" + ), + new NavLink( + href: "/Drag", + icon: new Icons.Regular.Size20.Drag(), + title: "Drag and Drop" + ), + new NavLink( + href: "/Emoji", + icon: new Icons.Regular.Size20.EmojiSmileSlight(), + title: "Emoji" + ), + new NavLink( + href: "/Flipper", + icon: new Icons.Regular.Size20.Navigation(), + title: "Flipper" + ), + new NavLink( + href: "/Highlighter", + icon: new Icons.Regular.Size20.Highlight(), + title: "Highlighter" + ), + new NavLink( + href: "/HorizontalScroll", + icon: new Icons.Regular.Size20.ArrowForward(), + title: "Horizontal Scroll" + ), + new NavLink( + href: "/Icon", + icon: new Icons.Regular.Size20.Symbols(), + title: "Icon" + ), + new NavLink( + href: "/Label", + icon: new Icons.Regular.Size20.DoorTag(), + title: "Label" + ), + new NavGroup( + title: "List", + expanded: true, + gap: "10px", + icon: new Icons.Regular.Size20.List(), + children: + [ + new NavLink( + href: "/Autocomplete", + icon: new Icons.Regular.Size20.ArrowAutofitContent(), + title: "Autocomplete" + ), + new NavLink( + href: "/Combobox", + icon: new Icons.Regular.Size20.BoxEdit(), + title: "Combobox" + ), + new NavLink( + href: "/Listbox", + icon: new Icons.Regular.Size20.List(), + title: "Listbox" + ), + new NavLink( + href: "/Select", + icon: new Icons.Regular.Size20.GroupList(), + title: "Select" + ), + new NavLink( + href: "/Option", + icon: new Icons.Regular.Size20.MultiselectRtl(), + title: "Option" + ) + ] + ), + new NavLink( + href: "/Menu", + icon: new Icons.Regular.Size20.Navigation(), + title: "Menu" + ), + new NavLink( + href: "/MenuButton", + icon: new Icons.Regular.Size20.ChevronCircleDown(), + title: "MenuButton" + ), + new NavLink( + href: "/MessageBar", + icon: new Icons.Regular.Size20.WindowHeaderHorizontal(), + title: "MessageBar" + ), + new NavLink( + href: "/MessageBox", + icon: new Icons.Regular.Size20.MegaphoneLoud(), + title: "MessageBox" + ), + new NavLink( + href: "/NavMenu", + icon: new Icons.Regular.Size20.Navigation(), + title: "NavMenu" + ), + new NavLink( + href: "/NavMenuTree", + icon: new Icons.Regular.Size20.Navigation(), + title: "NavMenuTree" + ), + new NavLink( + href: "/Overflow", + icon: new Icons.Regular.Size20.MultiselectRtl(), + title: "Overflow" + ), + new NavLink( + href: "/Overlay", + icon: new Icons.Regular.Size20.CursorHover(), + title: "Overlay" + ), + new NavLink( + href: "/Panel", + icon: new Icons.Regular.Size20.PanelRight(), + title: "Panel" + ), + new NavLink( + href: "/Persona", + icon: new Icons.Regular.Size20.PersonAvailable(), + title: "Persona" + ), + new NavLink( + href: "/Popover", + icon: new Icons.Regular.Size20.TooltipQuote(), + title: "Popover" + ), + new NavLink( + href: "/Progress", + icon: new Icons.Regular.Size20.SquareHint(), + title: "Progress" + ), + new NavLink( + href: "/ProgressRing", + icon: new Icons.Regular.Size20.ArrowClockwiseDashes(), + title: "Progress Ring" + ), + new NavLink( + href: "/Skeleton", + icon: new Icons.Regular.Size20.Shortpick(), + title: "Skeleton" + ), + new NavLink( + href: "/SortableList", + icon: new Icons.Regular.Size20.ArrowSort(), + title: "Sortable List" + ), + new NavLink( + href: "/SplashScreen", + icon: new Icons.Regular.Size20.Drop(), + title: "SplashScreen" + ), + new NavLink( + href: "/Tabs", + icon: new Icons.Regular.Size20.TabDesktop(), + title: "Tabs" + ), + new NavLink( + href: "/Toast", + icon: new Icons.Regular.Size20.FoodToast(), + title: "Toast" + ), + new NavLink( + href: "/Toolbar", + icon: new Icons.Regular.Size20.WrenchScrewdriver(), + title: "Toolbar" + ), + new NavLink( + href: "/Tooltip", + icon: new Icons.Regular.Size20.TooltipQuote(), + title: "Tooltip" + ), + new NavLink( + href: "/TreeView", + icon: new Icons.Regular.Size20.TextBulletListTree(), + title: "Tree View" + ), + new NavLink( + href: "/new Icons.Regular.Size20.ArrowStepInRight()", + icon: new Icons.Regular.Size20.TextBulletListTree(), + title: "Wizard" + ) + ]), + + new NavGroup( + icon: new Icons.Regular.Size20.Beaker(), + title: "Incubation lab", + expanded: false, + gap: "10px", + children: + [ + new NavLink( + href: "/Lab/Overview", + icon: new Icons.Regular.Size20.Beaker(), + title: "Overview" + ), + + new NavLink( + href: "/Lab/MarkdownSection", + icon: new Icons.Regular.Size20.ArrowSortDown(), + title: "MarkdownSection" + ), + + new NavLink( + href: "/Lab/TableOfContents", + icon: new Icons.Regular.Size20.DocumentTextLink(), + title: "TableOfContents" + ), + + new NavLink( + href: "/MultiSplitter", + icon: new Icons.Regular.Size20.SplitHorizontal(), + title: "Multi Splitter" + ), + + new NavLink( + href: "/KeyCode", + icon: new Icons.Regular.Size20.Keyboard(), + title: "KeyCode" + ) + ] + ) + ]; + + FlattenedMenuItems = GetFlattenedMenuItems(NavMenuItems) + .ToList() + .AsReadOnly(); + } + + private static IEnumerable GetFlattenedMenuItems(IEnumerable items) + { + foreach (var item in items) + { + yield return item; + + if (item is not NavGroup group || !group.Children.Any()) + { + continue; + } + + foreach (var flattenedMenuItem in GetFlattenedMenuItems(group.Children)) + { + yield return flattenedMenuItem; + } + } + } +} diff --git a/examples/Demo/Shared/Shared/DemoSearch.razor b/examples/Demo/Shared/Shared/DemoSearch.razor new file mode 100644 index 0000000000..714bf2dcb1 --- /dev/null +++ b/examples/Demo/Shared/Shared/DemoSearch.razor @@ -0,0 +1,24 @@ + + +@if (_searchResults is not null) +{ + + @foreach (var searchResult in _searchResults) + { + var item = NavProvider.FlattenedMenuItems.First(x => x.Title == searchResult); + + + + + + @item.Title + + } + +} diff --git a/examples/Demo/Shared/Shared/DemoSearch.razor.cs b/examples/Demo/Shared/Shared/DemoSearch.razor.cs new file mode 100644 index 0000000000..f391d8c980 --- /dev/null +++ b/examples/Demo/Shared/Shared/DemoSearch.razor.cs @@ -0,0 +1,63 @@ +// ------------------------------------------------------------------------ +// MIT License - Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.AspNetCore.Components; + +namespace FluentUI.Demo.Shared.Shared; + +public partial class DemoSearch +{ + [Inject] + protected DemoNavProvider NavProvider { get; set; } = default!; + + [Inject] + protected NavigationManager NavigationManager { get; set; } = default!; + + private string? _searchValue = string.Empty; + + private bool _visible; + + private List? _searchResults = DefaultResults(); + private static List? DefaultResults() => null; + + private void HandleSearchInput() + { + if (string.IsNullOrWhiteSpace(_searchValue)) + { + _searchResults = DefaultResults(); + _searchValue = string.Empty; + } + else + { + var searchTerm = _searchValue.ToLower(); + + if (searchTerm.Length > 0) + { + _searchResults = NavProvider.FlattenedMenuItems + .Where(x => x.Href != null) // Ignore Group headers + .Where(x => x.Title.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)) + .Select(x => x.Title) + .ToList(); + + if (_searchResults.Count == 0) + { + _searchResults = DefaultResults(); + } + } + } + + _visible = _searchResults is not null; + } + + private void HandleSearchClicked(NavItem item) + { + _searchValue = string.Empty; + _searchResults = DefaultResults(); + _visible = false; + InvokeAsync(StateHasChanged); + + NavigationManager.NavigateTo(item.Href ?? throw new UnreachableException("Item has no href")); + } +} diff --git a/examples/Demo/Shared/Shared/NavItem.cs b/examples/Demo/Shared/Shared/NavItem.cs new file mode 100644 index 0000000000..ad433d609a --- /dev/null +++ b/examples/Demo/Shared/Shared/NavItem.cs @@ -0,0 +1,44 @@ +// ------------------------------------------------------------------------ +// MIT License - Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------------------ + +using Microsoft.AspNetCore.Components.Routing; +using Microsoft.FluentUI.AspNetCore.Components; + +namespace FluentUI.Demo.Shared; + +public abstract record NavItem +{ + public string Title { get; init; } = string.Empty; + public string? Href { get; init; } + public NavLinkMatch Match { get; init; } = NavLinkMatch.Prefix; + public Icon Icon { get; init; } = new Icons.Regular.Size20.Document(); +} + +public record NavLink : NavItem +{ + public NavLink(string? href, Icon icon, string title, NavLinkMatch match = NavLinkMatch.Prefix) + { + Href = href; + Icon = icon; + Title = title; + Match = match; + } +} + +public record NavGroup : NavItem +{ + public bool Expanded { get; init; } + public string Gap { get; init; } + public IReadOnlyList Children { get; } + + public NavGroup(Icon icon, string title, bool expanded, string gap, List children) + { + Href = null; + Icon = icon; + Title = title; + Expanded = expanded; + Gap = gap; + Children = children.AsReadOnly(); + } +} diff --git a/examples/Demo/Shared/wwwroot/css/site.css b/examples/Demo/Shared/wwwroot/css/site.css index 1a0c2a79a7..c1e7d3d4bd 100644 --- a/examples/Demo/Shared/wwwroot/css/site.css +++ b/examples/Demo/Shared/wwwroot/css/site.css @@ -17,6 +17,12 @@ body { grid-column: 1; } + .siteheader .search { + display: flex; + align-items: center; + padding-right: 20px; + } + .siteheader .links { padding-right: 10px; display: flex; @@ -42,10 +48,19 @@ body { margin-right: 0; } +[dir="rtl"] .siteheader .search { + padding-left: 20px; + padding-right: 0; +} + [dir="rtl"] .siteheader .links { padding-left: 10px; } +.search-result-icon { + vertical-align: middle; +} + .body-stack { flex-direction: row; } @@ -260,6 +275,10 @@ code { justify-content: flex-start; } + .siteheader .search { + display: none; + } + .siteheader .logo { width: 160px; height: 23px; @@ -272,10 +291,10 @@ code { } nav.sitenav { - + width: 100%; height: calc(100dvh - 50px); - + } .navmenu {