Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libs/react/ui/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@
--background-highlight-base: var(--color-primary-950);
--background-neutral-hover: var(--color-neutral-800);
--background-neutral-overlay: var(--color-neutral-800);
--background-components-base: var(--color-neutral-900);
--background-components-base: var(--color-neutral-800);
--background-field-component: var(--color-neutral-900);
--background-switch-off-hover: var(--color-neutral-600);
--background-field-component-hover: var(--color-neutral-800);
Expand Down
77 changes: 77 additions & 0 deletions libs/react/ui/src/components/alert/alert.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type {Meta, StoryObj} from '@storybook/react';
import {Header} from 'components/typography';
import {
Alert,
AlertAction,
AlertActions,
AlertClose,
AlertContent,
AlertDescription,
AlertTitle,
} from './alert';

const meta = {
title: 'Components/Alert',
component: Alert,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['default', 'info', 'success', 'warning', 'destructive'],
},
},
args: {
variant: 'default',
},
} satisfies Meta<typeof Alert>;

export default meta;

type Story = StoryObj<typeof meta>;

const variants = ['default', 'info', 'success', 'warning', 'destructive'] as const;

export const Default: Story = {
render: (args) => {
return (
<Alert {...args}>
<AlertContent>
<AlertTitle>Title</AlertTitle>
<AlertDescription>Description</AlertDescription>
<AlertActions>
<AlertAction>Download</AlertAction>
<AlertAction>View</AlertAction>
</AlertActions>
</AlertContent>
<AlertClose variant={args.variant} />
</Alert>
);
},
};

export const DesignMock: Story = {
render: () => {
return (
<div className="flex flex-col gap-32 pb-64 pt-32 px-32 bg-background-neutral-base">
<Header variant="h3" className="text-foreground-neutral-subtle">
ALERTS
</Header>
<div className="flex flex-col gap-16">
{variants.map((variant) => (
<Alert key={variant} variant={variant}>
<AlertContent>
<AlertTitle>Title</AlertTitle>
<AlertDescription>Description</AlertDescription>
<AlertActions>
<AlertAction>Download</AlertAction>
<AlertAction>View</AlertAction>
</AlertActions>
</AlertContent>
<AlertClose variant={variant} />
</Alert>
))}
</div>
</div>
);
},
};
144 changes: 144 additions & 0 deletions libs/react/ui/src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import {cva, type VariantProps} from 'class-variance-authority';
import {Icon} from 'components/icon';
import type {ComponentProps} from 'react';
import {cn} from 'utils/cn';

const alertVariants = cva(
'relative w-full rounded-l-4 rounded-r-8 px-16 py-12 text-sm flex gap-12 items-start border',
{
variants: {
variant: {
default: 'bg-tag-neutral-bg text-foreground-neutral-base border-tag-neutral-border',
info: 'bg-tag-blue-bg text-foreground-neutral-base border-tag-blue-border',
success: 'bg-tag-success-bg text-foreground-neutral-base border-tag-success-border',
warning: 'bg-tag-warning-bg text-foreground-neutral-base border-tag-warning-border',
destructive: 'bg-tag-error-bg text-foreground-neutral-base border-tag-error-border',
},
},
defaultVariants: {
variant: 'default',
},
},
);

const alertLineVariants = cva('w-4 self-stretch rounded-full', {
variants: {
variant: {
default: 'bg-tag-neutral-icon',
info: 'bg-tag-blue-icon',
success: 'bg-tag-success-icon',
warning: 'bg-tag-warning-icon',
destructive: 'bg-tag-error-icon',
},
},
defaultVariants: {
variant: 'default',
},
});

const closeIconVariants = cva('w-16 h-16', {
variants: {
variant: {
default: 'text-tag-neutral-icon',
info: 'text-tag-blue-icon',
success: 'text-tag-success-icon',
warning: 'text-tag-warning-icon',
destructive: 'text-tag-error-icon',
},
},
defaultVariants: {
variant: 'default',
},
});

