From 234ef5659cf3e079bd0aec5b7e57291fffbf000d Mon Sep 17 00:00:00 2001 From: desmondinho <68395709+desmondinho@users.noreply.github.com> Date: Fri, 31 Jan 2025 02:38:00 +0200 Subject: [PATCH] feat(components): add XML summaries to all publicly visible members (#162) * chore: .editorconfig * feat(components): add missing summaries * refactor(components): adjust LabelPlacement namespace * refactor(radio): update `Color` param summary to point the correct default value * test: update `_Imports` --- .editorconfig | 4 +- .../CompositionComponentAttribute.cs | 29 +- src/LumexUI/Common/Enums/AccordionVariant.cs | 25 +- src/LumexUI/Common/Enums/Align.cs | 28 +- src/LumexUI/Common/Enums/ButtonType.cs | 16 +- src/LumexUI/Common/Enums/InputBehavior.cs | 20 +- src/LumexUI/Common/Enums/InputType.cs | 96 ++--- src/LumexUI/Common/Enums/InputVariant.cs | 29 +- src/LumexUI/Common/Enums/LabelPlacement.cs | 25 +- src/LumexUI/Common/Enums/Layout.cs | 26 +- src/LumexUI/Common/Enums/ListboxVariant.cs | 30 +- src/LumexUI/Common/Enums/MaxWidth.cs | 27 +- src/LumexUI/Common/Enums/Orientation.cs | 11 +- src/LumexUI/Common/Enums/PopoverPlacement.cs | 123 +++--- src/LumexUI/Common/Enums/Radius.cs | 17 +- src/LumexUI/Common/Enums/SelectionMode.cs | 28 +- src/LumexUI/Common/Enums/Shadow.cs | 17 +- src/LumexUI/Common/Enums/Size.cs | 12 + src/LumexUI/Common/Enums/SortDirection.cs | 32 +- src/LumexUI/Common/Enums/TabVariant.cs | 17 +- src/LumexUI/Common/Enums/ThemeColor.cs | 35 +- src/LumexUI/Common/Enums/ThemeType.cs | 13 +- src/LumexUI/Common/Enums/Underline.cs | 12 + src/LumexUI/Common/Enums/Variant.cs | 35 +- .../Events/DataGridRowClickEventArgs.cs | 48 +-- .../Common/Exceptions/ContextNullException.cs | 40 +- src/LumexUI/Common/Interfaces/ISlot.cs | 14 +- .../Components/Accordion/AccordionContext.cs | 114 +++--- .../Accordion/AccordionItemSlots.cs | 63 ++- .../Accordion/LumexAccordion.razor.cs | 163 ++++---- .../Accordion/LumexAccordionItem.razor.cs | 380 +++++++++--------- .../Components/Bases/InputFieldSlots.cs | 43 ++ .../Components/Bases/LumexBooleanInputBase.cs | 107 ++--- .../Components/Bases/LumexComponentBase.cs | 73 ++-- src/LumexUI/Components/Card/CardSlots.cs | 33 +- .../Components/Card/LumexCard.razor.cs | 96 ++--- .../Components/Card/LumexCardBody.razor.cs | 28 +- .../Components/Card/LumexCardFooter.razor.cs | 44 +- .../Components/Card/LumexCardHeader.razor.cs | 28 +- .../Components/Checkbox/CheckboxGroupSlots.cs | 33 +- .../Components/Checkbox/CheckboxSlots.cs | 33 +- .../Checkbox/LumexCheckbox.razor.cs | 147 +++---- .../Checkbox/LumexCheckboxGroup.razor.cs | 160 ++++---- .../Components/Collapse/LumexCollapse.cs | 279 ++++++------- .../Components/Divider/LumexDivider.razor.cs | 23 +- .../Components/Icon/LumexIcon.razor.cs | 91 +++-- .../Components/Link/LumexLink.razor.cs | 171 ++++---- src/LumexUI/Components/Navbar/NavbarSlots.cs | 39 ++ .../Components/Popover/PopoverSlots.cs | 19 + .../Providers/LumexThemeProvider.razor.cs | 179 +++++---- .../Components/Radio/LumexRadio.razor.cs | 238 +++++------ .../Components/Radio/LumexRadioGroup.razor.cs | 350 ++++++++-------- .../Components/Radio/RadioGroupContext.cs | 71 ++-- .../Components/Switch/LumexSwitch.razor.cs | 76 ++-- src/LumexUI/Components/Switch/SwitchSlots.cs | 51 ++- .../Components/Textbox/LumexTextbox.razor.cs | 47 ++- .../Extensions/ElementReferenceExtensions.cs | 57 ++- .../Extensions/ServiceCollectionExtensions.cs | 93 ++--- .../Services/Popover/IPopoverService.cs | 34 +- .../Services/Popover/PopoverService.cs | 41 +- tests/LumexUI.Tests/_Imports.razor | 1 + 61 files changed, 2400 insertions(+), 1814 deletions(-) diff --git a/.editorconfig b/.editorconfig index dfb80530..c3ac438c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -17,6 +17,8 @@ insert_final_newline = false #### .NET Code Actions #### +dotnet_diagnostic.IDE0130.severity = silent + # Type members dotnet_hide_advanced_members = false dotnet_member_insertion_location = with_other_members_of_the_same_kind @@ -217,7 +219,7 @@ dotnet_naming_rule.property_should_be_pascal_case.severity = warning dotnet_naming_rule.property_should_be_pascal_case.symbols = property dotnet_naming_rule.property_should_be_pascal_case.style = pascal_case -dotnet_naming_rule.private_or_internal_field_should_be_camel_case_prefixed_with__.severity = warning +dotnet_naming_rule.private_or_internal_field_should_be_camel_case_prefixed_with__.severity = silent dotnet_naming_rule.private_or_internal_field_should_be_camel_case_prefixed_with__.symbols = private_or_internal_field dotnet_naming_rule.private_or_internal_field_should_be_camel_case_prefixed_with__.style = camel_case_prefixed_with__ diff --git a/src/LumexUI/Common/Attributes/CompositionComponentAttribute.cs b/src/LumexUI/Common/Attributes/CompositionComponentAttribute.cs index 4dcb5b5b..542c8cf2 100644 --- a/src/LumexUI/Common/Attributes/CompositionComponentAttribute.cs +++ b/src/LumexUI/Common/Attributes/CompositionComponentAttribute.cs @@ -1,28 +1,23 @@ -using System.Diagnostics.CodeAnalysis; +// 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; namespace LumexUI.Common; /// -/// Specifies that a class is a composition component and defines the type of the aggregator component. +/// Specifies that a class is a composition component and associates it with an aggregator type. /// /// /// This attribute is used to indicate that the decorated class is part of a composition and is aggregated by a specific type. /// -[AttributeUsage( AttributeTargets.Class )] [ExcludeFromCodeCoverage] -public class CompositionComponentAttribute : Attribute +[AttributeUsage( AttributeTargets.Class )] +public class CompositionComponentAttribute( Type aggregatorType ) : Attribute { - /// - /// Gets the type of the aggregator component. - /// - public Type AggregatorType { get; } - - /// - /// Initializes a new instance of the class with the specified aggregator type. - /// - /// The type of the aggregator component. - public CompositionComponentAttribute( Type aggregatorType ) - { - AggregatorType = aggregatorType; - } + /// + /// Gets the aggregator type associated with the composition component. + /// + public Type AggregatorType { get; } = aggregatorType; } diff --git a/src/LumexUI/Common/Enums/AccordionVariant.cs b/src/LumexUI/Common/Enums/AccordionVariant.cs index 121e41bb..4546087b 100644 --- a/src/LumexUI/Common/Enums/AccordionVariant.cs +++ b/src/LumexUI/Common/Enums/AccordionVariant.cs @@ -4,13 +4,28 @@ namespace LumexUI.Common; +/// +/// Specifies the visual variants for the . +/// public enum AccordionVariant { - Light, + /// + /// A variant without any styling. + /// + Light, - Shadow, + /// + /// A variant with a shadow effect. + /// + Shadow, - Bordered, + /// + /// A variant with a visible border around the accordion. + /// + Bordered, - Splitted -} \ No newline at end of file + /// + /// A variant where sections are visually separated. + /// + Splitted +} diff --git a/src/LumexUI/Common/Enums/Align.cs b/src/LumexUI/Common/Enums/Align.cs index 4edb1525..cc3c7781 100644 --- a/src/LumexUI/Common/Enums/Align.cs +++ b/src/LumexUI/Common/Enums/Align.cs @@ -5,22 +5,22 @@ namespace LumexUI.Common; /// -/// Specifies the alignment options. +/// Specifies alignment options of a component. /// public enum Align { - /// - /// Aligns the content to the start. - /// - Start, + /// + /// Alignment to the start. + /// + Start, - /// - /// Aligns the content to the center. - /// - Center, + /// + /// Alignment to the center. + /// + Center, - /// - /// Aligns the content to the end. - /// - End -} \ No newline at end of file + /// + /// Alignment to the end. + /// + End +} diff --git a/src/LumexUI/Common/Enums/ButtonType.cs b/src/LumexUI/Common/Enums/ButtonType.cs index 5004cbd9..acba89dc 100644 --- a/src/LumexUI/Common/Enums/ButtonType.cs +++ b/src/LumexUI/Common/Enums/ButtonType.cs @@ -4,11 +4,23 @@ namespace LumexUI.Common; +/// +/// Specifies the type of the . +/// public enum ButtonType { + /// + /// A standard button with no default behavior. + /// Button, - Submit, + /// + /// A button that submits form data. + /// + Submit, - Reset + /// + /// A button that resets all form fields to their initial values. + /// + Reset } \ No newline at end of file diff --git a/src/LumexUI/Common/Enums/InputBehavior.cs b/src/LumexUI/Common/Enums/InputBehavior.cs index d9350646..09d0b01e 100644 --- a/src/LumexUI/Common/Enums/InputBehavior.cs +++ b/src/LumexUI/Common/Enums/InputBehavior.cs @@ -5,19 +5,17 @@ namespace LumexUI.Common; /// -/// Specifies when the input component updates its value and triggers validation. +/// Specifies the behavior of an input field when handling user input. /// public enum InputBehavior { - /// - /// Updates the value and triggers validation - /// on each input event (e.g., when the user types in the input field). - /// - OnInput, + /// + /// Triggers the input event as the user types. + /// + OnInput, - /// - /// Updates the value and triggers validation - /// on change events (e.g., when the input field loses focus or the user presses enter). - /// - OnChange + /// + /// Triggers the input event only when the field loses focus or the value changes. + /// + OnChange } diff --git a/src/LumexUI/Common/Enums/InputType.cs b/src/LumexUI/Common/Enums/InputType.cs index 13f970c8..561f9041 100644 --- a/src/LumexUI/Common/Enums/InputType.cs +++ b/src/LumexUI/Common/Enums/InputType.cs @@ -7,55 +7,55 @@ namespace LumexUI.Common; /// -/// Specifies the different types of input elements. +/// Specifies the type of the . /// public enum InputType { - /// - /// A text input field. - /// - [Description( "text" )] - Text, - - /// - /// A password input field. - /// - [Description( "password" )] - Password, - - /// - /// An email input field. - /// - [Description( "email" )] - Email, - - /// - /// A hidden input field. - /// - [Description( "hidden" )] - Hidden, - - /// - /// A search input field. - /// - [Description( "search" )] - Search, - - /// - /// A telephone input field. - /// - [Description( "tel" )] - Telephone, - - /// - /// A URL input field. - /// - [Description( "url" )] - Url, - - /// - /// A color input field. - /// - [Description( "color" )] - Color + /// + /// A single-line text input field. + /// + [Description( "text" )] + Text, + + /// + /// A password input field that masks the entered characters. + /// + [Description( "password" )] + Password, + + /// + /// An input field for email addresses with built-in validation. + /// + [Description( "email" )] + Email, + + /// + /// A hidden input field that is not visible to the user. + /// + [Description( "hidden" )] + Hidden, + + /// + /// A search input field optimized for search queries. + /// + [Description( "search" )] + Search, + + /// + /// An input field for telephone numbers. + /// + [Description( "tel" )] + Telephone, + + /// + /// An input field for entering a URL. + /// + [Description( "url" )] + Url, + + /// + /// An input field for selecting a color. + /// + [Description( "color" )] + Color } diff --git a/src/LumexUI/Common/Enums/InputVariant.cs b/src/LumexUI/Common/Enums/InputVariant.cs index e823531f..337eacd5 100644 --- a/src/LumexUI/Common/Enums/InputVariant.cs +++ b/src/LumexUI/Common/Enums/InputVariant.cs @@ -5,22 +5,23 @@ namespace LumexUI.Common; /// -/// Specifies the different variants for an input component. +/// Specifies the visual variants for input field components, +/// such as , , and others. /// public enum InputVariant { - /// - /// A flat variant input. - /// - Flat, + /// + /// A variant with a subtle background. + /// + Flat, - /// - /// An outlined variant input. - /// - Outlined, + /// + /// A variant with an outlined border. + /// + Outlined, - /// - /// An underlined variant input. - /// - Underlined -} \ No newline at end of file + /// + /// A variant with an underline. + /// + Underlined +} diff --git a/src/LumexUI/Common/Enums/LabelPlacement.cs b/src/LumexUI/Common/Enums/LabelPlacement.cs index 2a8131c8..15a99dcf 100644 --- a/src/LumexUI/Common/Enums/LabelPlacement.cs +++ b/src/LumexUI/Common/Enums/LabelPlacement.cs @@ -1,17 +1,22 @@ -namespace LumexUI; +// 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 + +namespace LumexUI.Common; /// -/// Specifies the placement options for the label of an input component. +/// Specifies the placement options for label in input field components, +/// such as , and others. /// public enum LabelPlacement { - /// - /// Places the label inside the input component. - /// - Inside, + /// + /// The label is placed inside the input field. + /// + Inside, - /// - /// Places the label outside the input component. - /// - Outside + /// + /// The label is placed outside the input field. + /// + Outside } diff --git a/src/LumexUI/Common/Enums/Layout.cs b/src/LumexUI/Common/Enums/Layout.cs index 50568e20..4f20688e 100644 --- a/src/LumexUI/Common/Enums/Layout.cs +++ b/src/LumexUI/Common/Enums/Layout.cs @@ -5,26 +5,20 @@ namespace LumexUI.Common; /// -/// Specifies the layout behavior for a table, determining how the table and its columns are sized. +/// Specifies the table layout algorithm used for rendering. /// /// -/// See +/// See table-layout for details. /// public enum Layout { - /// - /// Automatically sizes columns based on content, allowing for flexible widths. - /// - /// - /// See - /// - Auto, + /// + /// The table layout is determined automatically based on the content of the cells. + /// + Auto, - /// - /// Fixes the width of columns, preventing them from adjusting based on content. - /// - /// - /// See - /// - Fixed + /// + /// The table layout is fixed, meaning column widths are determined by the table's first row and do not change based on content. + /// + Fixed } diff --git a/src/LumexUI/Common/Enums/ListboxVariant.cs b/src/LumexUI/Common/Enums/ListboxVariant.cs index 0c6b4747..d7562da3 100644 --- a/src/LumexUI/Common/Enums/ListboxVariant.cs +++ b/src/LumexUI/Common/Enums/ListboxVariant.cs @@ -4,15 +4,33 @@ namespace LumexUI.Common; +/// +/// Specifies the visual variants for the . +/// public enum ListboxVariant { - Solid, + /// + /// A variant with a solid background. + /// + Solid, - Outlined, + /// + /// A variant with an outlined border. + /// + Outlined, - Flat, + /// + /// A variant with a subtle background. + /// + Flat, - Shadow, + /// + /// A variant with a shadow effect. + /// + Shadow, - Light -} \ No newline at end of file + /// + /// A variant without any styling. + /// + Light +} diff --git a/src/LumexUI/Common/Enums/MaxWidth.cs b/src/LumexUI/Common/Enums/MaxWidth.cs index a58b5625..fce375c1 100644 --- a/src/LumexUI/Common/Enums/MaxWidth.cs +++ b/src/LumexUI/Common/Enums/MaxWidth.cs @@ -4,17 +4,38 @@ namespace LumexUI.Common; +/// +/// Specifies the maximum width options for a . +/// public enum MaxWidth { + /// + /// A small maximum width. + /// Small, + /// + /// A medium maximum width. + /// Medium, + /// + /// A large maximum width. + /// Large, - XLarge, + /// + /// An extra-large maximum width. + /// + XLarge, - XXLarge, + /// + /// A double extra-large maximum width. + /// + XXLarge, - Full + /// + /// The maximum width spans the full available space. + /// + Full } diff --git a/src/LumexUI/Common/Enums/Orientation.cs b/src/LumexUI/Common/Enums/Orientation.cs index 58e363ce..cc5dce18 100644 --- a/src/LumexUI/Common/Enums/Orientation.cs +++ b/src/LumexUI/Common/Enums/Orientation.cs @@ -4,9 +4,18 @@ namespace LumexUI.Common; +/// +/// Specifies the orientation of a component. +/// public enum Orientation { + /// + /// The horizontal arrangement. + /// Horizontal, + /// + /// The vertical arrangement. + /// Vertical -} \ No newline at end of file +} diff --git a/src/LumexUI/Common/Enums/PopoverPlacement.cs b/src/LumexUI/Common/Enums/PopoverPlacement.cs index 40fd6581..5d9c5251 100644 --- a/src/LumexUI/Common/Enums/PopoverPlacement.cs +++ b/src/LumexUI/Common/Enums/PopoverPlacement.cs @@ -7,79 +7,80 @@ namespace LumexUI.Common; /// -/// Specifies the placement options for a popover component. +/// Specifies the placement options for the . /// public enum PopoverPlacement { - /// - /// Places the popover above the reference. - /// - [Description( "top" )] - Top, + /// + /// Positions the popover at the top. + /// + [Description( "top" )] + Top, - /// - /// Places the popover above the reference, aligned to the start (left) of the reference. - /// - [Description( "top-start" )] - TopStart, + /// + /// Positions the popover at the top, aligned to the start. + /// + [Description( "top-start" )] + TopStart, - /// - /// Places the popover above the reference, aligned to the end (right) of the reference. - /// - [Description( "top-end" )] - TopEnd, + /// + /// Positions the popover at the top, aligned to the end. + /// + [Description( "top-end" )] + TopEnd, - /// - /// Places the popover to the right of the reference. - /// - [Description( "right" )] - Right, + /// + /// Positions the popover to the right. + /// + [Description( "right" )] + Right, - /// - /// Places the popover to the right of the reference, aligned to the start (top) of the reference. - /// - [Description( "right-start" )] - RightStart, + /// + /// Positions the popover to the right, aligned to the start. + /// + [Description( "right-start" )] + RightStart, - /// - /// Places the popover to the right of the reference, aligned to the end (bottom) of the reference. - /// - [Description( "right-end" )] - RightEnd, + /// + /// Positions the popover to the right, aligned to the end. + /// + [Description( "right-end" )] + RightEnd, - /// - /// Places the popover below the reference. - /// - [Description( "bottom" )] - Bottom, + /// + /// Positions the popover at the bottom. + /// + [Description( "bottom" )] + Bottom, - /// - /// Places the popover below the reference, aligned to the start (left) of the reference. - /// - [Description( "bottom-start" )] - BottomStart, + /// + /// Positions the popover at the bottom, aligned to the start. + /// + [Description( "bottom-start" )] + BottomStart, - /// - /// Places the popover below the reference, aligned to the end (right) of the reference. - /// - [Description( "bottom-end" )] - BottomEnd, + /// + /// Positions the popover at the bottom, aligned to the end. + /// + [Description( "bottom-end" )] + BottomEnd, - /// - /// Places the popover to the left of the reference. - /// - [Description( "left" )] - Left, + /// + /// Positions the popover to the left. + /// + [Description( "left" )] + Left, - /// - /// Places the popover to the left of the reference, aligned to the start (top) of the reference. - /// - [Description( "left-start" )] - LeftStart, + /// + /// Positions the popover to the left, aligned to the start. + /// + [Description( "left-start" )] + LeftStart, - /// - /// Places the popover to the left of the reference, aligned to the end (bottom) of reference. - /// - [Description( "left-end" )] - LeftEnd + /// + /// Positions the popover to the left, aligned to the end. + /// + [Description( "left-end" )] + LeftEnd } + diff --git a/src/LumexUI/Common/Enums/Radius.cs b/src/LumexUI/Common/Enums/Radius.cs index 062673bd..fdf6b298 100644 --- a/src/LumexUI/Common/Enums/Radius.cs +++ b/src/LumexUI/Common/Enums/Radius.cs @@ -4,13 +4,28 @@ namespace LumexUI.Common; +/// +/// Specifies the border radius options for a component. +/// public enum Radius { - None, + /// + /// No border radius. + /// + None, + /// + /// A small border radius. + /// Small, + /// + /// A medium border radius. + /// Medium, + /// + /// A large border radius. + /// Large } diff --git a/src/LumexUI/Common/Enums/SelectionMode.cs b/src/LumexUI/Common/Enums/SelectionMode.cs index 1fd9ea4b..c64d47b8 100644 --- a/src/LumexUI/Common/Enums/SelectionMode.cs +++ b/src/LumexUI/Common/Enums/SelectionMode.cs @@ -5,22 +5,22 @@ namespace LumexUI.Common; /// -/// Specifies the selection mode for a component. +/// Specifies the selection modes for a component. /// public enum SelectionMode { - /// - /// No selection is allowed. - /// - None, + /// + /// No selection allowed. + /// + None, - /// - /// Single item selection is allowed. - /// - Single, + /// + /// Allows selecting a single item. + /// + Single, - /// - /// Multiple items selection is allowed. - /// - Multiple -} \ No newline at end of file + /// + /// Allows selecting multiple items. + /// + Multiple +} diff --git a/src/LumexUI/Common/Enums/Shadow.cs b/src/LumexUI/Common/Enums/Shadow.cs index 46c67c0f..dce85600 100644 --- a/src/LumexUI/Common/Enums/Shadow.cs +++ b/src/LumexUI/Common/Enums/Shadow.cs @@ -4,13 +4,28 @@ namespace LumexUI.Common; +/// +/// Specifies the shadow intensity levels for a component. +/// public enum Shadow { - None, + /// + /// No shadow. + /// + None, + /// + /// A small shadow. + /// Small, + /// + /// A medium shadow. + /// Medium, + /// + /// A large shadow. + /// Large } diff --git a/src/LumexUI/Common/Enums/Size.cs b/src/LumexUI/Common/Enums/Size.cs index b77f45b1..afb424da 100644 --- a/src/LumexUI/Common/Enums/Size.cs +++ b/src/LumexUI/Common/Enums/Size.cs @@ -4,11 +4,23 @@ namespace LumexUI.Common; +/// +/// Specifies size options for a component. +/// public enum Size { + /// + /// A small size. + /// Small, + /// + /// A medium size. + /// Medium, + /// + /// A large size. + /// Large } diff --git a/src/LumexUI/Common/Enums/SortDirection.cs b/src/LumexUI/Common/Enums/SortDirection.cs index 31854ce8..464b90d2 100644 --- a/src/LumexUI/Common/Enums/SortDirection.cs +++ b/src/LumexUI/Common/Enums/SortDirection.cs @@ -1,22 +1,26 @@ -namespace LumexUI.Common; +// 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 + +namespace LumexUI.Common; /// -/// Specifies the sort direction for a column. +/// Specifies the sort direction for a column in the . /// public enum SortDirection { - /// - /// Sorts the data in automatic order. - /// - Auto, + /// + /// An automatic sort direction. + /// + Auto, - /// - /// Sorts the data in ascending order. - /// - Ascending, + /// + /// An ascending sort order. + /// + Ascending, - /// - /// Sorts the data in descending order. - /// - Descending + /// + /// A descending sort order. + /// + Descending } diff --git a/src/LumexUI/Common/Enums/TabVariant.cs b/src/LumexUI/Common/Enums/TabVariant.cs index 5e0a9d62..2c14e46f 100644 --- a/src/LumexUI/Common/Enums/TabVariant.cs +++ b/src/LumexUI/Common/Enums/TabVariant.cs @@ -4,13 +4,28 @@ namespace LumexUI.Common; +/// +/// Specifies the visual variants of the . +/// public enum TabVariant { + /// + /// A variant with a solid background. + /// Solid, + /// + /// A variant with an outlined border. + /// Outlined, + /// + /// A variant with an underline. + /// Underlined, + /// + /// A variant without any styling. + /// Light -} \ No newline at end of file +} diff --git a/src/LumexUI/Common/Enums/ThemeColor.cs b/src/LumexUI/Common/Enums/ThemeColor.cs index f5aa4608..abc4c0fd 100644 --- a/src/LumexUI/Common/Enums/ThemeColor.cs +++ b/src/LumexUI/Common/Enums/ThemeColor.cs @@ -4,21 +4,48 @@ namespace LumexUI.Common; +/// +/// Specifies theme color options for a component. +/// public enum ThemeColor { - None, - - Default, - + /// + /// No specific theme color. + /// + None, + + /// + /// The default theme color. + /// + Default, + + /// + /// The primary theme color. + /// Primary, + /// + /// The secondary theme color. + /// Secondary, + /// + /// The success theme color. + /// Success, + /// + /// The warning theme color. + /// Warning, + /// + /// The danger theme color. + /// Danger, + /// + /// The info theme color. + /// Info } diff --git a/src/LumexUI/Common/Enums/ThemeType.cs b/src/LumexUI/Common/Enums/ThemeType.cs index 64e2cdf3..5b28f474 100644 --- a/src/LumexUI/Common/Enums/ThemeType.cs +++ b/src/LumexUI/Common/Enums/ThemeType.cs @@ -4,13 +4,24 @@ using System.ComponentModel; +using LumexUI.Theme; + namespace LumexUI.Common; +/// +/// Specifies theme type options for the . +/// public enum ThemeType { + /// + /// A light theme. + /// [Description( "light" )] Light, + /// + /// A dark theme. + /// [Description( "dark" )] Dark -} \ No newline at end of file +} diff --git a/src/LumexUI/Common/Enums/Underline.cs b/src/LumexUI/Common/Enums/Underline.cs index 1f7fe0fb..fee19db8 100644 --- a/src/LumexUI/Common/Enums/Underline.cs +++ b/src/LumexUI/Common/Enums/Underline.cs @@ -4,11 +4,23 @@ namespace LumexUI.Common; +/// +/// Specifies underline behavior for the . +/// public enum Underline { + /// + /// No underline. + /// None, + /// + /// Underline on hover. + /// Hover, + /// + /// Underline always visible. + /// Always } diff --git a/src/LumexUI/Common/Enums/Variant.cs b/src/LumexUI/Common/Enums/Variant.cs index ff9dd1c8..9e99e271 100644 --- a/src/LumexUI/Common/Enums/Variant.cs +++ b/src/LumexUI/Common/Enums/Variant.cs @@ -4,17 +4,38 @@ namespace LumexUI.Common; +/// +/// Specifies the visual variants for a component. +/// public enum Variant { - Solid, + /// + /// A variant with a filled background. + /// + Solid, - Outlined, + /// + /// A variant with an outlined border. + /// + Outlined, - Flat, + /// + /// A variant with a subtle background. + /// + Flat, - Shadow, + /// + /// A variant with a shadow effect. + /// + Shadow, - Ghost, + /// + /// A variant with an outlined border; with a filled background on hover. + /// + Ghost, - Light -} \ No newline at end of file + /// + /// A variant without any styling. + /// + Light +} diff --git a/src/LumexUI/Common/Events/DataGridRowClickEventArgs.cs b/src/LumexUI/Common/Events/DataGridRowClickEventArgs.cs index 4e38b9c9..8d3c9ae9 100644 --- a/src/LumexUI/Common/Events/DataGridRowClickEventArgs.cs +++ b/src/LumexUI/Common/Events/DataGridRowClickEventArgs.cs @@ -1,34 +1,36 @@ -using System.Diagnostics.CodeAnalysis; +// 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; namespace LumexUI.Common; /// -/// Provides data for the event, -/// including the clicked item and its index. +/// Provides data for the row click event in the . /// -/// The type of data represented by the row. +/// The type of the data item associated with the clicked row. [ExcludeFromCodeCoverage] public class DataGridRowClickEventArgs : EventArgs { - /// - /// Gets the data item associated with the clicked row. - /// - public T Item { get; } + /// + /// Gets the data item associated with the clicked row. + /// + public T Item { get; } - /// - /// Gets the index of the clicked row in the data grid. - /// - public int Index { get; } + /// + /// Gets the zero-based index of the clicked row. + /// + public int Index { get; } - /// - /// Initializes a new instance of the class with the specified item and index. - /// - /// The data item associated with the clicked row. - /// The index of the clicked row. - public DataGridRowClickEventArgs( T item, int index ) - { - Item = item; - Index = index; - } + /// + /// Initializes a new instance of the class. + /// + /// The data item associated with the clicked row. + /// The zero-based index of the clicked row. + public DataGridRowClickEventArgs( T item, int index ) + { + Item = item; + Index = index; + } } - diff --git a/src/LumexUI/Common/Exceptions/ContextNullException.cs b/src/LumexUI/Common/Exceptions/ContextNullException.cs index 9f3896f2..e0d0da5e 100644 --- a/src/LumexUI/Common/Exceptions/ContextNullException.cs +++ b/src/LumexUI/Common/Exceptions/ContextNullException.cs @@ -9,27 +9,27 @@ namespace LumexUI.Common; [ExcludeFromCodeCoverage] internal class ContextNullException : Exception { - internal ContextNullException( string? message ) - : base( message ) { } + internal ContextNullException( string? message ) + : base( message ) { } - internal ContextNullException( string? message, Exception? innerException ) - : base( message, innerException ) { } + internal ContextNullException( string? message, Exception? innerException ) + : base( message, innerException ) { } - internal static void ThrowIfNull( [NotNull] IComponentContext? context, string descendant ) - where T : LumexComponentBase - { - if( context is null ) - { - var owner = typeof( T ).Name; - Throw( owner, descendant ); - } - } + internal static void ThrowIfNull( [NotNull] IComponentContext? context, string descendant ) + where T : LumexComponentBase + { + if( context is null ) + { + var owner = typeof( T ).Name; + Throw( owner, descendant ); + } + } - [DoesNotReturn] - private static void Throw( string owner, string descendant ) - { - throw new ContextNullException( - $"The '<{descendant} />' component can only be used inside a '<{owner} />' component. " + - $"Please ensure that '<{descendant} />' is a child of '<{owner} />' in component tree." ); - } + [DoesNotReturn] + private static void Throw( string owner, string descendant ) + { + throw new ContextNullException( + $"The '<{descendant} />' component can only be used inside a '<{owner} />' component. " + + $"Please ensure that '<{descendant} />' is a child of '<{owner} />' in component tree." ); + } } diff --git a/src/LumexUI/Common/Interfaces/ISlot.cs b/src/LumexUI/Common/Interfaces/ISlot.cs index ba62560a..1c664b9f 100644 --- a/src/LumexUI/Common/Interfaces/ISlot.cs +++ b/src/LumexUI/Common/Interfaces/ISlot.cs @@ -1,6 +1,16 @@ -namespace LumexUI.Common; +// 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 +namespace LumexUI.Common; + +/// +/// Defines a contract for a slot that provides styling customization through CSS class properties. +/// public interface ISlot { - string? Root { get; } + /// + /// Gets the CSS class for the root element of the slot. + /// + string? Root { get; } } diff --git a/src/LumexUI/Components/Accordion/AccordionContext.cs b/src/LumexUI/Components/Accordion/AccordionContext.cs index 1b55c906..9849d106 100644 --- a/src/LumexUI/Components/Accordion/AccordionContext.cs +++ b/src/LumexUI/Components/Accordion/AccordionContext.cs @@ -1,61 +1,65 @@ -using LumexUI.Common; +// 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; internal sealed class AccordionContext( LumexAccordion owner ) : IComponentContext { - public readonly List _items = []; - - public LumexAccordion Owner { get; } = owner; - - public void Register( LumexAccordionItem item ) - { - _items.Add( item ); - - if( Owner.Expanded ) - { - Owner.ExpandedItems.Add( item.Id ); - } - } - - public void Unregister( LumexAccordionItem item ) - { - _items.Remove( item ); - } - - public bool IsLast( LumexAccordionItem item ) - { - return item == _items[^1]; - } - - public ValueTask ToggleExpansionAsync( LumexAccordionItem item ) - { - if( Owner.SelectionMode is SelectionMode.Single ) - { - if( !Owner.ExpandedItems.Contains( item.Id ) ) - { - return CollapseAllButThisAsync( item ); - } - - Owner.ExpandedItems.Remove( item.Id ); - } - - return ValueTask.CompletedTask; - } - - private async ValueTask CollapseAllButThisAsync( LumexAccordionItem item ) - { - foreach( var accordionItem in _items ) - { - if( accordionItem == item - || accordionItem.Disabled - || !accordionItem.GetExpandedState() ) - { - continue; - } - - Owner.ExpandedItems.Remove( accordionItem.Id ); - await accordionItem.CollapseAsync(); - } - } + public readonly List _items = []; + + public LumexAccordion Owner { get; } = owner; + + public void Register( LumexAccordionItem item ) + { + _items.Add( item ); + + if( Owner.Expanded ) + { + Owner.ExpandedItems.Add( item.Id ); + } + } + + public void Unregister( LumexAccordionItem item ) + { + _items.Remove( item ); + } + + public bool IsLast( LumexAccordionItem item ) + { + return item == _items[^1]; + } + + public ValueTask ToggleExpansionAsync( LumexAccordionItem item ) + { + if( Owner.SelectionMode is SelectionMode.Single ) + { + if( !Owner.ExpandedItems.Contains( item.Id ) ) + { + return CollapseAllButThisAsync( item ); + } + + Owner.ExpandedItems.Remove( item.Id ); + } + + return ValueTask.CompletedTask; + } + + private async ValueTask CollapseAllButThisAsync( LumexAccordionItem item ) + { + foreach( var accordionItem in _items ) + { + if( accordionItem == item + || accordionItem.Disabled + || !accordionItem.GetExpandedState() ) + { + continue; + } + + Owner.ExpandedItems.Remove( accordionItem.Id ); + await accordionItem.CollapseAsync(); + } + } } diff --git a/src/LumexUI/Components/Accordion/AccordionItemSlots.cs b/src/LumexUI/Components/Accordion/AccordionItemSlots.cs index 18bbca02..58770e66 100644 --- a/src/LumexUI/Components/Accordion/AccordionItemSlots.cs +++ b/src/LumexUI/Components/Accordion/AccordionItemSlots.cs @@ -1,19 +1,62 @@ -using System.Diagnostics.CodeAnalysis; +// 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.Common; namespace LumexUI; +/// +/// Represents the slot names for the , +/// used to assign CSS classes to different parts of the component. +/// [ExcludeFromCodeCoverage] public class AccordionItemSlots : ISlot { - public string? Root { get; set; } - public string? Heading { get; set; } - public string? Trigger { get; set; } - public string? TitleWrapper { get; set; } - public string? Title { get; set; } - public string? Subtitle { get; set; } - public string? StartContent { get; set; } - public string? Indicator { get; set; } - public string? Content { get; set; } + /// + /// Gets or sets the CSS class for the root slot. + /// + public string? Root { get; set; } + + /// + /// Gets or sets the CSS class for the heading slot. + /// + public string? Heading { get; set; } + + /// + /// Gets or sets the CSS class for the trigger slot. + /// + public string? Trigger { get; set; } + + /// + /// Gets or sets the CSS class for the title wrapper slot. + /// + public string? TitleWrapper { get; set; } + + /// + /// Gets or sets the CSS class for the title slot. + /// + public string? Title { get; set; } + + /// + /// Gets or sets the CSS class for the subtitle slot. + /// + public string? Subtitle { get; set; } + + /// + /// Gets or sets the CSS class for the start content slot. + /// + public string? StartContent { get; set; } + + /// + /// Gets or sets the CSS class for the indicator slot. + /// + public string? Indicator { get; set; } + + /// + /// Gets or sets the CSS class for the content slot. + /// + public string? Content { get; set; } } diff --git a/src/LumexUI/Components/Accordion/LumexAccordion.razor.cs b/src/LumexUI/Components/Accordion/LumexAccordion.razor.cs index 4d6e7074..31b7633a 100644 --- a/src/LumexUI/Components/Accordion/LumexAccordion.razor.cs +++ b/src/LumexUI/Components/Accordion/LumexAccordion.razor.cs @@ -9,87 +9,88 @@ namespace LumexUI; +/// +/// A component that represents an accordion, allowing content to be expanded and collapsed. +/// public partial class LumexAccordion : LumexComponentBase { - /// - /// Gets or sets content to be rendered inside the accordion. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - /// - /// Gets or sets an appearance style of the accordion. - /// - /// - /// The default is - /// - [Parameter] public AccordionVariant Variant { get; set; } - - /// - /// Gets or sets the selection mode for the accordion, - /// determining how items can be selected. - /// - /// - /// The default is - /// - [Parameter] public SelectionMode SelectionMode { get; set; } = SelectionMode.Single; - - /// - /// Gets or sets a value indicating whether the accordion is full-width. - /// - /// - /// The default is - /// - [Parameter] public bool FullWidth { get; set; } = true; - - /// - /// Gets or sets a value indicating whether the accordion items are disabled. - /// - [Parameter] public bool Disabled { get; set; } - - /// - /// Gets or sets a value indicating whether the accordion items are expanded. - /// - [Parameter] public bool Expanded { get; set; } - - /// - /// Gets or sets a value indicating whether to display a divider - /// under each accordion item (except the last one). - /// - /// - /// The default is - /// - [Parameter] public bool ShowDividers { get; set; } = true; - - /// - /// Gets or sets a value indicating whether to display an indicator in each accordion item. - /// - /// - /// The default is - /// - [Parameter] public bool ShowIndicators { get; set; } = true; - - /// - /// Gets or sets the set of accordion item identifiers that are expanded by default in the accordion. - /// - [Parameter] public ICollection ExpandedItems { get; set; } = []; - - /// - /// Gets or sets the set of accordion item identifiers that are disabled in the accordion. - /// - [Parameter] public ICollection DisabledItems { get; set; } = []; - - /// - /// Gets or sets the CSS class names for the accordion items slots. - /// - [Parameter] public AccordionItemSlots? ItemClasses { get; set; } - - private protected override string? RootClass => - TwMerge.Merge( Accordion.GetStyles( this ) ); - - private readonly AccordionContext _context; - - public LumexAccordion() - { - _context = new AccordionContext( this ); - } + /// + /// Gets or sets the content to render. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Gets or sets the visual variant. + /// + [Parameter] public AccordionVariant Variant { get; set; } + + /// + /// Gets or sets the selection mode. + /// + /// + /// The default value is . + /// + [Parameter] public SelectionMode SelectionMode { get; set; } = SelectionMode.Single; + + /// + /// Gets or sets a value indicating whether the accordion is full width. + /// + /// + /// The default value is . + /// + [Parameter] public bool FullWidth { get; set; } = true; + + /// + /// Gets or sets a value indicating whether the accordion is disabled. + /// + [Parameter] public bool Disabled { get; set; } + + /// + /// Gets or sets a value indicating whether the accordion is expanded by default. + /// + [Parameter] public bool Expanded { get; set; } + + /// + /// Gets or sets a value indicating whether dividers should be shown between accordion items. + /// + /// + /// The default value is . + /// + [Parameter] public bool ShowDividers { get; set; } = true; + + /// + /// Gets or sets a value indicating whether indicators should be shown for accordion items. + /// + /// + /// The default value is . + /// + [Parameter] public bool ShowIndicators { get; set; } = true; + + /// + /// Gets or sets the collection of item keys that are expanded. + /// + [Parameter] public ICollection ExpandedItems { get; set; } = []; + + /// + /// Gets or sets the collection of item keys that are disabled. + /// + [Parameter] public ICollection DisabledItems { get; set; } = []; + + /// + /// Gets or sets the CSS classes for individual accordion items. + /// + [Parameter] public AccordionItemSlots? ItemClasses { get; set; } + + private protected override string? RootClass => + TwMerge.Merge( Accordion.GetStyles( this ) ); + + private readonly AccordionContext _context; + + /// + /// Initializes a new instance of the . + /// + public LumexAccordion() + { + _context = new AccordionContext( this ); + } } diff --git a/src/LumexUI/Components/Accordion/LumexAccordionItem.razor.cs b/src/LumexUI/Components/Accordion/LumexAccordionItem.razor.cs index 0ef9b783..92dcb124 100644 --- a/src/LumexUI/Components/Accordion/LumexAccordionItem.razor.cs +++ b/src/LumexUI/Components/Accordion/LumexAccordionItem.razor.cs @@ -9,189 +9,203 @@ namespace LumexUI; +/// +/// A component that represents an item within the . +/// [CompositionComponent( typeof( LumexAccordion ) )] -public partial class LumexAccordionItem : LumexComponentBase, ISlotComponent, IDisposable +public partial class LumexAccordionItem : LumexComponentBase, + ISlotComponent, + IDisposable { - /// - /// Gets or sets the unique identifier for the accordion item. - /// - [Parameter, EditorRequired] public string Id { get; set; } = default!; - - /// - /// Gets or sets content to be rendered inside the accordion item. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - /// - /// Gets or sets content to be rendered at the start of the accordion item. - /// - [Parameter] public RenderFragment? StartContent { get; set; } - - /// - /// Gets or sets content to be rendered as the title of the accordion item. - /// - [Parameter] public RenderFragment? TitleContent { get; set; } - - /// - /// Gets or sets content to be rendered as the subtitle of the accordion item. - /// - [Parameter] public RenderFragment? SubtitleContent { get; set; } - - /// - /// Gets or sets the title of the accordion item. - /// - [Parameter] public string? Title { get; set; } - - /// - /// Gets or sets the subtitle of the accordion item. - /// - [Parameter] public string? Subtitle { get; set; } - - /// - /// Gets or sets the delegate that resolves the indicator icon based on its expanded state. - /// - /// - /// The default is - /// - [Parameter] public IndicatorResolver Indicator { get; set; } = ( _ ) => Icons.Rounded.ChevronLeft; - - /// - /// Gets or sets a value indicating whether the accordion item is disabled. - /// - [Parameter] public bool Disabled { get; set; } - - /// - /// Gets or sets a value indicating whether the accordion item is expanded. - /// - [Parameter] public bool Expanded { get; set; } - - /// - /// Gets or sets the callback that is invoked when the expanded state of the accordion item changes. - /// - [Parameter] public EventCallback ExpandedChanged { get; set; } - - /// - /// Gets or sets the CSS class names for the accordion item slots. - /// - [Parameter] public AccordionItemSlots? Classes { get; set; } - - [CascadingParameter] internal AccordionContext Context { get; set; } = default!; - - private protected override string? RootClass => - TwMerge.Merge( AccordionItem.GetStyles( this ) ); - - private string? HeadingClass => - TwMerge.Merge( AccordionItem.GetHeadingStyles( this ) ); - - private string? TriggerClass => - TwMerge.Merge( AccordionItem.GetTriggerStyles( this ) ); - - private string? StartContentClass => - TwMerge.Merge( AccordionItem.GetStartContentStyles( this ) ); - - private string? TitleWrapperClass => - TwMerge.Merge( AccordionItem.GetTitleWrapperStyles( this ) ); - - private string? TitleClass => - TwMerge.Merge( AccordionItem.GetTitleStyles( this ) ); - - private string? SubtitleClass => - TwMerge.Merge( AccordionItem.GetSubtitleStyles( this ) ); - - private string? IndicatorClass => - TwMerge.Merge( AccordionItem.GetIndicatorStyles( this ) ); - - private string? ContentClass => - TwMerge.Merge( AccordionItem.GetContentStyles( this ) ); - - private bool _disposed; - private bool _disabled; - private bool _expanded; - - public Task ExpandAsync() - { - return SetExpandedStateTo( true ); - } - - public Task CollapseAsync() - { - return SetExpandedStateTo( false ); - } - - internal bool GetExpandedState() => _expanded; - - internal bool GetDisabledState() => _disabled; - - protected override void OnInitialized() - { - ContextNullException.ThrowIfNull( Context, nameof( LumexAccordionItem ) ); - - Context.Register( this ); - } - - protected override void OnParametersSet() - { - if( string.IsNullOrWhiteSpace( Id ) ) - { - throw new InvalidOperationException( - $"{GetType()} requires a non-null, non-empty, non-whitespace value for parameter '{nameof( Id )}'." ); - } - - if( !string.IsNullOrWhiteSpace( Title ) && TitleContent is not null ) - { - throw new InvalidOperationException( - $"{GetType()} can only accept one title content source from its parameters. " + - $"Do not supply both '{nameof( Title )}' and '{nameof( TitleContent )}'." ); - } - - if( !string.IsNullOrWhiteSpace( Subtitle ) && SubtitleContent is not null ) - { - throw new InvalidOperationException( - $"{GetType()} can only accept one subtitle content source from its parameters. " + - $"Do not supply both '{nameof( Subtitle )}' and '{nameof( SubtitleContent )}'." ); - } - - _expanded = Expanded || Context.Owner.ExpandedItems.Contains( Id ) || Context.Owner.Expanded; - _disabled = Disabled || Context.Owner.DisabledItems.Contains( Id ) || Context.Owner.Disabled; - } - - private async Task ToggleExpansionAsync() - { - if( _disabled || Context.Owner.SelectionMode is SelectionMode.None ) - { - return; - } - - _expanded = !_expanded; - - await Context.ToggleExpansionAsync( this ); - await ExpandedChanged.InvokeAsync( _expanded ); - } - - private Task SetExpandedStateTo( bool expanded ) - { - _expanded = expanded; - StateHasChanged(); - - return ExpandedChanged.InvokeAsync( _expanded ); - } - - public void Dispose() - { - Dispose( disposing: true ); - GC.SuppressFinalize( this ); - } - - protected virtual void Dispose( bool disposing ) - { - if( !_disposed ) - { - if( disposing ) - { - Context.Unregister( this ); - } - - _disposed = true; - } - } + /// + /// Gets or sets the content to render. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Gets or sets the content to render at the start. + /// + [Parameter] public RenderFragment? StartContent { get; set; } + + /// + /// Gets or sets the content to render as the title. + /// + [Parameter] public RenderFragment? TitleContent { get; set; } + + /// + /// Gets or sets the content to render as the subtitle. + /// + [Parameter] public RenderFragment? SubtitleContent { get; set; } + + /// + /// Gets or sets the unique identifier for the accordion item. + /// + [Parameter, EditorRequired] public string Id { get; set; } = default!; + + /// + /// Gets or sets the title. + /// + [Parameter] public string? Title { get; set; } + + /// + /// Gets or sets the subtitle. + /// + [Parameter] public string? Subtitle { get; set; } + + /// + /// Gets or sets the function that resolves the indicator icon. + /// + [Parameter] public IndicatorResolver Indicator { get; set; } = ( _ ) => Icons.Rounded.ChevronLeft; + + /// + /// Gets or sets a value indicating whether the accordion item is disabled. + /// + [Parameter] public bool Disabled { get; set; } + + /// + /// Gets or sets a value indicating whether the accordion item is expanded. + /// + [Parameter] public bool Expanded { get; set; } + + /// + /// Gets or sets the callback invoked when the expanded state changes. + /// + [Parameter] public EventCallback ExpandedChanged { get; set; } + + /// + /// Gets or sets the CSS classes for the accordion item slots. + /// + [Parameter] public AccordionItemSlots? Classes { get; set; } + + [CascadingParameter] internal AccordionContext Context { get; set; } = default!; + + private protected override string? RootClass => + TwMerge.Merge( AccordionItem.GetStyles( this ) ); + + private string? HeadingClass => + TwMerge.Merge( AccordionItem.GetHeadingStyles( this ) ); + + private string? TriggerClass => + TwMerge.Merge( AccordionItem.GetTriggerStyles( this ) ); + + private string? StartContentClass => + TwMerge.Merge( AccordionItem.GetStartContentStyles( this ) ); + + private string? TitleWrapperClass => + TwMerge.Merge( AccordionItem.GetTitleWrapperStyles( this ) ); + + private string? TitleClass => + TwMerge.Merge( AccordionItem.GetTitleStyles( this ) ); + + private string? SubtitleClass => + TwMerge.Merge( AccordionItem.GetSubtitleStyles( this ) ); + + private string? IndicatorClass => + TwMerge.Merge( AccordionItem.GetIndicatorStyles( this ) ); + + private string? ContentClass => + TwMerge.Merge( AccordionItem.GetContentStyles( this ) ); + + private bool _disposed; + private bool _disabled; + private bool _expanded; + + /// + /// Asynchronously expands the accordion item. + /// + /// A representing the asynchronous expand operation. + public Task ExpandAsync() + { + return SetExpandedStateTo( true ); + } + + /// + /// Asynchronously collapses the accordion item. + /// + /// A representing the asynchronous collapse operation. + public Task CollapseAsync() + { + return SetExpandedStateTo( false ); + } + + internal bool GetExpandedState() => _expanded; + + internal bool GetDisabledState() => _disabled; + + /// + protected override void OnInitialized() + { + ContextNullException.ThrowIfNull( Context, nameof( LumexAccordionItem ) ); + + Context.Register( this ); + } + + /// + protected override void OnParametersSet() + { + if( string.IsNullOrWhiteSpace( Id ) ) + { + throw new InvalidOperationException( + $"{GetType()} requires a non-null, non-empty, non-whitespace value for parameter '{nameof( Id )}'." ); + } + + if( !string.IsNullOrWhiteSpace( Title ) && TitleContent is not null ) + { + throw new InvalidOperationException( + $"{GetType()} can only accept one title content source from its parameters. " + + $"Do not supply both '{nameof( Title )}' and '{nameof( TitleContent )}'." ); + } + + if( !string.IsNullOrWhiteSpace( Subtitle ) && SubtitleContent is not null ) + { + throw new InvalidOperationException( + $"{GetType()} can only accept one subtitle content source from its parameters. " + + $"Do not supply both '{nameof( Subtitle )}' and '{nameof( SubtitleContent )}'." ); + } + + _expanded = Expanded || Context.Owner.ExpandedItems.Contains( Id ) || Context.Owner.Expanded; + _disabled = Disabled || Context.Owner.DisabledItems.Contains( Id ) || Context.Owner.Disabled; + } + + private async Task ToggleExpansionAsync() + { + if( _disabled || Context.Owner.SelectionMode is SelectionMode.None ) + { + return; + } + + _expanded = !_expanded; + + await Context.ToggleExpansionAsync( this ); + await ExpandedChanged.InvokeAsync( _expanded ); + } + + private Task SetExpandedStateTo( bool expanded ) + { + _expanded = expanded; + StateHasChanged(); + + return ExpandedChanged.InvokeAsync( _expanded ); + } + + /// + public void Dispose() + { + Dispose( disposing: true ); + GC.SuppressFinalize( this ); + } + + /// + protected virtual void Dispose( bool disposing ) + { + if( !_disposed ) + { + if( disposing ) + { + Context.Unregister( this ); + } + + _disposed = true; + } + } } diff --git a/src/LumexUI/Components/Bases/InputFieldSlots.cs b/src/LumexUI/Components/Bases/InputFieldSlots.cs index 9fefbd0d..83394968 100644 --- a/src/LumexUI/Components/Bases/InputFieldSlots.cs +++ b/src/LumexUI/Components/Bases/InputFieldSlots.cs @@ -8,17 +8,60 @@ namespace LumexUI; +/// +/// Represents the slot names for the and , +/// used to assign CSS classes to different parts of the component. +/// [ExcludeFromCodeCoverage] public class InputFieldSlots : ISlot { + /// + /// Gets or sets the CSS class for the root slot. + /// public string? Root { get; set; } + + /// + /// Gets or sets the CSS class for the label slot. + /// public string? Label { get; set; } + + /// + /// Gets or sets the CSS class for the main wrapper slot. + /// public string? MainWrapper { get; set; } + + /// + /// Gets or sets the CSS class for the input wrapper slot. + /// public string? InputWrapper { get; set; } + + /// + /// Gets or sets the CSS class for the inner wrapper slot. + /// public string? InnerWrapper { get; set; } + + /// + /// Gets or sets the CSS class for the input slot. + /// public string? Input { get; set; } + + /// + /// Gets or sets the CSS class for the clear button slot. + /// public string? ClearButton { get; set; } + + /// + /// Gets or sets the CSS class for the helper wrapper slot. + /// public string? HelperWrapper { get; set; } + + /// + /// Gets or sets the CSS class for the description slot. + /// public string? Description { get; set; } + + /// + /// Gets or sets the CSS class for the error message slot. + /// public string? ErrorMessage { get; set; } } diff --git a/src/LumexUI/Components/Bases/LumexBooleanInputBase.cs b/src/LumexUI/Components/Bases/LumexBooleanInputBase.cs index cce19e4f..b5957156 100644 --- a/src/LumexUI/Components/Bases/LumexBooleanInputBase.cs +++ b/src/LumexUI/Components/Bases/LumexBooleanInputBase.cs @@ -1,58 +1,65 @@ -using System.Diagnostics.CodeAnalysis; +// 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 Microsoft.AspNetCore.Components; namespace LumexUI; +/// +/// Represents a base class for boolean input components. +/// public abstract class LumexBooleanInputBase : LumexInputBase { - /// - /// Gets or sets content to be rendered inside the input. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - /// - /// Gets the disabled state of the input. - /// Derived classes can override this to determine the input's disabled state. - /// - /// A value indicating whether the input is disabled. - protected internal virtual bool GetDisabledState() => Disabled; - - /// - /// Gets the readonly state of the input. - /// Derived classes can override this to determine the input's readonly state. - /// - /// A value indicating whether the input is readonly. - protected internal virtual bool GetReadOnlyState() => ReadOnly; - - /// - protected override bool TryParseValueFromString( string? value, [MaybeNullWhen( false )] out bool result ) - { - throw new NotSupportedException( - $"This component does not parse string inputs. " + - $"Bind to the '{nameof( CurrentValue )}' property, not '{nameof( CurrentValueAsString )}'." ); - } - - /// - protected override ValueTask SetValidationMessageAsync() - { - // This component doesn't have a validation message by default. - return ValueTask.CompletedTask; - } - - /// - /// Handles the change event asynchronously. - /// Derived classes can override this to specify custom behavior when the input's value changes. - /// - /// The change event arguments. - /// A representing the asynchronous operation. - protected virtual Task OnChangeAsync( ChangeEventArgs args ) - { - if( GetDisabledState() || GetReadOnlyState() ) - { - return Task.CompletedTask; - } - - return SetCurrentValueAsync( (bool)args.Value! ); - } + /// + /// Gets or sets content to be rendered inside the input. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Gets the disabled state of the input. + /// Derived classes can override this to determine the input's disabled state. + /// + /// A value indicating whether the input is disabled. + protected internal virtual bool GetDisabledState() => Disabled; + + /// + /// Gets the readonly state of the input. + /// Derived classes can override this to determine the input's readonly state. + /// + /// A value indicating whether the input is readonly. + protected internal virtual bool GetReadOnlyState() => ReadOnly; + + /// + protected override bool TryParseValueFromString( string? value, [MaybeNullWhen( false )] out bool result ) + { + throw new NotSupportedException( + $"This component does not parse string inputs. " + + $"Bind to the '{nameof( CurrentValue )}' property, not '{nameof( CurrentValueAsString )}'." ); + } + + /// + protected override ValueTask SetValidationMessageAsync() + { + // This component doesn't have a validation message by default. + return ValueTask.CompletedTask; + } + + /// + /// Handles the change event asynchronously. + /// Derived classes can override this to specify custom behavior when the input's value changes. + /// + /// The change event arguments. + /// A representing the asynchronous operation. + protected virtual Task OnChangeAsync( ChangeEventArgs args ) + { + if( GetDisabledState() || GetReadOnlyState() ) + { + return Task.CompletedTask; + } + + return SetCurrentValueAsync( (bool)args.Value! ); + } } diff --git a/src/LumexUI/Components/Bases/LumexComponentBase.cs b/src/LumexUI/Components/Bases/LumexComponentBase.cs index f5aa0836..e7ddc78e 100644 --- a/src/LumexUI/Components/Bases/LumexComponentBase.cs +++ b/src/LumexUI/Components/Bases/LumexComponentBase.cs @@ -15,39 +15,42 @@ namespace LumexUI; /// public abstract class LumexComponentBase : ComponentBase { - /// - /// Gets or sets an HTML tag of the component. - /// - [Parameter] public string As { get; set; } = "div"; - - /// - /// Gets or sets CSS class names that will be applied to the component. - /// - [Parameter] public string? Class { get; set; } - - /// - /// Gets or sets styles that will be applied to the component. - /// - [Parameter] public string? Style { get; set; } - - /// - /// Gets or sets a collection of additional attributes that will be applied to the component. - /// - [Parameter( CaptureUnmatchedValues = true )] - public IReadOnlyDictionary? AdditionalAttributes { get; set; } - - [Inject] private protected TwMerge TwMerge { get; set; } = default!; - - /// - /// Gets or sets the associated . - /// - /// May be if accessed before the component is rendered. - /// - /// - [DisallowNull] public ElementReference? ElementReference { get; protected set; } - - private protected virtual string? RootClass => Class; - private protected virtual string? RootStyle => Style; - - public void Rerender() => StateHasChanged(); + /// + /// Gets or sets an HTML tag of the component. + /// + [Parameter] public string As { get; set; } = "div"; + + /// + /// Gets or sets CSS class names that will be applied to the component. + /// + [Parameter] public string? Class { get; set; } + + /// + /// Gets or sets styles that will be applied to the component. + /// + [Parameter] public string? Style { get; set; } + + /// + /// Gets or sets a collection of additional attributes that will be applied to the component. + /// + [Parameter( CaptureUnmatchedValues = true )] + public IReadOnlyDictionary? AdditionalAttributes { get; set; } + + [Inject] private protected TwMerge TwMerge { get; set; } = default!; + + /// + /// Gets or sets the associated . + /// + /// May be if accessed before the component is rendered. + /// + /// + [DisallowNull] public ElementReference? ElementReference { get; protected set; } + + private protected virtual string? RootClass => Class; + private protected virtual string? RootStyle => Style; + + /// + /// Triggers a re-render of the component. + /// + public void Rerender() => StateHasChanged(); } diff --git a/src/LumexUI/Components/Card/CardSlots.cs b/src/LumexUI/Components/Card/CardSlots.cs index 73acc4ba..a9005e71 100644 --- a/src/LumexUI/Components/Card/CardSlots.cs +++ b/src/LumexUI/Components/Card/CardSlots.cs @@ -1,14 +1,37 @@ -using System.Diagnostics.CodeAnalysis; +// 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.Common; namespace LumexUI; +/// +/// Represents the slot names for the , +/// used to assign CSS classes to different parts of the component. +/// [ExcludeFromCodeCoverage] public class CardSlots : ISlot { - public string? Root { get; set; } - public string? Header { get; set; } - public string? Body { get; set; } - public string? Footer { get; set; } + /// + /// Gets or sets the CSS class for the root slot. + /// + public string? Root { get; set; } + + /// + /// Gets or sets the CSS class for the header slot. + /// + public string? Header { get; set; } + + /// + /// Gets or sets the CSS class for the body slot. + /// + public string? Body { get; set; } + + /// + /// Gets or sets the CSS class for the footer slot. + /// + public string? Footer { get; set; } } diff --git a/src/LumexUI/Components/Card/LumexCard.razor.cs b/src/LumexUI/Components/Card/LumexCard.razor.cs index 90efc4b3..cba9a69b 100644 --- a/src/LumexUI/Components/Card/LumexCard.razor.cs +++ b/src/LumexUI/Components/Card/LumexCard.razor.cs @@ -9,51 +9,57 @@ namespace LumexUI; +/// +/// A component that represents a card. +/// public partial class LumexCard : LumexComponentBase, ISlotComponent { - /// - /// Gets or sets content to be rendered inside the card. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - /// - /// Gets or sets the border radius of the card. - /// - /// - /// Default value is - /// - [Parameter] public Radius Radius { get; set; } = Radius.Large; - - /// - /// Gets or sets the shadow of the card. - /// - /// - /// Default value is - /// - [Parameter] public Shadow Shadow { get; set; } = Shadow.Small; - - /// - /// Gets or sets a value indicating whether the card is full-width. - /// - [Parameter] public bool FullWidth { get; set; } - - /// - /// Gets or sets a value indicating whether the card is blurred. - /// - [Parameter] public bool Blurred { get; set; } - - /// - /// Gets or sets the CSS class names for the card slots. - /// - [Parameter] public CardSlots? Classes { get; set; } - - private protected override string? RootClass - => TwMerge.Merge( Card.GetStyles( this ) ); - - private readonly CardContext _context; - - public LumexCard() - { - _context = new CardContext( this ); - } + /// + /// Gets or sets content to be rendered inside the card. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Gets or sets the border radius of the card. + /// + /// + /// Default value is + /// + [Parameter] public Radius Radius { get; set; } = Radius.Large; + + /// + /// Gets or sets the shadow of the card. + /// + /// + /// Default value is + /// + [Parameter] public Shadow Shadow { get; set; } = Shadow.Small; + + /// + /// Gets or sets a value indicating whether the card is full-width. + /// + [Parameter] public bool FullWidth { get; set; } + + /// + /// Gets or sets a value indicating whether the card is blurred. + /// + [Parameter] public bool Blurred { get; set; } + + /// + /// Gets or sets the CSS class names for the card slots. + /// + [Parameter] public CardSlots? Classes { get; set; } + + private protected override string? RootClass + => TwMerge.Merge( Card.GetStyles( this ) ); + + private readonly CardContext _context; + + /// + /// Initializes a new instance of the . + /// + public LumexCard() + { + _context = new CardContext( this ); + } } \ No newline at end of file diff --git a/src/LumexUI/Components/Card/LumexCardBody.razor.cs b/src/LumexUI/Components/Card/LumexCardBody.razor.cs index fb1f0be0..85005943 100644 --- a/src/LumexUI/Components/Card/LumexCardBody.razor.cs +++ b/src/LumexUI/Components/Card/LumexCardBody.razor.cs @@ -9,21 +9,25 @@ namespace LumexUI; +/// +/// A component that represents the body section of the . +/// [CompositionComponent( typeof( LumexCard ) )] public partial class LumexCardBody : LumexComponentBase { - /// - /// Gets or sets content to be rendered inside the card body. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } + /// + /// Gets or sets content to be rendered inside the card body. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } - [CascadingParameter] internal CardContext Context { get; set; } = default!; + [CascadingParameter] internal CardContext Context { get; set; } = default!; - private protected override string? RootClass - => TwMerge.Merge( Card.GetBodyStyles( this ) ); + private protected override string? RootClass + => TwMerge.Merge( Card.GetBodyStyles( this ) ); - protected override void OnInitialized() - { - ContextNullException.ThrowIfNull( Context, nameof( LumexCardBody ) ); - } -} \ No newline at end of file + /// + protected override void OnInitialized() + { + ContextNullException.ThrowIfNull( Context, nameof( LumexCardBody ) ); + } +} diff --git a/src/LumexUI/Components/Card/LumexCardFooter.razor.cs b/src/LumexUI/Components/Card/LumexCardFooter.razor.cs index 77fca245..fc5d5159 100644 --- a/src/LumexUI/Components/Card/LumexCardFooter.razor.cs +++ b/src/LumexUI/Components/Card/LumexCardFooter.razor.cs @@ -9,26 +9,30 @@ namespace LumexUI; +/// +/// A component that represents the footer section of the . +/// [CompositionComponent( typeof( LumexCard ) )] public partial class LumexCardFooter : LumexComponentBase { - /// - /// Gets or sets content to be rendered inside the card footer. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - /// - /// Gets or sets a value indicating whether the card footer is blurred. - /// - [Parameter] public bool Blurred { get; set; } - - [CascadingParameter] internal CardContext Context { get; set; } = default!; - - private protected override string? RootClass - => TwMerge.Merge( Card.GetFooterStyles( this ) ); - - protected override void OnInitialized() - { - ContextNullException.ThrowIfNull( Context, nameof( LumexCardFooter ) ); - } -} \ No newline at end of file + /// + /// Gets or sets content to be rendered inside the card footer. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Gets or sets a value indicating whether the card footer is blurred. + /// + [Parameter] public bool Blurred { get; set; } + + [CascadingParameter] internal CardContext Context { get; set; } = default!; + + private protected override string? RootClass + => TwMerge.Merge( Card.GetFooterStyles( this ) ); + + /// + protected override void OnInitialized() + { + ContextNullException.ThrowIfNull( Context, nameof( LumexCardFooter ) ); + } +} diff --git a/src/LumexUI/Components/Card/LumexCardHeader.razor.cs b/src/LumexUI/Components/Card/LumexCardHeader.razor.cs index dd68e0ae..c0259988 100644 --- a/src/LumexUI/Components/Card/LumexCardHeader.razor.cs +++ b/src/LumexUI/Components/Card/LumexCardHeader.razor.cs @@ -9,21 +9,25 @@ namespace LumexUI; +/// +/// A component that represents the header section of the . +/// [CompositionComponent( typeof( LumexCard ) )] public partial class LumexCardHeader : LumexComponentBase { - /// - /// Gets or sets content to be rendered inside the card header. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } + /// + /// Gets or sets content to be rendered inside the card header. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } - [CascadingParameter] internal CardContext Context { get; set; } = default!; + [CascadingParameter] internal CardContext Context { get; set; } = default!; - private protected override string? RootClass - => TwMerge.Merge( Card.GetHeaderStyles( this ) ); + private protected override string? RootClass + => TwMerge.Merge( Card.GetHeaderStyles( this ) ); - protected override void OnInitialized() - { - ContextNullException.ThrowIfNull( Context, nameof( LumexCardHeader ) ); - } -} \ No newline at end of file + /// + protected override void OnInitialized() + { + ContextNullException.ThrowIfNull( Context, nameof( LumexCardHeader ) ); + } +} diff --git a/src/LumexUI/Components/Checkbox/CheckboxGroupSlots.cs b/src/LumexUI/Components/Checkbox/CheckboxGroupSlots.cs index 881e89f1..88685691 100644 --- a/src/LumexUI/Components/Checkbox/CheckboxGroupSlots.cs +++ b/src/LumexUI/Components/Checkbox/CheckboxGroupSlots.cs @@ -1,14 +1,37 @@ -using System.Diagnostics.CodeAnalysis; +// 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.Common; namespace LumexUI; +/// +/// Represents the slot names for the , +/// used to assign CSS classes to different parts of the component. +/// [ExcludeFromCodeCoverage] public class CheckboxGroupSlots : ISlot { - public string? Root { get; set; } - public string? Wrapper { get; set; } - public string? Label { get; set; } - public string? Description { get; set; } + /// + /// Gets or sets the CSS class for the root slot. + /// + public string? Root { get; set; } + + /// + /// Gets or sets the CSS class for the wrapper slot. + /// + public string? Wrapper { get; set; } + + /// + /// Gets or sets the CSS class for the label slot. + /// + public string? Label { get; set; } + + /// + /// Gets or sets the CSS class for the description slot. + /// + public string? Description { get; set; } } diff --git a/src/LumexUI/Components/Checkbox/CheckboxSlots.cs b/src/LumexUI/Components/Checkbox/CheckboxSlots.cs index e0566302..4b53b41b 100644 --- a/src/LumexUI/Components/Checkbox/CheckboxSlots.cs +++ b/src/LumexUI/Components/Checkbox/CheckboxSlots.cs @@ -1,14 +1,37 @@ -using System.Diagnostics.CodeAnalysis; +// 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.Common; namespace LumexUI; +/// +/// Represents the slot names for the , +/// used to assign CSS classes to different parts of the component. +/// [ExcludeFromCodeCoverage] public class CheckboxSlots : ISlot { - public string? Root { get; set; } - public string? Wrapper { get; set; } - public string? Icon { get; set; } - public string? Label { get; set; } + /// + /// Gets or sets the CSS class for the root slot. + /// + public string? Root { get; set; } + + /// + /// Gets or sets the CSS class for the wrapper slot. + /// + public string? Wrapper { get; set; } + + /// + /// Gets or sets the CSS class for the icon slot. + /// + public string? Icon { get; set; } + + /// + /// Gets or sets the CSS class for the label slot. + /// + public string? Label { get; set; } } diff --git a/src/LumexUI/Components/Checkbox/LumexCheckbox.razor.cs b/src/LumexUI/Components/Checkbox/LumexCheckbox.razor.cs index cb553080..24798761 100644 --- a/src/LumexUI/Components/Checkbox/LumexCheckbox.razor.cs +++ b/src/LumexUI/Components/Checkbox/LumexCheckbox.razor.cs @@ -9,78 +9,81 @@ namespace LumexUI; +/// +/// A component that represents a checkbox input. +/// public partial class LumexCheckbox : LumexBooleanInputBase, ISlotComponent { - /// - /// Gets or sets the border radius of the checkbox. - /// - /// - /// The default is - /// - [Parameter] public Radius Radius { get; set; } = Radius.Medium; - - /// - /// Gets or sets the icon to be used for indicating a checked state of the checkbox. - /// - [Parameter] public string? CheckIcon { get; set; } - - /// - /// Gets or sets the CSS class names for the checkbox slots. - /// - [Parameter] public CheckboxSlots? Classes { get; set; } - - [CascadingParameter] internal CheckboxGroupContext? Context { get; set; } - - private protected override string? RootClass => - TwMerge.Merge( Checkbox.GetStyles( this ) ); - - private string? WrapperClass => - TwMerge.Merge( Checkbox.GetWrapperStyles( this ) ); - - private string? IconClass => - TwMerge.Merge( Checkbox.GetIconStyles( this ) ); - - private string? LabelClass => - TwMerge.Merge( Checkbox.GetLabelStyles( this ) ); - - private readonly RenderFragment _renderCheckIcon; - - /// - /// Initializes a new instance of the . - /// - public LumexCheckbox() - { - _renderCheckIcon = RenderCheckIcon; - } - - /// - public override async Task SetParametersAsync( ParameterView parameters ) - { - await base.SetParametersAsync( parameters ); - - Color = parameters.TryGetValue( nameof( Color ), out var color ) - ? color - : Context?.Owner.Color ?? ThemeColor.Primary; - - Size = parameters.TryGetValue( nameof( Size ), out var size ) - ? size - : Context?.Owner.Size ?? Size.Medium; - - if( parameters.TryGetValue( nameof( Radius ), out var radius ) ) - { - Radius = radius; - } - else if( Context is not null ) - { - Radius = Context.Owner.Radius; - } - } - - /// - protected internal override bool GetDisabledState() => - Disabled || ( Context?.Owner.Disabled ?? false ); - - /// - protected internal override bool GetReadOnlyState() => - ReadOnly || ( Context?.Owner.ReadOnly ?? false ); + /// + /// Gets or sets the border radius of the checkbox. + /// + /// + /// The default is + /// + [Parameter] public Radius Radius { get; set; } = Radius.Medium; + + /// + /// Gets or sets the icon to be used for indicating a checked state of the checkbox. + /// + [Parameter] public string? CheckIcon { get; set; } + + /// + /// Gets or sets the CSS class names for the checkbox slots. + /// + [Parameter] public CheckboxSlots? Classes { get; set; } + + [CascadingParameter] internal CheckboxGroupContext? Context { get; set; } + + private protected override string? RootClass => + TwMerge.Merge( Checkbox.GetStyles( this ) ); + + private string? WrapperClass => + TwMerge.Merge( Checkbox.GetWrapperStyles( this ) ); + + private string? IconClass => + TwMerge.Merge( Checkbox.GetIconStyles( this ) ); + + private string? LabelClass => + TwMerge.Merge( Checkbox.GetLabelStyles( this ) ); + + private readonly RenderFragment _renderCheckIcon; + + /// + /// Initializes a new instance of the . + /// + public LumexCheckbox() + { + _renderCheckIcon = RenderCheckIcon; + } + + /// + public override async Task SetParametersAsync( ParameterView parameters ) + { + await base.SetParametersAsync( parameters ); + + Color = parameters.TryGetValue( nameof( Color ), out var color ) + ? color + : Context?.Owner.Color ?? ThemeColor.Primary; + + Size = parameters.TryGetValue( nameof( Size ), out var size ) + ? size + : Context?.Owner.Size ?? Size.Medium; + + if( parameters.TryGetValue( nameof( Radius ), out var radius ) ) + { + Radius = radius; + } + else if( Context is not null ) + { + Radius = Context.Owner.Radius; + } + } + + /// + protected internal override bool GetDisabledState() => + Disabled || ( Context?.Owner.Disabled ?? false ); + + /// + protected internal override bool GetReadOnlyState() => + ReadOnly || ( Context?.Owner.ReadOnly ?? false ); } diff --git a/src/LumexUI/Components/Checkbox/LumexCheckboxGroup.razor.cs b/src/LumexUI/Components/Checkbox/LumexCheckboxGroup.razor.cs index c5c21835..23d20196 100644 --- a/src/LumexUI/Components/Checkbox/LumexCheckboxGroup.razor.cs +++ b/src/LumexUI/Components/Checkbox/LumexCheckboxGroup.razor.cs @@ -9,83 +9,89 @@ namespace LumexUI; +/// +/// A component that represents a group of checkboxes. +/// public partial class LumexCheckboxGroup : LumexComponentBase, ISlotComponent { - /// - /// Gets or sets content to be rendered inside the checkbox group. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - /// - /// Gets or sets the label for the checkbox group. - /// - [Parameter] public string? Label { get; set; } - - /// - /// Gets or sets the description for the checkbox group. - /// - [Parameter] public string? Description { get; set; } - - /// - /// Gets or sets a value indicating whether the checkbox group is disabled. - /// - [Parameter] public bool Disabled { get; set; } - - /// - /// Gets or sets a value indicating whether the checkbox group is read-only. - /// - [Parameter] public bool ReadOnly { get; set; } - - /// - /// Gets or sets a color of the checkbox group. - /// - /// - /// The default is - /// - [Parameter] public ThemeColor Color { get; set; } = ThemeColor.Primary; - - /// - /// Gets or sets the border radius of the checkbox group. - /// - /// - /// The default is - /// - [Parameter] public Radius Radius { get; set; } = Radius.Medium; - - /// - /// Gets or sets the size of the checkbox group. - /// - /// - /// The default value is - /// - [Parameter] public Size Size { get; set; } = Size.Medium; - - /// - /// Gets or sets the CSS class names for the checkbox group slots. - /// - [Parameter] public CheckboxGroupSlots? Classes { get; set; } - - /// - /// Gets or sets the CSS class names for the checkboxes slots. - /// - [Parameter] public CheckboxSlots? CheckboxClasses { get; set; } - - private protected override string? RootClass => - TwMerge.Merge( CheckboxGroup.GetStyles( this ) ); - - private string? LabelClass => - TwMerge.Merge( CheckboxGroup.GetLabelStyles( this ) ); - - private string? WrapperClass => - TwMerge.Merge( CheckboxGroup.GetWrapperStyles( this ) ); - - private string? DescriptionClass => - TwMerge.Merge( CheckboxGroup.GetDescriptionStyles( this ) ); - - private readonly CheckboxGroupContext _context; - - public LumexCheckboxGroup() - { - _context = new CheckboxGroupContext( this ); - } + /// + /// Gets or sets content to be rendered inside the checkbox group. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Gets or sets the label for the checkbox group. + /// + [Parameter] public string? Label { get; set; } + + /// + /// Gets or sets the description for the checkbox group. + /// + [Parameter] public string? Description { get; set; } + + /// + /// Gets or sets a value indicating whether the checkbox group is disabled. + /// + [Parameter] public bool Disabled { get; set; } + + /// + /// Gets or sets a value indicating whether the checkbox group is read-only. + /// + [Parameter] public bool ReadOnly { get; set; } + + /// + /// Gets or sets a color of the checkbox group. + /// + /// + /// The default is + /// + [Parameter] public ThemeColor Color { get; set; } = ThemeColor.Primary; + + /// + /// Gets or sets the border radius of the checkbox group. + /// + /// + /// The default is + /// + [Parameter] public Radius Radius { get; set; } = Radius.Medium; + + /// + /// Gets or sets the size of the checkbox group. + /// + /// + /// The default value is + /// + [Parameter] public Size Size { get; set; } = Size.Medium; + + /// + /// Gets or sets the CSS class names for the checkbox group slots. + /// + [Parameter] public CheckboxGroupSlots? Classes { get; set; } + + /// + /// Gets or sets the CSS class names for the checkboxes slots. + /// + [Parameter] public CheckboxSlots? CheckboxClasses { get; set; } + + private protected override string? RootClass => + TwMerge.Merge( CheckboxGroup.GetStyles( this ) ); + + private string? LabelClass => + TwMerge.Merge( CheckboxGroup.GetLabelStyles( this ) ); + + private string? WrapperClass => + TwMerge.Merge( CheckboxGroup.GetWrapperStyles( this ) ); + + private string? DescriptionClass => + TwMerge.Merge( CheckboxGroup.GetDescriptionStyles( this ) ); + + private readonly CheckboxGroupContext _context; + + /// + /// Initializes a new instance of the . + /// + public LumexCheckboxGroup() + { + _context = new CheckboxGroupContext( this ); + } } \ No newline at end of file diff --git a/src/LumexUI/Components/Collapse/LumexCollapse.cs b/src/LumexUI/Components/Collapse/LumexCollapse.cs index 6be4f4b5..9353f04e 100644 --- a/src/LumexUI/Components/Collapse/LumexCollapse.cs +++ b/src/LumexUI/Components/Collapse/LumexCollapse.cs @@ -14,144 +14,147 @@ namespace LumexUI; +/// +/// A component that represents a collapsible container for expanding and collapsing content. +/// public class LumexCollapse : LumexComponentBase { - /// - /// Gets or sets content to be rendered inside the collapse. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - /// - /// Gets or sets a value indicating whether the collapse is expanded. - /// - [Parameter] public bool Expanded { get; set; } - - /// - /// Gets or sets the callback to be invoked when a collapse/expand transition ends. - /// - [Parameter] public EventCallback OnTransitionEnd { get; set; } - - internal CollapseState State { get; set; } - - private protected override string? RootClass => - TwMerge.Merge( Collapse.GetStyles( this ) ); - - private protected override string? RootStyle => - ElementStyle.Empty() - .Add( "height", $"{_height}px", when: State is CollapseState.Collapsing or CollapseState.Expanding ) - .Add( base.RootStyle ) - .ToString(); - - private int _height; - private bool _expanded; - private bool _isRendered; - private bool _heightUpdated; - - /// - protected override void BuildRenderTree( RenderTreeBuilder builder ) - { - builder.OpenElement( 0, As ); - builder.AddAttribute( 1, "class", RootClass ); - builder.AddAttribute( 2, "style", RootStyle ); - builder.AddAttribute( 3, "data-state", Utils.GetDataAttributeValue( State ) ); - builder.AddAttribute( 4, "ontransitionend", EventCallback.Factory.Create( this, OnTransitionEndAsync ) ); - builder.AddMultipleAttributes( 5, AdditionalAttributes ); - builder.AddElementReferenceCapture( 6, elementReference => ElementReference = elementReference ); - builder.AddContent( 7, ChildContent ); - builder.CloseElement(); - } - - /// - protected override void OnParametersSet() - { - // We don't want to call `UpdateHeightAsync` every time the component is rendered. - if( _expanded == Expanded ) - { - return; - } - - _expanded = Expanded; - - if( _isRendered ) - { - _heightUpdated = false; - SetTransitionState(); - } - else if( _expanded ) - { - State = CollapseState.Expanded; - } - } - - /// - protected override async Task OnAfterRenderAsync( bool firstRender ) - { - if( _heightUpdated ) - { - return; - } - - if( firstRender ) - { - _isRendered = true; - await UpdateHeightAsync(); - return; - } - - if( State is CollapseState.Expanding or CollapseState.Collapsing ) - { - await UpdateHeightAsync(); - StateHasChanged(); - } - } - - private async ValueTask UpdateHeightAsync() - { - try - { - _height = await ElementReference.GetScrollHeightAsync(); - - if( State is CollapseState.Collapsing ) - { - _height = 0; - } - - _heightUpdated = true; - } - catch( Exception ex ) when( ex is JSDisconnectedException or TaskCanceledException ) - { - _height = 0; - } - } - - private Task OnTransitionEndAsync() - { - if( State is CollapseState.Expanding ) - { - State = CollapseState.Expanded; - } - else if( State is CollapseState.Collapsing ) - { - State = CollapseState.Collapsed; - } - - return OnTransitionEnd.InvokeAsync(); - } - - private void SetTransitionState() - { - if( State is CollapseState.Expanded ) - { - State = CollapseState.Collapsing; - } - else if( State is CollapseState.Collapsed ) - { - State = CollapseState.Expanding; - } - } - - internal enum CollapseState - { - Collapsed, Expanding, Expanded, Collapsing - } + /// + /// Gets or sets content to be rendered inside the collapse. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Gets or sets a value indicating whether the collapse is expanded. + /// + [Parameter] public bool Expanded { get; set; } + + /// + /// Gets or sets the callback to be invoked when a collapse/expand transition ends. + /// + [Parameter] public EventCallback OnTransitionEnd { get; set; } + + internal CollapseState State { get; set; } + + private protected override string? RootClass => + TwMerge.Merge( Collapse.GetStyles( this ) ); + + private protected override string? RootStyle => + ElementStyle.Empty() + .Add( "height", $"{_height}px", when: State is CollapseState.Collapsing or CollapseState.Expanding ) + .Add( base.RootStyle ) + .ToString(); + + private int _height; + private bool _expanded; + private bool _isRendered; + private bool _heightUpdated; + + /// + protected override void BuildRenderTree( RenderTreeBuilder builder ) + { + builder.OpenElement( 0, As ); + builder.AddAttribute( 1, "class", RootClass ); + builder.AddAttribute( 2, "style", RootStyle ); + builder.AddAttribute( 3, "data-state", Utils.GetDataAttributeValue( State ) ); + builder.AddAttribute( 4, "ontransitionend", EventCallback.Factory.Create( this, OnTransitionEndAsync ) ); + builder.AddMultipleAttributes( 5, AdditionalAttributes ); + builder.AddElementReferenceCapture( 6, elementReference => ElementReference = elementReference ); + builder.AddContent( 7, ChildContent ); + builder.CloseElement(); + } + + /// + protected override void OnParametersSet() + { + // We don't want to call `UpdateHeightAsync` every time the component is rendered. + if( _expanded == Expanded ) + { + return; + } + + _expanded = Expanded; + + if( _isRendered ) + { + _heightUpdated = false; + SetTransitionState(); + } + else if( _expanded ) + { + State = CollapseState.Expanded; + } + } + + /// + protected override async Task OnAfterRenderAsync( bool firstRender ) + { + if( _heightUpdated ) + { + return; + } + + if( firstRender ) + { + _isRendered = true; + await UpdateHeightAsync(); + return; + } + + if( State is CollapseState.Expanding or CollapseState.Collapsing ) + { + await UpdateHeightAsync(); + StateHasChanged(); + } + } + + private async ValueTask UpdateHeightAsync() + { + try + { + _height = await ElementReference.GetScrollHeightAsync(); + + if( State is CollapseState.Collapsing ) + { + _height = 0; + } + + _heightUpdated = true; + } + catch( Exception ex ) when( ex is JSDisconnectedException or TaskCanceledException ) + { + _height = 0; + } + } + + private Task OnTransitionEndAsync() + { + if( State is CollapseState.Expanding ) + { + State = CollapseState.Expanded; + } + else if( State is CollapseState.Collapsing ) + { + State = CollapseState.Collapsed; + } + + return OnTransitionEnd.InvokeAsync(); + } + + private void SetTransitionState() + { + if( State is CollapseState.Expanded ) + { + State = CollapseState.Collapsing; + } + else if( State is CollapseState.Collapsed ) + { + State = CollapseState.Expanding; + } + } + + internal enum CollapseState + { + Collapsed, Expanding, Expanded, Collapsing + } } diff --git a/src/LumexUI/Components/Divider/LumexDivider.razor.cs b/src/LumexUI/Components/Divider/LumexDivider.razor.cs index f43e8609..d33464df 100644 --- a/src/LumexUI/Components/Divider/LumexDivider.razor.cs +++ b/src/LumexUI/Components/Divider/LumexDivider.razor.cs @@ -9,18 +9,21 @@ namespace LumexUI; +/// +/// A component that represents a divider for separating content. +/// public partial class LumexDivider : LumexComponentBase { - /// - /// Gets or sets a value defining the divider's orientation. - /// - /// - /// Default value is - /// - [Parameter] public Orientation Orientation { get; set; } + /// + /// Gets or sets a value defining the divider's orientation. + /// + /// + /// Default value is + /// + [Parameter] public Orientation Orientation { get; set; } - private protected override string? RootClass => - TwMerge.Merge( Divider.GetStyles( this ) ); + private protected override string? RootClass => + TwMerge.Merge( Divider.GetStyles( this ) ); - private new string As => Orientation is Orientation.Horizontal ? "hr" : "div"; + private new string As => Orientation is Orientation.Horizontal ? "hr" : "div"; } \ No newline at end of file diff --git a/src/LumexUI/Components/Icon/LumexIcon.razor.cs b/src/LumexUI/Components/Icon/LumexIcon.razor.cs index 2cd98e20..6929a8ce 100644 --- a/src/LumexUI/Components/Icon/LumexIcon.razor.cs +++ b/src/LumexUI/Components/Icon/LumexIcon.razor.cs @@ -11,50 +11,53 @@ namespace LumexUI; +/// +/// A component that represents an icon. +/// public partial class LumexIcon : LumexComponentBase { - /// - /// Gets or sets content to be rendered inside the icon. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - /// - /// Gets or sets the name or path of the icon to be displayed. - /// - [Parameter] public string? Icon { get; set; } - - /// - /// Gets or sets the dimensions of the icon. - /// - /// - /// The default is 24x24 - /// - [Parameter] public Dimensions Size { get; set; } = new( "24" ); - - /// - /// Gets or sets a color of the icon. - /// - [Parameter] public ThemeColor Color { get; set; } - - private protected override string? RootClass => - TwMerge.Merge( Styles.Icon.GetStyles( this ) ); - - private string? FontIconStyle => ElementStyle.Empty() - .Add( "font-size", $"{Size.W}px", when: Size.W == Size.H ) - .Add( RootStyle ) - .ToString(); - - [MemberNotNullWhen( true, nameof( Icon ) )] - private bool IsSvgIcon => !string.IsNullOrEmpty( Icon ) && Icon.Trim().StartsWith( '<' ); - - /// - protected override void OnParametersSet() - { - if( ChildContent is not null && ( Size.W != Size.H ) ) - { - throw new InvalidOperationException( - $"{GetType()} requires equal width and height dimensions for " + - $"{nameof( Size )} if used as a font icon." ); - } - } + /// + /// Gets or sets content to be rendered inside the icon. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Gets or sets the name or path of the icon to be displayed. + /// + [Parameter] public string? Icon { get; set; } + + /// + /// Gets or sets the dimensions of the icon. + /// + /// + /// The default is 24x24 + /// + [Parameter] public Dimensions Size { get; set; } = new( "24" ); + + /// + /// Gets or sets a color of the icon. + /// + [Parameter] public ThemeColor Color { get; set; } + + private protected override string? RootClass => + TwMerge.Merge( Styles.Icon.GetStyles( this ) ); + + private string? FontIconStyle => ElementStyle.Empty() + .Add( "font-size", $"{Size.W}px", when: Size.W == Size.H ) + .Add( RootStyle ) + .ToString(); + + [MemberNotNullWhen( true, nameof( Icon ) )] + private bool IsSvgIcon => !string.IsNullOrEmpty( Icon ) && Icon.Trim().StartsWith( '<' ); + + /// + protected override void OnParametersSet() + { + if( ChildContent is not null && ( Size.W != Size.H ) ) + { + throw new InvalidOperationException( + $"{GetType()} requires equal width and height dimensions for " + + $"{nameof( Size )} if used as a font icon." ); + } + } } diff --git a/src/LumexUI/Components/Link/LumexLink.razor.cs b/src/LumexUI/Components/Link/LumexLink.razor.cs index c6d1c90a..ce5e2e96 100644 --- a/src/LumexUI/Components/Link/LumexLink.razor.cs +++ b/src/LumexUI/Components/Link/LumexLink.razor.cs @@ -10,90 +10,93 @@ namespace LumexUI; +/// +/// A component that represents a navigation link. +/// public partial class LumexLink : LumexComponentBase { - /// - /// Gets or sets content to be rendered inside the link. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - /// - /// Gets or sets a value representing the URL of the link. - /// - [Parameter] public string Href { get; set; } = "#"; - - /// - /// Gets or sets a value representing the URL matching behavior. - /// - /// - /// The default value is - /// - [Parameter] public NavLinkMatch Match { get; set; } = NavLinkMatch.All; - - /// - /// Gets or sets a color of the link. - /// - /// - /// The default value is - /// - [Parameter] public ThemeColor Color { get; set; } = ThemeColor.Primary; - - /// - /// Gets or sets the underline style for the link. - /// - /// - /// Default value is - /// - [Parameter] public Underline Underline { get; set; } - - /// - /// Gets or sets a value indicating whether the link is disabled. - /// - [Parameter] public bool Disabled { get; set; } - - /// - /// Gets or sets a value indicating whether the link should open in the new tab. - /// - /// - /// Sets target to `_blank` and rel to `noopener noreferrer`. - /// - [Parameter] public bool External { get; set; } - - private protected override string? RootClass => - TwMerge.Merge( Link.GetStyles( this ) ); - - private IReadOnlyDictionary Attributes - { - get - { - var attributes = new Dictionary() - { - ["href"] = Href - }; - - if( External ) - { - attributes["target"] = "_blank"; - attributes["rel"] = "noopener noreferrer"; - } - - if( AdditionalAttributes is not null ) - { - foreach( var attribute in AdditionalAttributes ) - { - attributes[attribute.Key] = attribute.Value; - } - } - - return attributes; - } - } - - /// - /// Initializes a new instance of the . - /// - public LumexLink() - { - As = "a"; - } + /// + /// Gets or sets content to be rendered inside the link. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Gets or sets a value representing the URL of the link. + /// + [Parameter] public string Href { get; set; } = "#"; + + /// + /// Gets or sets a value representing the URL matching behavior. + /// + /// + /// The default value is + /// + [Parameter] public NavLinkMatch Match { get; set; } = NavLinkMatch.All; + + /// + /// Gets or sets a color of the link. + /// + /// + /// The default value is + /// + [Parameter] public ThemeColor Color { get; set; } = ThemeColor.Primary; + + /// + /// Gets or sets the underline style for the link. + /// + /// + /// Default value is + /// + [Parameter] public Underline Underline { get; set; } + + /// + /// Gets or sets a value indicating whether the link is disabled. + /// + [Parameter] public bool Disabled { get; set; } + + /// + /// Gets or sets a value indicating whether the link should open in the new tab. + /// + /// + /// Sets target to `_blank` and rel to `noopener noreferrer`. + /// + [Parameter] public bool External { get; set; } + + private protected override string? RootClass => + TwMerge.Merge( Link.GetStyles( this ) ); + + private IReadOnlyDictionary Attributes + { + get + { + var attributes = new Dictionary() + { + ["href"] = Href + }; + + if( External ) + { + attributes["target"] = "_blank"; + attributes["rel"] = "noopener noreferrer"; + } + + if( AdditionalAttributes is not null ) + { + foreach( var attribute in AdditionalAttributes ) + { + attributes[attribute.Key] = attribute.Value; + } + } + + return attributes; + } + } + + /// + /// Initializes a new instance of the . + /// + public LumexLink() + { + As = "a"; + } } diff --git a/src/LumexUI/Components/Navbar/NavbarSlots.cs b/src/LumexUI/Components/Navbar/NavbarSlots.cs index 78551c83..39da6848 100644 --- a/src/LumexUI/Components/Navbar/NavbarSlots.cs +++ b/src/LumexUI/Components/Navbar/NavbarSlots.cs @@ -8,16 +8,55 @@ namespace LumexUI; +/// +/// Represents the slot names for the , +/// used to assign CSS classes to different parts of the component. +/// [ExcludeFromCodeCoverage] public class NavbarSlots : ISlot { + /// + /// Gets or sets the CSS class for the root slot. + /// public string? Root { get; set; } + + /// + /// Gets or sets the CSS class for the wrapper slot. + /// public string? Wrapper { get; set; } + + /// + /// Gets or sets the CSS class for the brand slot. + /// public string? Brand { get; set; } + + /// + /// Gets or sets the CSS class for the content slot. + /// public string? Content { get; set; } + + /// + /// Gets or sets the CSS class for the item slot. + /// public string? Item { get; set; } + + /// + /// Gets or sets the CSS class for the toggle slot. + /// public string? Toggle { get; set; } + + /// + /// Gets or sets the CSS class for the toggle icon slot. + /// public string? ToggleIcon { get; set; } + + /// + /// Gets or sets the CSS class for the menu slot. + /// public string? Menu { get; set; } + + /// + /// Gets or sets the CSS class for the menu item slot. + /// public string? MenuItem { get; set; } } diff --git a/src/LumexUI/Components/Popover/PopoverSlots.cs b/src/LumexUI/Components/Popover/PopoverSlots.cs index 6dd3b2d1..2965819a 100644 --- a/src/LumexUI/Components/Popover/PopoverSlots.cs +++ b/src/LumexUI/Components/Popover/PopoverSlots.cs @@ -8,11 +8,30 @@ namespace LumexUI; +/// +/// Represents the slot names for the , +/// used to assign CSS classes to different parts of the component. +/// [ExcludeFromCodeCoverage] public class PopoverSlots : ISlot { + /// + /// Gets or sets the CSS class for the root slot. + /// public string? Root { get; set; } + + /// + /// Gets or sets the CSS class for the trigger slot. + /// public string? Trigger { get; set; } + + /// + /// Gets or sets the CSS class for the content slot. + /// public string? Content { get; set; } + + /// + /// Gets or sets the CSS class for the arrow slot. + /// public string? Arrow { get; set; } } diff --git a/src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs b/src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs index dd04f69a..ed95aa84 100644 --- a/src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs +++ b/src/LumexUI/Components/Providers/LumexThemeProvider.razor.cs @@ -12,94 +12,97 @@ namespace LumexUI; +/// +/// A component that provides theme configuration. +/// public partial class LumexThemeProvider : ComponentBase { - private const string Prefix = "lumex"; - - /// - /// Gets or sets the configuration of the theme. - /// - [Parameter] public LumexTheme Theme { get; set; } - - /// - /// Initializes a new instance of the . - /// - public LumexThemeProvider() - { - Theme = new(); - } - - private string GenerateTheme( ThemeConfig theme ) where TColors : ThemeColors, new() - { - var cssSelector = $"[data-theme={theme.Type.ToDescription()}]"; - - if( theme.Type == Theme.DefaultTheme ) - { - cssSelector = $":root, {cssSelector}"; - } - - var sb = new StringBuilder(); - sb.AppendLine( $"{cssSelector} {{" ); - - // Colors - var themeColors = GetThemeColorsDict( theme.Colors ); - - foreach( var color in themeColors ) - { - foreach( var scale in color.Value ) - { - var scaleKey = scale.Key == "default" ? "" : $"-{scale.Key}"; - var scaleValue = ColorUtils.HexToHsl( scale.Value ); - - sb.AppendLine( $"--{Prefix}-{color.Key}{scaleKey}: {scaleValue};" ); - } - } - - // Layout - sb.AppendLine( $"--{Prefix}-font-sans: {theme.Layout.FontFamily?.Sans};" ); - sb.AppendLine( $"--{Prefix}-font-mono: {theme.Layout.FontFamily?.Mono};" ); - sb.AppendLine( $"--{Prefix}-font-size-tiny: {theme.Layout.FontSize.Xs};" ); - sb.AppendLine( $"--{Prefix}-font-size-small: {theme.Layout.FontSize.Sm};" ); - sb.AppendLine( $"--{Prefix}-font-size-medium: {theme.Layout.FontSize.Md};" ); - sb.AppendLine( $"--{Prefix}-font-size-large: {theme.Layout.FontSize.Lg};" ); - sb.AppendLine( $"--{Prefix}-line-height-tiny: {theme.Layout.LineHeight.Xs};" ); - sb.AppendLine( $"--{Prefix}-line-height-small: {theme.Layout.LineHeight.Sm};" ); - sb.AppendLine( $"--{Prefix}-line-height-medium: {theme.Layout.LineHeight.Md};" ); - sb.AppendLine( $"--{Prefix}-line-height-large: {theme.Layout.LineHeight.Lg};" ); - sb.AppendLine( $"--{Prefix}-radius-small: {theme.Layout.Radius.Sm};" ); - sb.AppendLine( $"--{Prefix}-radius-medium: {theme.Layout.Radius.Md};" ); - sb.AppendLine( $"--{Prefix}-radius-large: {theme.Layout.Radius.Lg};" ); - sb.AppendLine( $"--{Prefix}-box-shadow-small: {theme.Layout.Shadow.Sm};" ); - sb.AppendLine( $"--{Prefix}-box-shadow-medium: {theme.Layout.Shadow.Md};" ); - sb.AppendLine( $"--{Prefix}-box-shadow-large: {theme.Layout.Shadow.Lg};" ); - sb.AppendLine( CultureInfo.InvariantCulture, $"--{Prefix}-divider-opacity: {theme.Layout.DividerOpacity};" ); - sb.AppendLine( CultureInfo.InvariantCulture, $"--{Prefix}-disabled-opacity: {theme.Layout.DisabledOpacity};" ); - sb.AppendLine( CultureInfo.InvariantCulture, $"--{Prefix}-focus-opacity: {theme.Layout.FocusOpacity};" ); - sb.AppendLine( CultureInfo.InvariantCulture, $"--{Prefix}-hover-opacity: {theme.Layout.HoverOpacity};" ); - - sb.AppendLine( "}" ); - return sb.ToString(); - } - - private static Dictionary GetThemeColorsDict( ThemeColors colors ) - { - return new() - { - ["background"] = colors.Background, - ["foreground"] = colors.Foreground, - ["overlay"] = colors.Overlay, - ["focus"] = colors.Focus, - ["divider"] = colors.Divider, - ["surface1"] = colors.Surface1, - ["surface2"] = colors.Surface2, - ["surface3"] = colors.Surface3, - ["default"] = colors.Default, - ["primary"] = colors.Primary, - ["secondary"] = colors.Secondary, - ["success"] = colors.Success, - ["warning"] = colors.Warning, - ["danger"] = colors.Danger, - ["info"] = colors.Info, - }; - } + private const string Prefix = "lumex"; + + /// + /// Gets or sets the configuration of the theme. + /// + [Parameter] public LumexTheme Theme { get; set; } + + /// + /// Initializes a new instance of the . + /// + public LumexThemeProvider() + { + Theme = new(); + } + + private string GenerateTheme( ThemeConfig theme ) where TColors : ThemeColors, new() + { + var cssSelector = $"[data-theme={theme.Type.ToDescription()}]"; + + if( theme.Type == Theme.DefaultTheme ) + { + cssSelector = $":root, {cssSelector}"; + } + + var sb = new StringBuilder(); + sb.AppendLine( $"{cssSelector} {{" ); + + // Colors + var themeColors = GetThemeColorsDict( theme.Colors ); + + foreach( var color in themeColors ) + { + foreach( var scale in color.Value ) + { + var scaleKey = scale.Key == "default" ? "" : $"-{scale.Key}"; + var scaleValue = ColorUtils.HexToHsl( scale.Value ); + + sb.AppendLine( $"--{Prefix}-{color.Key}{scaleKey}: {scaleValue};" ); + } + } + + // Layout + sb.AppendLine( $"--{Prefix}-font-sans: {theme.Layout.FontFamily?.Sans};" ); + sb.AppendLine( $"--{Prefix}-font-mono: {theme.Layout.FontFamily?.Mono};" ); + sb.AppendLine( $"--{Prefix}-font-size-tiny: {theme.Layout.FontSize.Xs};" ); + sb.AppendLine( $"--{Prefix}-font-size-small: {theme.Layout.FontSize.Sm};" ); + sb.AppendLine( $"--{Prefix}-font-size-medium: {theme.Layout.FontSize.Md};" ); + sb.AppendLine( $"--{Prefix}-font-size-large: {theme.Layout.FontSize.Lg};" ); + sb.AppendLine( $"--{Prefix}-line-height-tiny: {theme.Layout.LineHeight.Xs};" ); + sb.AppendLine( $"--{Prefix}-line-height-small: {theme.Layout.LineHeight.Sm};" ); + sb.AppendLine( $"--{Prefix}-line-height-medium: {theme.Layout.LineHeight.Md};" ); + sb.AppendLine( $"--{Prefix}-line-height-large: {theme.Layout.LineHeight.Lg};" ); + sb.AppendLine( $"--{Prefix}-radius-small: {theme.Layout.Radius.Sm};" ); + sb.AppendLine( $"--{Prefix}-radius-medium: {theme.Layout.Radius.Md};" ); + sb.AppendLine( $"--{Prefix}-radius-large: {theme.Layout.Radius.Lg};" ); + sb.AppendLine( $"--{Prefix}-box-shadow-small: {theme.Layout.Shadow.Sm};" ); + sb.AppendLine( $"--{Prefix}-box-shadow-medium: {theme.Layout.Shadow.Md};" ); + sb.AppendLine( $"--{Prefix}-box-shadow-large: {theme.Layout.Shadow.Lg};" ); + sb.AppendLine( CultureInfo.InvariantCulture, $"--{Prefix}-divider-opacity: {theme.Layout.DividerOpacity};" ); + sb.AppendLine( CultureInfo.InvariantCulture, $"--{Prefix}-disabled-opacity: {theme.Layout.DisabledOpacity};" ); + sb.AppendLine( CultureInfo.InvariantCulture, $"--{Prefix}-focus-opacity: {theme.Layout.FocusOpacity};" ); + sb.AppendLine( CultureInfo.InvariantCulture, $"--{Prefix}-hover-opacity: {theme.Layout.HoverOpacity};" ); + + sb.AppendLine( "}" ); + return sb.ToString(); + } + + private static Dictionary GetThemeColorsDict( ThemeColors colors ) + { + return new() + { + ["background"] = colors.Background, + ["foreground"] = colors.Foreground, + ["overlay"] = colors.Overlay, + ["focus"] = colors.Focus, + ["divider"] = colors.Divider, + ["surface1"] = colors.Surface1, + ["surface2"] = colors.Surface2, + ["surface3"] = colors.Surface3, + ["default"] = colors.Default, + ["primary"] = colors.Primary, + ["secondary"] = colors.Secondary, + ["success"] = colors.Success, + ["warning"] = colors.Warning, + ["danger"] = colors.Danger, + ["info"] = colors.Info, + }; + } } \ No newline at end of file diff --git a/src/LumexUI/Components/Radio/LumexRadio.razor.cs b/src/LumexUI/Components/Radio/LumexRadio.razor.cs index fb5bc666..1ea5de11 100644 --- a/src/LumexUI/Components/Radio/LumexRadio.razor.cs +++ b/src/LumexUI/Components/Radio/LumexRadio.razor.cs @@ -9,124 +9,128 @@ namespace LumexUI; +/// +/// A component that represents a radio button within the . +/// +/// The type of the selected value. [CompositionComponent( typeof( LumexRadioGroup<> ) )] public partial class LumexRadio : LumexComponentBase, ISlotComponent { - /// - /// Gets or sets a value indicating whether the input is disabled. - /// - [Parameter] public bool Disabled { get; set; } - - /// - /// Gets or sets a value indicating whether the input is read-only. - /// - [Parameter] public bool ReadOnly { get; set; } - - /// - /// Gets or sets a value indicating whether the input is required. - /// - [Parameter] public bool Required { get; set; } - - /// - /// Gets or sets a value indicating whether the input is invalid. - /// - [Parameter] public bool Invalid { get; set; } - - /// - /// Gets or sets the CSS class names for the checkbox slots. - /// - [Parameter] public RadioSlots? Classes { get; set; } - - /// - /// Gets or sets a color of the radio button. - /// - /// - /// The default is - /// - [Parameter] public ThemeColor Color { get; set; } = ThemeColor.Primary; - - /// - /// Gets or sets the size of the radio button. - /// - /// - /// The default value is - /// - [Parameter] public Size Size { get; set; } = Size.Medium; - - /// - /// Gets or sets the description for this particular option. - /// - [Parameter] public string? Description { get; set; } - - /// - /// Gets or sets the value of this input. - /// - [Parameter] public TValue? Value { get; set; } - - /// - /// Gets or sets content to be rendered inside the input. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - [CascadingParameter( Name = "Context" )] internal RadioGroupContext Context { get; set; } = default!; - - private protected override string? RootClass => - TwMerge.Merge( Radio.GetStyles( this ) ); - - private string? ControlWrapperClass => - TwMerge.Merge( Radio.GetControlWrapperStyles( this ) ); - - private string? ControlClass => - TwMerge.Merge( Radio.GetControlStyles( this ) ); - - private string? LabelWrapperClass => - TwMerge.Merge( Radio.GetLabelWrapperStyles( this ) ); - - private string? LabelClass => - TwMerge.Merge( Radio.GetLabelStyles( this ) ); - - private string? DescriptionClass => - TwMerge.Merge( Radio.GetDescriptionStyles( this ) ); - - /// - public override async Task SetParametersAsync( ParameterView parameters ) - { - await base.SetParametersAsync( parameters ); - - Color = parameters.TryGetValue( nameof( Color ), out var color ) - ? color - : Context.Owner.Color; - - Size = parameters.TryGetValue( nameof( Size ), out var size ) - ? size - : Context.Owner.Size; - } - - /// - /// Gets the disabled state of the input. - /// Derived classes can override this to determine the input's disabled state. - /// - /// A value indicating whether the input is disabled. - protected internal bool GetDisabledState() => Disabled || Context.Owner.Disabled; - - /// - /// Gets the readonly state of the input. - /// Derived classes can override this to determine the input's readonly state. - /// - /// A value indicating whether the input is readonly. - protected internal bool GetReadOnlyState() => ReadOnly || Context.Owner.ReadOnly; - - /// - /// Indicates whether this radio button is selected. - /// Derived classes can override this to determine the input's selected state. - /// - /// true if the of this matches - /// the CurrentValue property in the parent . Otherwise false. - protected internal bool GetSelectedState() => Context.CurrentValue?.Equals( Value ) ?? false; - - /// - protected override void OnInitialized() - { - ContextNullException.ThrowIfNull( Context, nameof( LumexRadio ) ); - } + /// + /// Gets or sets a value indicating whether the input is disabled. + /// + [Parameter] public bool Disabled { get; set; } + + /// + /// Gets or sets a value indicating whether the input is read-only. + /// + [Parameter] public bool ReadOnly { get; set; } + + /// + /// Gets or sets a value indicating whether the input is required. + /// + [Parameter] public bool Required { get; set; } + + /// + /// Gets or sets a value indicating whether the input is invalid. + /// + [Parameter] public bool Invalid { get; set; } + + /// + /// Gets or sets the CSS class names for the checkbox slots. + /// + [Parameter] public RadioSlots? Classes { get; set; } + + /// + /// Gets or sets a color of the radio button. + /// + /// + /// The default is + /// + [Parameter] public ThemeColor Color { get; set; } = ThemeColor.Primary; + + /// + /// Gets or sets the size of the radio button. + /// + /// + /// The default value is + /// + [Parameter] public Size Size { get; set; } = Size.Medium; + + /// + /// Gets or sets the description for this particular option. + /// + [Parameter] public string? Description { get; set; } + + /// + /// Gets or sets the value of this input. + /// + [Parameter] public TValue? Value { get; set; } + + /// + /// Gets or sets content to be rendered inside the input. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + [CascadingParameter( Name = "Context" )] internal RadioGroupContext Context { get; set; } = default!; + + private protected override string? RootClass => + TwMerge.Merge( Radio.GetStyles( this ) ); + + private string? ControlWrapperClass => + TwMerge.Merge( Radio.GetControlWrapperStyles( this ) ); + + private string? ControlClass => + TwMerge.Merge( Radio.GetControlStyles( this ) ); + + private string? LabelWrapperClass => + TwMerge.Merge( Radio.GetLabelWrapperStyles( this ) ); + + private string? LabelClass => + TwMerge.Merge( Radio.GetLabelStyles( this ) ); + + private string? DescriptionClass => + TwMerge.Merge( Radio.GetDescriptionStyles( this ) ); + + /// + public override async Task SetParametersAsync( ParameterView parameters ) + { + await base.SetParametersAsync( parameters ); + + Color = parameters.TryGetValue( nameof( Color ), out var color ) + ? color + : Context.Owner.Color; + + Size = parameters.TryGetValue( nameof( Size ), out var size ) + ? size + : Context.Owner.Size; + } + + /// + /// Gets the disabled state of the input. + /// Derived classes can override this to determine the input's disabled state. + /// + /// A value indicating whether the input is disabled. + protected internal bool GetDisabledState() => Disabled || Context.Owner.Disabled; + + /// + /// Gets the readonly state of the input. + /// Derived classes can override this to determine the input's readonly state. + /// + /// A value indicating whether the input is readonly. + protected internal bool GetReadOnlyState() => ReadOnly || Context.Owner.ReadOnly; + + /// + /// Indicates whether this radio button is selected. + /// Derived classes can override this to determine the input's selected state. + /// + /// true if the of this matches + /// the CurrentValue property in the parent . Otherwise false. + protected internal bool GetSelectedState() => Context.CurrentValue?.Equals( Value ) ?? false; + + /// + protected override void OnInitialized() + { + ContextNullException.ThrowIfNull( Context, nameof( LumexRadio ) ); + } } \ No newline at end of file diff --git a/src/LumexUI/Components/Radio/LumexRadioGroup.razor.cs b/src/LumexUI/Components/Radio/LumexRadioGroup.razor.cs index c8fcded5..b5b88471 100644 --- a/src/LumexUI/Components/Radio/LumexRadioGroup.razor.cs +++ b/src/LumexUI/Components/Radio/LumexRadioGroup.razor.cs @@ -12,177 +12,183 @@ namespace LumexUI; -[CascadingTypeParameter(nameof(TValue))] -public partial class LumexRadioGroup : LumexInputBase, ISlotComponent, IRadioGroupValueProvider +/// +/// A component that represents a group of radio buttons. +/// +/// The type of the selected value. +[CascadingTypeParameter( nameof( TValue ) )] +public partial class LumexRadioGroup : LumexInputBase, + ISlotComponent, + IRadioGroupValueProvider { - /// - /// Gets or sets content to be rendered inside the radio group. - /// - [Parameter] public RenderFragment? ChildContent { get; set; } - - /// - /// Gets or sets the name of the group. - /// - [Parameter] public string? Name { get; set; } - - /// - /// Gets or sets the label for the radio group. - /// - [Parameter] public string? Label { get; set; } - - /// - /// Gets or sets the description for the radio group. - /// - [Parameter] public string? Description { get; set; } - - /// - /// Gets or sets the orientation of the radio group. - /// - /// - /// The default value is - /// - [Parameter] public Orientation Orientation { get; set; } = Orientation.Vertical; - - /// - /// Gets or sets the CSS class names for the radio group slots. - /// - [Parameter] public RadioGroupSlots? Classes { get; set; } - - /// - /// Gets or sets the CSS class names for the radio button slots. - /// - [Parameter] public RadioSlots? RadioClasses { get; set; } - - /// - TValue? IRadioGroupValueProvider.CurrentValue => Value; - - private protected override string? RootClass => - TwMerge.Merge( RadioGroup.GetStyles( this ) ); - - private string? LabelClass => - TwMerge.Merge( RadioGroup.GetLabelStyles( this ) ); - - private string? WrapperClass => - TwMerge.Merge( RadioGroup.GetWrapperStyles( this ) ); - - private string? DescriptionClass => - TwMerge.Merge( RadioGroup.GetDescriptionStyles( this ) ); - - private readonly string _defaultGroupName = Guid.NewGuid().ToString("N"); - - private RadioGroupContext? _context; - - /// - public override async Task SetParametersAsync( ParameterView parameters ) - { - Color = parameters.TryGetValue( nameof( Color ), out var color ) - ? color - : ThemeColor.Primary; - - Size = parameters.TryGetValue( nameof( Size ), out var size ) - ? size - : Size.Medium; - - // On the first render, we can instantiate the InputRadioContext - if (_context is null) - { - var changeEventCallback = EventCallback.Factory.Create( this, OnValueChangeAsync ); - _context = new RadioGroupContext(this, changeEventCallback); - } - - await base.SetParametersAsync( parameters ); - } - - /// - protected override void OnParametersSet() - { - // Prefer the explicitly-specified group name over anything else. - // Otherwise, just use a GUID to disambiguate this group's radio inputs from any others on the page. - _context!.GroupName = !string.IsNullOrEmpty(Name) ? Name : _defaultGroupName; - - base.OnParametersSet(); - } - - /// - protected override ValueTask SetValidationMessageAsync() - { - return ValueTask.CompletedTask; - } - - /// - protected override bool TryParseValueFromString( string? value, [MaybeNullWhen(false)] out TValue result ) - { - try - { - // We special-case bool values because BindConverter reserves bool conversion for conditional attributes. - if (typeof(TValue) == typeof(bool)) - { - if (TryConvertToBool(value, out result)) - { - return true; - } - } - else if (typeof(TValue) == typeof(bool?)) - { - if (TryConvertToNullableBool(value, out result)) - { - return true; - } - } - else if (BindConverter.TryConvertTo(value, CultureInfo.CurrentCulture, out var parsedValue)) - { - result = parsedValue; - return true; - } - - result = default; - - return false; - } - catch (InvalidOperationException ex) - { - throw new InvalidOperationException($"{Label} does not support the type '{typeof(TValue)}'.", ex); - } - } - - /// - /// Handles the change event asynchronously. - /// Derived classes can override this to specify custom behavior when the input's value changes. - /// - /// The change event arguments. - /// A representing the asynchronous operation. - protected async Task OnValueChangeAsync( ChangeEventArgs args ) - { - if( Disabled || ReadOnly ) - { - return; - } - - await SetCurrentValueAsStringAsync( args.Value?.ToString() ); - } - - private static bool TryConvertToBool(string? value, out TValue result) - { - if (bool.TryParse(value, out var @bool)) - { - result = (TValue)(object)@bool; - return true; - } - - result = default!; - return false; - } - - private static bool TryConvertToNullableBool(string? value, out TValue result) - { - // This is unlikely to be true because LumexInputBase.SetCurrentValueAsStringAsync - // should have already handled this case. - if (string.IsNullOrEmpty(value)) - { - result = default!; - return true; - } - - return TryConvertToBool(value, out result); - } + /// + /// Gets or sets content to be rendered inside the radio group. + /// + [Parameter] public RenderFragment? ChildContent { get; set; } + + /// + /// Gets or sets the name of the group. + /// + [Parameter] public string? Name { get; set; } + + /// + /// Gets or sets the label for the radio group. + /// + [Parameter] public string? Label { get; set; } + + /// + /// Gets or sets the description for the radio group. + /// + [Parameter] public string? Description { get; set; } + + /// + /// Gets or sets the orientation of the radio group. + /// + /// + /// The default value is + /// + [Parameter] public Orientation Orientation { get; set; } = Orientation.Vertical; + + /// + /// Gets or sets the CSS class names for the radio group slots. + /// + [Parameter] public RadioGroupSlots? Classes { get; set; } + + /// + /// Gets or sets the CSS class names for the radio button slots. + /// + [Parameter] public RadioSlots? RadioClasses { get; set; } + + /// + TValue? IRadioGroupValueProvider.CurrentValue => Value; + + private protected override string? RootClass => + TwMerge.Merge( RadioGroup.GetStyles( this ) ); + + private string? LabelClass => + TwMerge.Merge( RadioGroup.GetLabelStyles( this ) ); + + private string? WrapperClass => + TwMerge.Merge( RadioGroup.GetWrapperStyles( this ) ); + + private string? DescriptionClass => + TwMerge.Merge( RadioGroup.GetDescriptionStyles( this ) ); + + private readonly string _defaultGroupName = Guid.NewGuid().ToString( "N" ); + + private RadioGroupContext? _context; + + /// + public override async Task SetParametersAsync( ParameterView parameters ) + { + Color = parameters.TryGetValue( nameof( Color ), out var color ) + ? color + : ThemeColor.Primary; + + Size = parameters.TryGetValue( nameof( Size ), out var size ) + ? size + : Size.Medium; + + // On the first render, we can instantiate the InputRadioContext + if( _context is null ) + { + var changeEventCallback = EventCallback.Factory.Create( this, OnValueChangeAsync ); + _context = new RadioGroupContext( this, changeEventCallback ); + } + + await base.SetParametersAsync( parameters ); + } + + /// + protected override void OnParametersSet() + { + // Prefer the explicitly-specified group name over anything else. + // Otherwise, just use a GUID to disambiguate this group's radio inputs from any others on the page. + _context!.GroupName = !string.IsNullOrEmpty( Name ) ? Name : _defaultGroupName; + + base.OnParametersSet(); + } + + /// + protected override ValueTask SetValidationMessageAsync() + { + return ValueTask.CompletedTask; + } + + /// + protected override bool TryParseValueFromString( string? value, [MaybeNullWhen( false )] out TValue result ) + { + try + { + // We special-case bool values because BindConverter reserves bool conversion for conditional attributes. + if( typeof( TValue ) == typeof( bool ) ) + { + if( TryConvertToBool( value, out result ) ) + { + return true; + } + } + else if( typeof( TValue ) == typeof( bool? ) ) + { + if( TryConvertToNullableBool( value, out result ) ) + { + return true; + } + } + else if( BindConverter.TryConvertTo( value, CultureInfo.CurrentCulture, out var parsedValue ) ) + { + result = parsedValue; + return true; + } + + result = default; + + return false; + } + catch( InvalidOperationException ex ) + { + throw new InvalidOperationException( $"{Label} does not support the type '{typeof( TValue )}'.", ex ); + } + } + + /// + /// Handles the change event asynchronously. + /// Derived classes can override this to specify custom behavior when the input's value changes. + /// + /// The change event arguments. + /// A representing the asynchronous operation. + protected async Task OnValueChangeAsync( ChangeEventArgs args ) + { + if( Disabled || ReadOnly ) + { + return; + } + + await SetCurrentValueAsStringAsync( args.Value?.ToString() ); + } + + private static bool TryConvertToBool( string? value, out TValue result ) + { + if( bool.TryParse( value, out var @bool ) ) + { + result = (TValue)(object)@bool; + return true; + } + + result = default!; + return false; + } + + private static bool TryConvertToNullableBool( string? value, out TValue result ) + { + // This is unlikely to be true because LumexInputBase.SetCurrentValueAsStringAsync + // should have already handled this case. + if( string.IsNullOrEmpty( value ) ) + { + result = default!; + return true; + } + + return TryConvertToBool( value, out result ); + } } diff --git a/src/LumexUI/Components/Radio/RadioGroupContext.cs b/src/LumexUI/Components/Radio/RadioGroupContext.cs index 98292fb2..eef530c7 100644 --- a/src/LumexUI/Components/Radio/RadioGroupContext.cs +++ b/src/LumexUI/Components/Radio/RadioGroupContext.cs @@ -1,43 +1,44 @@ -using LumexUI.Common; +// 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; using Microsoft.AspNetCore.Components; namespace LumexUI; -[CascadingTypeParameter(nameof(TValue))] +[CascadingTypeParameter( nameof( TValue ) )] internal sealed class RadioGroupContext( LumexRadioGroup owner, EventCallback changeEventCallback ) : IComponentContext> { - /// - /// The owner of the context. - /// - /// - /// An instance of will automatically generate this context object and assign itself. - /// - public LumexRadioGroup Owner { get; } = owner; - - /// - /// The callback to be invoked when the value of the radio group changes. - /// - public EventCallback ChangeEventCallback { get; } = changeEventCallback; - - /// - /// The name of the radio group. This is used to group radios together and gets applied to the name attribute of the radio input. - /// - /// - /// If this is not provided explicitly, the name will be generated automatically by the . - /// - public string? GroupName { get; set; } - - /// - /// The currently-selected value of the radio group. - /// - /// - /// It can be set in the parameter, and it will also be updated as the user interacts with the radios. - /// - public TValue? CurrentValue => _valueProvider.CurrentValue; - - /// - /// The provider for the value of the radio group. In this instance, it's . - /// - private readonly IRadioGroupValueProvider _valueProvider = owner; + /// + /// The owner of the context. + /// + /// + /// An instance of will automatically generate this context object and assign itself. + /// + public LumexRadioGroup Owner { get; } = owner; + + /// + /// The callback to be invoked when the value of the radio group changes. + /// + public EventCallback ChangeEventCallback { get; } = changeEventCallback; + + /// + /// The name of the radio group. This is used to group radios together and gets applied to the name attribute of the radio input. + /// + /// + /// If this is not provided explicitly, the name will be generated automatically by the . + /// + public string? GroupName { get; set; } + + /// + /// The currently-selected value of the radio group. + /// + public TValue? CurrentValue => _valueProvider.CurrentValue; + + /// + /// The provider for the value of the radio group. In this instance, it's . + /// + private readonly IRadioGroupValueProvider _valueProvider = owner; } \ No newline at end of file diff --git a/src/LumexUI/Components/Switch/LumexSwitch.razor.cs b/src/LumexUI/Components/Switch/LumexSwitch.razor.cs index 1bfb4bb0..189133c1 100644 --- a/src/LumexUI/Components/Switch/LumexSwitch.razor.cs +++ b/src/LumexUI/Components/Switch/LumexSwitch.razor.cs @@ -9,52 +9,58 @@ namespace LumexUI; +/// +/// A component that represents a switch input. +/// public partial class LumexSwitch : LumexBooleanInputBase, ISlotComponent { - /// - /// Gets or sets the delegate that resolves the - /// indicator icon based on the switch state. - /// - [Parameter] public IndicatorResolver? ThumbIcon { get; set; } + /// + /// Gets or sets the delegate that resolves the + /// indicator icon based on the switch state. + /// + [Parameter] public IndicatorResolver? ThumbIcon { get; set; } - /// - /// Gets or sets the icon to be rendered before the switch. - /// - [Parameter] public string? StartIcon { get; set; } + /// + /// Gets or sets the icon to be rendered before the switch. + /// + [Parameter] public string? StartIcon { get; set; } - /// - /// Gets or sets the icon to be rendered after the switch. - /// - [Parameter] public string? EndIcon { get; set; } + /// + /// Gets or sets the icon to be rendered after the switch. + /// + [Parameter] public string? EndIcon { get; set; } - /// - /// Gets or sets the CSS class names for the switch slots. - /// - [Parameter] public SwitchSlots? Classes { get; set; } + /// + /// Gets or sets the CSS class names for the switch slots. + /// + [Parameter] public SwitchSlots? Classes { get; set; } - private protected override string? RootClass => - TwMerge.Merge( Switch.GetStyles( this ) ); + private protected override string? RootClass => + TwMerge.Merge( Switch.GetStyles( this ) ); - private string? WrapperClass => - TwMerge.Merge( Switch.GetWrapperStyles( this ) ); + private string? WrapperClass => + TwMerge.Merge( Switch.GetWrapperStyles( this ) ); - private string? ThumbClass => - TwMerge.Merge( Switch.GetThumbStyles( this ) ); + private string? ThumbClass => + TwMerge.Merge( Switch.GetThumbStyles( this ) ); - private string? ThumbIconClass => - TwMerge.Merge( Switch.GetThumbIconStyles( this ) ); + private string? ThumbIconClass => + TwMerge.Merge( Switch.GetThumbIconStyles( this ) ); - private string? StartIconClass => - TwMerge.Merge( Switch.GetStartIconStyles( this ) ); + private string? StartIconClass => + TwMerge.Merge( Switch.GetStartIconStyles( this ) ); - private string? EndIconClass => - TwMerge.Merge( Switch.GetEndIconStyles( this ) ); + private string? EndIconClass => + TwMerge.Merge( Switch.GetEndIconStyles( this ) ); - private string? LabelClass => - TwMerge.Merge( Switch.GetLabelStyles( this ) ); + private string? LabelClass => + TwMerge.Merge( Switch.GetLabelStyles( this ) ); - public LumexSwitch() - { - Color = ThemeColor.Primary; - } + /// + /// Initializes a new instance of the . + /// + public LumexSwitch() + { + Color = ThemeColor.Primary; + } } diff --git a/src/LumexUI/Components/Switch/SwitchSlots.cs b/src/LumexUI/Components/Switch/SwitchSlots.cs index c81a5d6f..4fb0abce 100644 --- a/src/LumexUI/Components/Switch/SwitchSlots.cs +++ b/src/LumexUI/Components/Switch/SwitchSlots.cs @@ -1,17 +1,52 @@ -using System.Diagnostics.CodeAnalysis; +// 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.Common; namespace LumexUI; +/// +/// Represents the slot names for the , +/// used to assign CSS classes to different parts of the component. +/// [ExcludeFromCodeCoverage] public class SwitchSlots : ISlot { - public string? Root { get; set; } - public string? Wrapper { get; set; } - public string? Thumb { get; set; } - public string? Label { get; set; } - public string? StartIcon { get; set; } - public string? EndIcon { get; set; } - public string? ThumbIcon { get; set; } + /// + /// Gets or sets the CSS class for the root slot. + /// + public string? Root { get; set; } + + /// + /// Gets or sets the CSS class for the wrapper slot. + /// + public string? Wrapper { get; set; } + + /// + /// Gets or sets the CSS class for the thumb slot. + /// + public string? Thumb { get; set; } + + /// + /// Gets or sets the CSS class for the label slot. + /// + public string? Label { get; set; } + + /// + /// Gets or sets the CSS class for the start icon slot. + /// + public string? StartIcon { get; set; } + + /// + /// Gets or sets the CSS class for the end icon slot. + /// + public string? EndIcon { get; set; } + + /// + /// Gets or sets the CSS class for the thumb icon slot. + /// + public string? ThumbIcon { get; set; } } diff --git a/src/LumexUI/Components/Textbox/LumexTextbox.razor.cs b/src/LumexUI/Components/Textbox/LumexTextbox.razor.cs index fb2241ea..182eff5c 100644 --- a/src/LumexUI/Components/Textbox/LumexTextbox.razor.cs +++ b/src/LumexUI/Components/Textbox/LumexTextbox.razor.cs @@ -2,8 +2,6 @@ // 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; @@ -12,29 +10,30 @@ namespace LumexUI; /// -/// A component representing an input field for entering/editing values. +/// A component that represents an input field for entering values. /// public partial class LumexTextbox : LumexInputFieldBase { - /// - /// Gets or sets the input type of the textbox. - /// - /// - /// The default value is - /// - [Parameter] public InputType Type { get; set; } = InputType.Text; - - protected override void OnParametersSet() - { - SetInputType( Type.ToDescription() ); - - base.OnParametersSet(); - } - - /// - protected override bool TryParseValueFromString( string? value, out string? result ) - { - result = value; - return true; - } + /// + /// Gets or sets the input type of the textbox. + /// + /// + /// The default value is + /// + [Parameter] public InputType Type { get; set; } = InputType.Text; + + /// + protected override void OnParametersSet() + { + SetInputType( Type.ToDescription() ); + + base.OnParametersSet(); + } + + /// + protected override bool TryParseValueFromString( string? value, out string? result ) + { + result = value; + return true; + } } diff --git a/src/LumexUI/Extensions/ElementReferenceExtensions.cs b/src/LumexUI/Extensions/ElementReferenceExtensions.cs index 5bf12e58..2a258508 100644 --- a/src/LumexUI/Extensions/ElementReferenceExtensions.cs +++ b/src/LumexUI/Extensions/ElementReferenceExtensions.cs @@ -10,36 +10,35 @@ namespace LumexUI.Extensions; +/// +/// Provides extension methods for working with . +/// [ExcludeFromCodeCoverage] public static class ElementReferenceExtensions { - [UnsafeAccessor( UnsafeAccessorKind.Field, Name = "k__BackingField" )] - private static extern ref IJSRuntime GetJSRuntime( WebElementReferenceContext context ); - - public static ValueTask GetScrollHeightAsync( this ElementReference? elementReference ) - { - var jsRuntime = elementReference.GetJSRuntime(); - return jsRuntime.InvokeAsync( "Lumex.elementReference.getScrollHeight", elementReference ); - } - - public static ValueTask PortalToAsync( this ElementReference? elementReference ) - { - return PortalToAsync( elementReference, "body" ); - } - - public static ValueTask PortalToAsync( this ElementReference? elementReference, string selector ) - { - var jsRuntime = elementReference.GetJSRuntime(); - return jsRuntime.InvokeVoidAsync( "Lumex.elementReference.portalTo", elementReference, selector ); - } - - private static IJSRuntime GetJSRuntime( this ElementReference? elementReference ) - { - if( elementReference is not { Context: WebElementReferenceContext context } ) - { - throw new InvalidOperationException( "ElementReference has not been configured correctly." ); - } - - return GetJSRuntime( context ) ?? throw new InvalidOperationException( "No JavaScript runtime found." ); - } + [UnsafeAccessor( UnsafeAccessorKind.Field, Name = "k__BackingField" )] + private static extern ref IJSRuntime GetJSRuntime( WebElementReferenceContext context ); + + /// + /// Asynchronously gets the scroll height of the specified . + /// + /// The element reference. + /// + /// A representing the asynchronous operation that returns the scroll height of the element. + /// + public static ValueTask GetScrollHeightAsync( this ElementReference? elementReference ) + { + var jsRuntime = elementReference.GetJSRuntime(); + return jsRuntime.InvokeAsync( "Lumex.elementReference.getScrollHeight", elementReference ); + } + + private static IJSRuntime GetJSRuntime( this ElementReference? elementReference ) + { + if( elementReference is not { Context: WebElementReferenceContext context } ) + { + throw new InvalidOperationException( "ElementReference has not been configured correctly." ); + } + + return GetJSRuntime( context ) ?? throw new InvalidOperationException( "No JavaScript runtime found." ); + } } diff --git a/src/LumexUI/Extensions/ServiceCollectionExtensions.cs b/src/LumexUI/Extensions/ServiceCollectionExtensions.cs index bd306e0d..896245a0 100644 --- a/src/LumexUI/Extensions/ServiceCollectionExtensions.cs +++ b/src/LumexUI/Extensions/ServiceCollectionExtensions.cs @@ -21,54 +21,55 @@ namespace LumexUI.Extensions; [ExcludeFromCodeCoverage] public static class ServiceCollectionExtensions { - /// - /// Adds the LumexUI services to the specified . - /// - /// The . - public static void AddLumexServices( this IServiceCollection services ) - { - services.AddLumexMotion(); - services.AddTwMerge(); - services.AddPopoverService(); - } + /// + /// Adds the LumexUI services to the specified . + /// + /// The . + public static void AddLumexServices( this IServiceCollection services ) + { + services.AddLumexMotion(); + services.AddTwMerge(); + services.AddPopoverService(); + } - /// - /// Adds the LumexUI services to the specified . - /// - /// The . - public static void AddLumexServices( this IServiceCollection services, Action options ) - { - services.AddLumexMotion(); - services.AddTwMerge( options ); - services.AddPopoverService(); - } + /// + /// Adds Lumex services to the specified . + /// + /// The service collection to configure. + /// An action to configure the . + public static void AddLumexServices( this IServiceCollection services, Action options ) + { + services.AddLumexMotion(); + services.AddTwMerge( options ); + services.AddPopoverService(); + } - private static void AddTwMerge( this IServiceCollection services ) - { - services.AddTailwindMerge( options => - { - options.Extend( new ExtendedConfig() - { - ClassGroups = new() - { - ["shadow"] = new ClassGroup( "shadow", ["small", "medium", "large"] ), - ["rounded"] = new ClassGroup( "rounded", ["small", "medium", "large"] ), - ["opacity"] = new ClassGroup( "opacity", ["hover", "focus", "disabled", "divider"] ), - ["leading"] = new ClassGroup( "leading", ["tiny", "small", "medium", "large"] ), - ["font-size"] = new ClassGroup( "text", ["tiny", "small", "medium", "large"] ), - } - } ); - } ); - } + private static void AddTwMerge( this IServiceCollection services ) + { + services.AddTailwindMerge( options => + { + options.Extend( new ExtendedConfig() + { + ClassGroups = new() + { + ["shadow"] = new ClassGroup( "shadow", ["small", "medium", "large"] ), + ["rounded"] = new ClassGroup( "rounded", ["small", "medium", "large"] ), + ["opacity"] = new ClassGroup( "opacity", ["hover", "focus", "disabled", "divider"] ), + ["leading"] = new ClassGroup( "leading", ["tiny", "small", "medium", "large"] ), + ["font-size"] = new ClassGroup( "text", ["tiny", "small", "medium", "large"] ), + } + } ); + } ); + } - private static void AddTwMerge( this IServiceCollection services, Action options ) - { - services.AddTwMerge(); - services.Configure( options ); - } + private static void AddTwMerge( this IServiceCollection services, Action options ) + { + services.AddTwMerge(); + services.Configure( options ); + } - private static void AddPopoverService( this IServiceCollection services ) - { - services.AddScoped(); - } + private static void AddPopoverService( this IServiceCollection services ) + { + services.AddScoped(); + } } diff --git a/src/LumexUI/Services/Popover/IPopoverService.cs b/src/LumexUI/Services/Popover/IPopoverService.cs index 0412c05c..5eb236ab 100644 --- a/src/LumexUI/Services/Popover/IPopoverService.cs +++ b/src/LumexUI/Services/Popover/IPopoverService.cs @@ -1,10 +1,34 @@ -namespace LumexUI.Services; +// 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 +namespace LumexUI.Services; + +/// +/// Defines a service for managing instances. +/// public interface IPopoverService { - LumexPopover? LastShown { get; } + /// + /// Gets the last shown popover. + /// + LumexPopover? LastShown { get; } + + /// + /// Registers a popover with the service. + /// + /// The popover to register. + void Register( LumexPopover popover ); + + /// + /// Unregisters a popover from the service. + /// + /// The popover to unregister. + void Unregister( LumexPopover popover ); - void Register( LumexPopover popover ); - void Unregister( LumexPopover popover ); - void SetLastShown( LumexPopover? popover ); + /// + /// Sets the last shown popover. + /// + /// The popover to set as the last shown instance. + void SetLastShown( LumexPopover? popover ); } diff --git a/src/LumexUI/Services/Popover/PopoverService.cs b/src/LumexUI/Services/Popover/PopoverService.cs index 3d13ffc7..3f59dc07 100644 --- a/src/LumexUI/Services/Popover/PopoverService.cs +++ b/src/LumexUI/Services/Popover/PopoverService.cs @@ -1,23 +1,34 @@ -namespace LumexUI.Services; +// 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 +namespace LumexUI.Services; + +/// +/// Provides an implementation of for managing popover components. +/// public class PopoverService : IPopoverService { - private readonly HashSet _registeredPopovers = []; + private readonly HashSet _registeredPopovers = []; - public LumexPopover? LastShown { get; private set; } + /// + public LumexPopover? LastShown { get; private set; } - public void SetLastShown( LumexPopover? popover ) - { - LastShown = popover; - } + /// + public void SetLastShown( LumexPopover? popover ) + { + LastShown = popover; + } - public void Register( LumexPopover popover ) - { - _registeredPopovers.Add( popover ); - } + /// + public void Register( LumexPopover popover ) + { + _registeredPopovers.Add( popover ); + } - public void Unregister( LumexPopover popover ) - { - _registeredPopovers.Remove( popover ); - } + /// + public void Unregister( LumexPopover popover ) + { + _registeredPopovers.Remove( popover ); + } } diff --git a/tests/LumexUI.Tests/_Imports.razor b/tests/LumexUI.Tests/_Imports.razor index 880cafd5..a981019d 100644 --- a/tests/LumexUI.Tests/_Imports.razor +++ b/tests/LumexUI.Tests/_Imports.razor @@ -2,6 +2,7 @@ @using Bunit.TestDoubles +@using LumexUI.Common @using LumexUI.Motion @using LumexUI.Services @using LumexUI.Tests.Extensions