diff --git a/packages/demo/src/content/components/alert.mdx b/packages/demo/src/content/components/alert.mdx index 173012f1..c59483c3 100644 --- a/packages/demo/src/content/components/alert.mdx +++ b/packages/demo/src/content/components/alert.mdx @@ -4,7 +4,7 @@ heading: "Alert" description: "Alert with sizes and variants" --- -import { Alert, AlertTitle, AlertDescription } from "@eqtylab/equality"; +import { Alert } from "@eqtylab/equality"; ## Overview @@ -12,7 +12,8 @@ import { Alert, AlertTitle, AlertDescription } from "@eqtylab/equality"; The Alert component displays important messages to users, usually as a result of an action they took. Use alerts to communicate status, warnings, errors, or success states. -- **Default:** General information. +- **Primary:** General information. +- **Neutral:** Low-emphasis, informational messages. - **Success:** Positive outcomes or confirmations. - **Warning:** Cautionary information that needs attention. - **Danger:** Critical errors or destructive actions. @@ -24,75 +25,89 @@ The Alert component displays important messages to users, usually as a result of Import the component: ```tsx -import { Alert, AlertTitle, AlertDescription } from "@eqtylab/equality"; +import { Alert } from "@eqtylab/equality"; ``` Basic usage with title and description: ```tsx - - Heads up! - This is an important message. - + ``` ## Variants --- -
- - Default - This is a default alert for general information. - - - - Success - - Your changes have been saved successfully. - - - - - Warning - - Please review your input before continuing. - - - - - Danger - A serious error occurred. - +The `success`, `warning`, and `danger` variants show a default icon. The `primary` and `neutral` variants have no icon by default. + +
+ + + + +
### Usage ```tsx - - Success - Your changes have been saved successfully. - - - - Warning - Please review your input before continuing. - - - - Danger - A serious error occurred. - + + + + + ``` -## Slots - ---- +### Icons + +The `success`, `warning`, and `danger` variants apply a default icon automatically. Override it with the `icon` prop by passing a [Lucide](https://lucide.dev/icons/) icon name or a React element. Pass `icon={null}` to remove a variant's default icon, or add an icon to a variant that has none. + +
+ + + +
-| Name | Description | -| ------------------ | ---------------------------------------------------- | -| `AlertTitle` | Renders the alert heading as an `
` element. | -| `AlertDescription` | Renders the alert body content as a `
` element. | +```tsx + + + +``` ## Props @@ -100,14 +115,9 @@ Basic usage with title and description: ### Alert -| Name | Description | Type | Default | Required | -| --------- | ----------------------------- | ----------------------------------------- | --------- | -------- | -| `variant` | The visual style of the alert | `default`, `success`, `warning`, `danger` | `default` | ❌ | - -### AlertTitle - -Accepts all standard HTML heading attributes. - -### AlertDescription - -Accepts all standard HTML paragraph attributes. +| Name | Description | Type | Default | Required | +| ------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------- | --------------- | -------- | +| `title` | The alert heading, rendered as an `

` | `string` | — | ✅ | +| `description` | The alert body text, rendered as a `

` | `string` | — | ✅ | +| `variant` | The visual style of the alert | `primary`, `neutral`, `success`, `warning`, `danger` | `primary` | ❌ | +| `icon` | Overrides the variant's default icon. Lucide name, element, or `null` to hide | `string`, `ReactElement`, `null` | Variant default | ❌ | diff --git a/packages/ui/src/components/alert/alert.module.css b/packages/ui/src/components/alert/alert.module.css index d99308ef..de3be9b1 100644 --- a/packages/ui/src/components/alert/alert.module.css +++ b/packages/ui/src/components/alert/alert.module.css @@ -1,38 +1,58 @@ @reference '../../theme/theme.module.css'; .alert { - @apply [&>svg]:text-text-primary [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg~*]:pl-7; @apply relative w-full; @apply rounded-lg border; @apply px-4 py-3; @apply text-sm; + @apply grid gap-y-1; } -.alert--default { - @apply text-badge-text-neutral [&>svg]:text-badge-text-neutral; +.alert--primary { + @apply text-badge-text-primary; + @apply bg-badge-background-primary border-badge-text-primary; +} + +.alert--neutral { + @apply text-badge-text-neutral; @apply bg-badge-background-neutral border-badge-text-neutral; } .alert--success { - @apply text-badge-text-success [&>svg]:text-badge-text-success; + @apply text-badge-text-success; @apply bg-badge-background-success border-badge-text-success; } .alert--warning { - @apply text-badge-text-warning [&>svg]:text-badge-text-warning; + @apply text-badge-text-warning; @apply bg-badge-background-warning border-badge-text-warning; } .alert--danger { - @apply text-badge-text-danger [&>svg]:text-badge-text-danger; + @apply text-badge-text-danger; @apply bg-badge-background-danger border-badge-text-danger; } +.alert .alert-icon { + @apply text-inherit; +} + .alert-title { @apply font-medium leading-none tracking-tight; - @apply mb-1; } .alert-description { @apply text-sm [&_p]:leading-relaxed; } + +.alert--with-icon { + @apply grid-cols-[auto_1fr] gap-x-2; +} + +.alert--with-icon .alert-title { + @apply col-start-2 self-center; +} + +.alert--with-icon .alert-description { + @apply col-start-2; +} diff --git a/packages/ui/src/components/alert/alert.tsx b/packages/ui/src/components/alert/alert.tsx index 937185e5..3095586c 100644 --- a/packages/ui/src/components/alert/alert.tsx +++ b/packages/ui/src/components/alert/alert.tsx @@ -2,43 +2,70 @@ import * as React from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; import styles from '@/components/alert/alert.module.css'; +import { Icon } from '@/components/icon/icon'; import { cn } from '@/lib/utils'; const alertVariants = cva(styles['alert'], { variants: { variant: { - default: styles['alert--default'], + primary: styles['alert--primary'], + neutral: styles['alert--neutral'], success: styles['alert--success'], warning: styles['alert--warning'], danger: styles['alert--danger'], }, }, defaultVariants: { - variant: 'default', + variant: 'primary', }, }); -const Alert = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes & VariantProps ->(({ className, variant, ...props }, ref) => ( -

-)); -Alert.displayName = 'Alert'; +// Default icons for variants. Primary and neutral have no icon by default. +const defaultVariantIcons: Record = { + success: 'Check', + warning: 'OctagonAlert', + danger: 'TriangleAlert', +}; -const AlertTitle = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ) -); -AlertTitle.displayName = 'AlertTitle'; +interface AlertProps + extends React.HTMLAttributes, VariantProps { + title: string; + description: string; + icon?: React.ReactElement | string | null; +} + +const Alert = React.forwardRef( + ({ className, variant, title, description, icon, ...props }, ref) => { + // Use the provided icon, otherwise fall back to the variant's default icon. + // Passing `icon={null}` explicitly opts out of the default icon. + const effectiveIcon = + icon === undefined ? (variant ? defaultVariantIcons[variant] : undefined) : icon; -const AlertDescription = React.forwardRef< - HTMLParagraphElement, - React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); -AlertDescription.displayName = 'AlertDescription'; + return ( +
+ {effectiveIcon ? ( + + ) : null} +

{title}

+

{description}

+
+ ); + } +); +Alert.displayName = 'Alert'; -export { Alert, AlertDescription, AlertTitle }; +export { Alert };