-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(components): add card component (#13)
* feat(components): add the card component * feat(components): add the card body component * feat(components): add the card footer component * feat(components): add the card header component * feat(theme): add Radius configuration in the layout config * feat(theme): add Shadow configuration in the layout config * feat(components): add styles for the card component * docs(theme): update the props for extended theme in the tailwind config * test(components): add tests for the card component * chore(components): update theme generation in the theme provider component * chore(components): update button styles * chore(components): nits * test(components): remove redundant `Button_ChildContent_ShouldRenderCorrectly` test * feat(components): introduce the concept of the components with slots
- Loading branch information
1 parent
417a2f5
commit 4e630bb
Showing
24 changed files
with
741 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,174 @@ | ||
/** @type {import('tailwindcss').Config} */ | ||
module.exports = { | ||
content: [ | ||
"./Shared/**/*.razor", | ||
"../../src/LumexUI/Styles/*.cs" | ||
], | ||
theme: { | ||
extend: {}, | ||
extend: { | ||
colors: { | ||
/** Base */ | ||
background: { | ||
DEFAULT: "hsl(var(--lumex-background) / <alpha-value>)" | ||
}, | ||
foreground: { | ||
50: "hsl(var(--lumex-foreground-50) / <alpha-value>)", | ||
100: "hsl(var(--lumex-foreground-100) / <alpha-value>)", | ||
200: "hsl(var(--lumex-foreground-200) / <alpha-value>)", | ||
300: "hsl(var(--lumex-foreground-300) / <alpha-value>)", | ||
400: "hsl(var(--lumex-foreground-400) / <alpha-value>)", | ||
500: "hsl(var(--lumex-foreground-500) / <alpha-value>)", | ||
600: "hsl(var(--lumex-foreground-600) / <alpha-value>)", | ||
700: "hsl(var(--lumex-foreground-700) / <alpha-value>)", | ||
800: "hsl(var(--lumex-foreground-800) / <alpha-value>)", | ||
900: "hsl(var(--lumex-foreground-900) / <alpha-value>)", | ||
DEFAULT: "hsl(var(--lumex-foreground) / <alpha-value>)" | ||
}, | ||
focus: { | ||
DEFAULT: "hsl(var(--lumex-focus) / <alpha-value>)" | ||
}, | ||
overlay: { | ||
DEFAULT: "hsl(var(--lumex-overlay) / <alpha-value>)" | ||
}, | ||
divider: { | ||
DEFAULT: "hsl(var(--lumex-divider) / var(--lumex-divider-opacity ,<alpha-value>))" | ||
}, | ||
|
||
/** Default */ | ||
default: { | ||
50: "hsl(var(--lumex-default-50) / <alpha-value>)", | ||
100: "hsl(var(--lumex-default-100) / <alpha-value>)", | ||
200: "hsl(var(--lumex-default-200) / <alpha-value>)", | ||
300: "hsl(var(--lumex-default-300) / <alpha-value>)", | ||
400: "hsl(var(--lumex-default-400) / <alpha-value>)", | ||
500: "hsl(var(--lumex-default-500) / <alpha-value>)", | ||
600: "hsl(var(--lumex-default-600) / <alpha-value>)", | ||
700: "hsl(var(--lumex-default-700) / <alpha-value>)", | ||
800: "hsl(var(--lumex-default-800) / <alpha-value>)", | ||
900: "hsl(var(--lumex-default-900) / <alpha-value>)", | ||
DEFAULT: "hsl(var(--lumex-default) / <alpha-value>)", | ||
foreground: "hsl(var(--lumex-default-foreground) / <alpha-value>)" | ||
}, | ||
|
||
/** Primary */ | ||
primary: { | ||
50: "hsl(var(--lumex-primary-50) / <alpha-value>)", | ||
100: "hsl(var(--lumex-primary-100) / <alpha-value>)", | ||
200: "hsl(var(--lumex-primary-200) / <alpha-value>)", | ||
300: "hsl(var(--lumex-primary-300) / <alpha-value>)", | ||
400: "hsl(var(--lumex-primary-400) / <alpha-value>)", | ||
500: "hsl(var(--lumex-primary-500) / <alpha-value>)", | ||
600: "hsl(var(--lumex-primary-600) / <alpha-value>)", | ||
700: "hsl(var(--lumex-primary-700) / <alpha-value>)", | ||
800: "hsl(var(--lumex-primary-800) / <alpha-value>)", | ||
900: "hsl(var(--lumex-primary-900) / <alpha-value>)", | ||
DEFAULT: "hsl(var(--lumex-primary) / <alpha-value>)", | ||
foreground: "hsl(var(--lumex-primary-foreground) / <alpha-value>)" | ||
}, | ||
|
||
/** Secondary */ | ||
secondary: { | ||
50: "hsl(var(--lumex-secondary-50) / <alpha-value>)", | ||
100: "hsl(var(--lumex-secondary-100) / <alpha-value>)", | ||
200: "hsl(var(--lumex-secondary-200) / <alpha-value>)", | ||
300: "hsl(var(--lumex-secondary-300) / <alpha-value>)", | ||
400: "hsl(var(--lumex-secondary-400) / <alpha-value>)", | ||
500: "hsl(var(--lumex-secondary-500) / <alpha-value>)", | ||
600: "hsl(var(--lumex-secondary-600) / <alpha-value>)", | ||
700: "hsl(var(--lumex-secondary-700) / <alpha-value>)", | ||
800: "hsl(var(--lumex-secondary-800) / <alpha-value>)", | ||
900: "hsl(var(--lumex-secondary-900) / <alpha-value>)", | ||
DEFAULT: "hsl(var(--lumex-secondary) / <alpha-value>)", | ||
foreground: "hsl(var(--lumex-secondary-foreground) / <alpha-value>)" | ||
}, | ||
|
||
/** Success */ | ||
success: { | ||
50: "hsl(var(--lumex-success-50) / <alpha-value>)", | ||
100: "hsl(var(--lumex-success-100) / <alpha-value>)", | ||
200: "hsl(var(--lumex-success-200) / <alpha-value>)", | ||
300: "hsl(var(--lumex-success-300) / <alpha-value>)", | ||
400: "hsl(var(--lumex-success-400) / <alpha-value>)", | ||
500: "hsl(var(--lumex-success-500) / <alpha-value>)", | ||
600: "hsl(var(--lumex-success-600) / <alpha-value>)", | ||
700: "hsl(var(--lumex-success-700) / <alpha-value>)", | ||
800: "hsl(var(--lumex-success-800) / <alpha-value>)", | ||
900: "hsl(var(--lumex-success-900) / <alpha-value>)", | ||
DEFAULT: "hsl(var(--lumex-success) / <alpha-value>)", | ||
foreground: "hsl(var(--lumex-success-foreground) / <alpha-value>)" | ||
}, | ||
|
||
/** Warning */ | ||
warning: { | ||
50: "hsl(var(--lumex-warning-50) / <alpha-value>)", | ||
100: "hsl(var(--lumex-warning-100) / <alpha-value>)", | ||
200: "hsl(var(--lumex-warning-200) / <alpha-value>)", | ||
300: "hsl(var(--lumex-warning-300) / <alpha-value>)", | ||
400: "hsl(var(--lumex-warning-400) / <alpha-value>)", | ||
500: "hsl(var(--lumex-warning-500) / <alpha-value>)", | ||
600: "hsl(var(--lumex-warning-600) / <alpha-value>)", | ||
700: "hsl(var(--lumex-warning-700) / <alpha-value>)", | ||
800: "hsl(var(--lumex-warning-800) / <alpha-value>)", | ||
900: "hsl(var(--lumex-warning-900) / <alpha-value>)", | ||
DEFAULT: "hsl(var(--lumex-warning) / <alpha-value>)", | ||
foreground: "hsl(var(--lumex-warning-foreground) / <alpha-value>)" | ||
}, | ||
|
||
/** Danger */ | ||
danger: { | ||
50: "hsl(var(--lumex-danger-50) / <alpha-value>)", | ||
100: "hsl(var(--lumex-danger-100) / <alpha-value>)", | ||
200: "hsl(var(--lumex-danger-200) / <alpha-value>)", | ||
300: "hsl(var(--lumex-danger-300) / <alpha-value>)", | ||
400: "hsl(var(--lumex-danger-400) / <alpha-value>)", | ||
500: "hsl(var(--lumex-danger-500) / <alpha-value>)", | ||
600: "hsl(var(--lumex-danger-600) / <alpha-value>)", | ||
700: "hsl(var(--lumex-danger-700) / <alpha-value>)", | ||
800: "hsl(var(--lumex-danger-800) / <alpha-value>)", | ||
900: "hsl(var(--lumex-danger-900) / <alpha-value>)", | ||
DEFAULT: "hsl(var(--lumex-danger) / <alpha-value>)", | ||
foreground: "hsl(var(--lumex-danger-foreground) / <alpha-value>)" | ||
}, | ||
|
||
/** Info */ | ||
info: { | ||
50: "hsl(var(--lumex-info-50) / <alpha-value>)", | ||
100: "hsl(var(--lumex-info-100) / <alpha-value>)", | ||
200: "hsl(var(--lumex-info-200) / <alpha-value>)", | ||
300: "hsl(var(--lumex-info-300) / <alpha-value>)", | ||
400: "hsl(var(--lumex-info-400) / <alpha-value>)", | ||
500: "hsl(var(--lumex-info-500) / <alpha-value>)", | ||
600: "hsl(var(--lumex-info-600) / <alpha-value>)", | ||
700: "hsl(var(--lumex-info-700) / <alpha-value>)", | ||
800: "hsl(var(--lumex-info-800) / <alpha-value>)", | ||
900: "hsl(var(--lumex-info-900) / <alpha-value>)", | ||
DEFAULT: "hsl(var(--lumex-info) / <alpha-value>)", | ||
foreground: "hsl(var(--lumex-info-foreground) / <alpha-value>)" | ||
} | ||
}, | ||
fontSize: { | ||
tiny: ["var(--lumex-font-size-tiny)", "var(--lumex-line-height-tiny)"], | ||
small: ["var(--lumex-font-size-small)", "var(--lumex-line-height-small)"], | ||
medium: ["var(--lumex-font-size-medium)", "var(--lumex-line-height-medium)"], | ||
large: ["var(--lumex-font-size-large)", "var(--lumex-line-height-large)"] | ||
}, | ||
borderRadius: { | ||
small: "var(--lumex-radius-small)", | ||
medium: "var(--lumex-radius-medium)", | ||
large: "var(--lumex-radius-large)" | ||
}, | ||
boxShadow: { | ||
small: "var(--lumex-box-shadow-small)", | ||
medium: "var(--lumex-box-shadow-medium)", | ||
large: "var(--lumex-box-shadow-large)" | ||
}, | ||
opacity: { | ||
divider: "var(--lumex-divider-opacity)", | ||
disabled: "var(--lumex-disabled-opacity)", | ||
focus: "var(--lumex-focus-opacity)", | ||
hover: "var(--lumex-hover-opacity)" | ||
}, | ||
}, | ||
}, | ||
plugins: [], | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// 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; | ||
|
||
public enum Radius | ||
{ | ||
None, | ||
|
||
Small, | ||
|
||
Medium, | ||
|
||
Large | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// 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; | ||
|
||
public enum Shadow | ||
{ | ||
None, | ||
|
||
Small, | ||
|
||
Medium, | ||
|
||
Large | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace LumexUI.Common; | ||
|
||
public interface ISlot | ||
{ | ||
string? Root { get; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace LumexUI.Common; | ||
|
||
internal interface ISlotComponent<T> where T : ISlot | ||
{ | ||
T? Classes { get; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
namespace LumexUI; | ||
|
||
internal class CardContext( LumexCard owner ) | ||
{ | ||
public LumexCard Owner { get; } = owner; | ||
|
||
public static void ThrowMissingParentComponentException( CardContext context, string componentName ) | ||
{ | ||
if( context is null ) | ||
{ | ||
throw new InvalidOperationException( | ||
$"<{componentName} /> component must be used within a <{nameof( LumexCard )} /> component." ); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using LumexUI.Common; | ||
|
||
namespace LumexUI; | ||
|
||
public class CardSlots : ISlot | ||
{ | ||
public string? Root { get; set; } | ||
public string? Header { get; set; } | ||
public string? Body { get; set; } | ||
public string? Footer { get; set; } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
@namespace LumexUI | ||
@inherits LumexComponentBase | ||
|
||
<CascadingValue TValue="CardContext" Value="@_context" IsFixed="@true"> | ||
<LumexComponent Class="@RootClass" | ||
Style="@RootStyle" | ||
@attributes="@AdditionalAttributes" | ||
data-slot="root"> | ||
@ChildContent | ||
</LumexComponent> | ||
</CascadingValue> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// 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 LumexUI.Styles; | ||
|
||
using Microsoft.AspNetCore.Components; | ||
|
||
namespace LumexUI; | ||
|
||
public partial class LumexCard : LumexComponentBase, ISlotComponent<CardSlots> | ||
{ | ||
/// <summary> | ||
/// Gets or sets content to be rendered inside the card. | ||
/// </summary> | ||
[Parameter] public RenderFragment? ChildContent { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the border radius of the card. | ||
/// </summary> | ||
/// <remarks> | ||
/// Default value is <see cref="Radius.Large"/> | ||
/// </remarks> | ||
[Parameter] public Radius Radius { get; set; } = Radius.Large; | ||
|
||
/// <summary> | ||
/// Gets or sets the shadow of the card. | ||
/// </summary> | ||
/// <remarks> | ||
/// Default value is <see cref="Shadow.Medium"/> | ||
/// </remarks> | ||
[Parameter] public Shadow Shadow { get; set; } = Shadow.Small; | ||
|
||
/// <summary> | ||
/// Gets or sets a value indicating whether the card is full-width. | ||
/// </summary> | ||
[Parameter] public bool FullWidth { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets a value indicating whether the card is blurred. | ||
/// </summary> | ||
[Parameter] public bool Blurred { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the CSS class names for the card slots. | ||
/// </summary> | ||
[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 ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
@namespace LumexUI | ||
@inherits LumexComponentBase | ||
|
||
<LumexComponent Class="@RootClass" | ||
Style="@RootStyle" | ||
@attributes="@AdditionalAttributes" | ||
data-slot="body"> | ||
@ChildContent | ||
</LumexComponent> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// 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.Styles; | ||
|
||
using Microsoft.AspNetCore.Components; | ||
|
||
namespace LumexUI; | ||
|
||
public partial class LumexCardBody : LumexComponentBase | ||
{ | ||
/// <summary> | ||
/// Gets or sets content to be rendered inside the card body. | ||
/// </summary> | ||
[Parameter] public RenderFragment? ChildContent { get; set; } | ||
|
||
[CascadingParameter] internal CardContext Context { get; set; } = default!; | ||
|
||
private protected override string? RootClass | ||
=> TwMerge.Merge( Card.GetBodyStyles( this ) ); | ||
|
||
protected override void OnInitialized() | ||
{ | ||
CardContext.ThrowMissingParentComponentException( Context, nameof( LumexCardBody ) ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
@namespace LumexUI | ||
@inherits LumexComponentBase | ||
|
||
<LumexComponent Class="@RootClass" | ||
Style="@RootStyle" | ||
@attributes="@AdditionalAttributes" | ||
data-slot="footer"> | ||
@ChildContent | ||
</LumexComponent> |
Oops, something went wrong.