type AlertProps = ComponentProps<'div'> & VariantProps<typeof alertVariants>;

function Alert({className, variant, children, ...props}: AlertProps) {
return (
<div className="w-full flex items-start gap-4">
<div data-slot="alert-line" className={cn(alertLineVariants({variant}))} aria-hidden="true" />
<div
data-slot="alert"
role="alert"
className={cn(alertVariants({variant}), className)}
{...props}
>
{children}
</div>
</div>
);
}

function AlertContent({className, ...props}: ComponentProps<'div'>) {
return <div data-slot="alert-content" className={cn('flex-1 min-w-0', className)} {...props} />;
}

function AlertTitle({className, ...props}: ComponentProps<'div'>) {
return (
<div
data-slot="alert-title"
className={cn('font-medium text-sm leading-20 text-foreground-neutral-base mb-4', className)}
{...props}
/>
);
}

function AlertDescription({className, ...props}: ComponentProps<'div'>) {
return (
<div
data-slot="alert-description"
className={cn(
'text-xs leading-20 text-foreground-neutral-base [&_p]:leading-relaxed',
className,
)}
{...props}
/>
);
}

function AlertActions({className, ...props}: ComponentProps<'div'>) {
return (
<div
data-slot="alert-actions"
className={cn('flex items-center gap-8 mt-8', className)}
{...props}
/>
);
}

function AlertAction({className, ...props}: ComponentProps<'button'>) {
return (
<button
data-slot="alert-action"
type="button"
className={cn(
'bg-transparent border-none p-0 cursor-pointer text-xs font-medium leading-20 text-foreground-neutral-base hover:text-foreground-neutral-subtle transition-colors duration-150 outline-none focus-visible:ring-2 focus-visible:ring-background-accent-blue-base focus-visible:ring-offset-2',
className,
)}
{...props}
/>
);
}

function AlertClose({
className,
variant = 'default',
...props
}: ComponentProps<'button'> & VariantProps<typeof closeIconVariants>) {
return (
<button
data-slot="alert-close"
type="button"
className={cn(
'absolute cursor-pointer top-12 right-12 rounded-4 p-4 bg-transparent border-none text-foreground-neutral-muted hover:text-foreground-neutral-base hover:bg-background-components-hover transition-colors duration-150 outline-none focus-visible:ring-2 focus-visible:ring-background-accent-blue-base focus-visible:ring-offset-2',
className,
)}
aria-label="Close"
{...props}
>
<Icon name="close" className={cn(closeIconVariants({variant}))} />
</button>
);
}

export {Alert, AlertContent, AlertTitle, AlertDescription, AlertActions, AlertAction, AlertClose};
1 change: 1 addition & 0 deletions libs/react/ui/src/components/alert/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './alert';
8 changes: 7 additions & 1 deletion libs/react/ui/src/components/icon/icon.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import {type RemixiconComponentType, RiGoogleFill, RiMicrosoftFill} from '@remixicon/react';
import {
type RemixiconComponentType,
RiCloseLine,
RiGoogleFill,
RiMicrosoftFill,
} from '@remixicon/react';
import type {ComponentProps} from 'react';
import {
BadgeIcon,
Expand Down Expand Up @@ -28,6 +33,7 @@ const iconsMap = {
spinner: SpinnerIcon,
ellipseMiniSolid: EllipseMiniSolidIcon,
componentLine: ComponentLineIcon,
close: RiCloseLine,
} as const satisfies Record<string, RemixiconComponentType>;

export type IconName = keyof typeof iconsMap;
Expand Down
2 changes: 2 additions & 0 deletions libs/react/ui/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export * from './alert';
export * from './button';
export * from './icon';
export * from './inline-tips';
export * from './textarea';
export * from './theme-provider';
export * from './typography';
1 change: 1 addition & 0 deletions libs/react/ui/src/components/inline-tips/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './inline-tips';
126 changes: 126 additions & 0 deletions libs/react/ui/src/components/inline-tips/inline-tips.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import type {Meta, StoryObj} from '@storybook/react';
import {Code, Header} from 'components/typography';
import {
InlineTips,
InlineTipsAction,
InlineTipsActions,
InlineTipsContent,
InlineTipsDescription,
InlineTipsTitle,
} from './inline-tips';

const meta = {
title: 'Components/InlineTips',
component: InlineTips,
tags: ['autodocs'],
argTypes: {
type: {
control: 'select',
options: ['default', 'info', 'success', 'destructive'],
},
variant: {
control: 'select',
options: ['primary', 'secondary'],
},
},
args: {
type: 'default',
variant: 'primary',
},
} satisfies Meta<typeof InlineTips>;

export default meta;

type Story = StoryObj<typeof meta>;

const types = ['default', 'info', 'success', 'destructive'] as const;
const variants = ['primary', 'secondary'] as const;

export const Default: Story = {
render: (args) => {
return (
<InlineTips {...args}>
<InlineTipsContent>
<InlineTipsTitle>Title</InlineTipsTitle>
<InlineTipsDescription>Description</InlineTipsDescription>
</InlineTipsContent>
<InlineTipsActions>
<InlineTipsAction variant={args.variant}>Label</InlineTipsAction>
<InlineTipsAction variant={args.variant}>Label</InlineTipsAction>
</InlineTipsActions>
</InlineTips>
);
},
};

export const Variants: Story = {
render: () => {
return (
<div className="flex flex-col gap-16">
{variants.map((variant) => (
<InlineTips key={variant} type="default" variant={variant}>
<InlineTipsContent>
<InlineTipsTitle>Title</InlineTipsTitle>
<InlineTipsDescription>Description</InlineTipsDescription>
</InlineTipsContent>
<InlineTipsActions>
<InlineTipsAction variant="primary">Label</InlineTipsAction>
<InlineTipsAction variant="secondary">Label</InlineTipsAction>
</InlineTipsActions>
</InlineTips>
))}
</div>
);
},
};

export const DesignMock: Story = {
render: () => {
const content = {
default: {title: 'Title', description: 'Description'},
info: {title: 'Title', description: 'Description'},
success: {title: 'Title', description: 'Description'},
destructive: {title: "Don't's", description: 'Title'},
};

return (
<div className="flex flex-col gap-32 pb-64 pt-32 px-32 bg-background-neutral-base">
<Header variant="h3" className="text-foreground-neutral-subtle">
INLINE TIPS
</Header>
<div className="flex flex-col gap-16">
<Code variant="label" className="text-foreground-neutral-subtle">
Primary
</Code>
{types.map((type) => (
<InlineTips key={type} type={type} variant="primary">
<InlineTipsContent>
<InlineTipsTitle>{content[type].title}</InlineTipsTitle>
<InlineTipsDescription>{content[type].description}</InlineTipsDescription>
</InlineTipsContent>
<InlineTipsActions>
<InlineTipsAction variant="primary">Label</InlineTipsAction>
<InlineTipsAction variant="secondary">Label</InlineTipsAction>
</InlineTipsActions>
</InlineTips>
))}
<Code variant="label" className="text-foreground-neutral-subtle">
Secondary
</Code>
{types.map((type) => (
<InlineTips key={type} type={type} variant="secondary">
<InlineTipsContent>
<InlineTipsTitle>{content[type].title}</InlineTipsTitle>
<InlineTipsDescription>{content[type].description}</InlineTipsDescription>
</InlineTipsContent>
<InlineTipsActions>
<InlineTipsAction variant="primary">Label</InlineTipsAction>
<InlineTipsAction variant="secondary">Label</InlineTipsAction>
</InlineTipsActions>
</InlineTips>
))}
</div>
</div>
);
},
};
Loading
Loading