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
5 changes: 5 additions & 0 deletions .changeset/hungry-jars-show.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shipfox/react-ui": minor
---

Add toast components and refactor
2 changes: 1 addition & 1 deletion libs/react/ui/.storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '../index.css';
import type {Decorator, Preview} from '@storybook/react';
import {ThemeProvider} from '../src/components/theme-provider';
import {ThemeProvider} from '../src/components/theme';

const withTheme: Decorator = (Story, context) => {
return <ThemeProvider defaultTheme={context.globals.theme}>{Story()}</ThemeProvider>;
Expand Down
4 changes: 2 additions & 2 deletions libs/react/ui/src/components/alert/alert.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const meta = {
argTypes: {
variant: {
control: 'select',
options: ['default', 'info', 'success', 'warning', 'destructive'],
options: ['default', 'info', 'success', 'warning', 'error'],
},
},
args: {
Expand All @@ -29,7 +29,7 @@ export default meta;

type Story = StoryObj<typeof meta>;

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

export const Default: Story = {
render: (args) => {
Expand Down
6 changes: 3 additions & 3 deletions libs/react/ui/src/components/alert/alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const alertVariants = cva(
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',
error: 'bg-tag-error-bg text-foreground-neutral-base border-tag-error-border',
},
},
defaultVariants: {
Expand All @@ -28,7 +28,7 @@ const alertLineVariants = cva('w-4 self-stretch rounded-full', {
info: 'bg-tag-blue-icon',
success: 'bg-tag-success-icon',
warning: 'bg-tag-warning-icon',
destructive: 'bg-tag-error-icon',
error: 'bg-tag-error-icon',
},
},
defaultVariants: {
Expand All @@ -43,7 +43,7 @@ const closeIconVariants = cva('w-16 h-16', {
info: 'text-tag-blue-icon',
success: 'text-tag-success-icon',
warning: 'text-tag-warning-icon',
destructive: 'text-tag-error-icon',
error: 'text-tag-error-icon',
},
},
defaultVariants: {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {Slot} from '@radix-ui/react-slot';
import {cva, type VariantProps} from 'class-variance-authority';
import {Icon, type IconName} from 'components/icon';
import type {ComponentProps} from 'react';
import {cn} from 'utils/cn';
import {Icon, type IconName} from './icon/icon';

export const buttonVariants = cva(
'rounded-6 inline-flex items-center justify-center whitespace-nowrap transition-colors disabled:pointer-events-none shrink-0 outline-none',
Expand Down
1 change: 1 addition & 0 deletions libs/react/ui/src/components/button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './button';
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type {Meta, StoryObj} from '@storybook/react';
import {Button} from 'components/button/button';
import {ItemTitle} from 'components/item';
import {cn} from 'utils/cn';
import illustration1 from '../../assets/illustration-1.svg';
import illustration2 from '../../assets/illustration-2.svg';
import illustrationBg from '../../assets/illustration-gradient.svg';
import {Avatar} from '../avatar/avatar';
import {AvatarGroup, AvatarGroupTooltip} from '../avatar/avatar-group';
import {Button} from '../button';
import {Icon} from '../icon/icon';
import {MovingBorder} from '../moving-border/moving-border';
import {DynamicItem} from './dynamic-item';
Expand Down
4 changes: 3 additions & 1 deletion libs/react/ui/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ export * from './checkbox';
export * from './dynamic-item';
export * from './icon';
export * from './inline-tips';
export * from './input';
export * from './item';
export * from './label';
export * from './textarea';
export * from './theme-provider';
export * from './theme';
export * from './toast';
export * from './typography';
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const meta = {
argTypes: {
type: {
control: 'select',
options: ['default', 'info', 'success', 'destructive'],
options: ['default', 'info', 'success', 'error'],
},
variant: {
control: 'select',
Expand All @@ -33,7 +33,7 @@ export default meta;

type Story = StoryObj<typeof meta>;

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

export const Default: Story = {
Expand Down Expand Up @@ -80,7 +80,7 @@ export const DesignMock: Story = {
default: {title: 'Title', description: 'Description'},
info: {title: 'Title', description: 'Description'},
success: {title: 'Title', description: 'Description'},
destructive: {title: "Don't's", description: 'Title'},
error: {title: 'Title', description: 'Description'},
};

return (
Expand Down
4 changes: 2 additions & 2 deletions libs/react/ui/src/components/inline-tips/inline-tips.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const inlineTipsLineVariants = cva('w-4 self-stretch my-4 rounded-full', {
default: 'bg-tag-neutral-icon',
info: 'bg-tag-warning-icon',
success: 'bg-tag-success-icon',
destructive: 'bg-tag-error-icon',
error: 'bg-tag-error-icon',
},
},
defaultVariants: {
Expand All @@ -31,7 +31,7 @@ const inlineTipsLineVariants = cva('w-4 self-stretch my-4 rounded-full', {

type InlineTipsProps = ComponentProps<'div'> &
VariantProps<typeof inlineTipsBaseVariants> & {
type?: 'default' | 'info' | 'success' | 'destructive';
type?: 'default' | 'info' | 'success' | 'error';
};

function InlineTips({className, variant, type = 'default', children, ...props}: InlineTipsProps) {
Expand Down
1 change: 1 addition & 0 deletions libs/react/ui/src/components/input/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './input';
10 changes: 5 additions & 5 deletions libs/react/ui/src/components/item/item.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type {Meta, StoryObj} from '@storybook/react';
import {Button} from '../button';
import {Icon} from '../icon/icon';
import {Input} from '../input';
import {Label} from '../label/label';
import {Button} from 'components/button/button';
import {Icon} from 'components/icon/icon';
import {Input} from 'components/input/input';
import {Label} from 'components/label/label';
import {
Item,
ItemActions,
Expand All @@ -13,7 +13,7 @@ import {
ItemHeader,
ItemSeparator,
ItemTitle,
} from './';
} from './item';

const meta = {
title: 'Components/Item',
Expand Down
2 changes: 1 addition & 1 deletion libs/react/ui/src/components/label/label.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {Meta, StoryObj} from '@storybook/react';
import {Checkbox} from 'components/checkbox';
import {Input} from 'components/input';
import {Input} from 'components/input/input';
import {Textarea} from 'components/textarea';
import {Label} from './label';

Expand Down
1 change: 1 addition & 0 deletions libs/react/ui/src/components/theme/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './theme-provider';
2 changes: 2 additions & 0 deletions libs/react/ui/src/components/toast/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export {Toaster, type ToasterProps, toast} from './toast';
export {ToastCustom, type ToastCustomProps} from './toast-custom';
154 changes: 154 additions & 0 deletions libs/react/ui/src/components/toast/toast-custom.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import {cva, type VariantProps} from 'class-variance-authority';
import {Icon} from 'components/icon/icon';
import type {ReactNode} from 'react';
import {cn} from 'utils/cn';

const toastCustomVariants = cva('group relative flex items-start gap-8', {
variants: {
variant: {
default: 'text-tag-neutral-icon',
info: 'text-tag-neutral-icon',
success: 'text-tag-success-icon',
warning: 'text-tag-warning-icon',
error: 'text-tag-error-icon',
},
},
defaultVariants: {
variant: 'default',
},
});

export type ToastAction = {
label: string;
onClick: () => void;
};

export type ToastCustomProps = VariantProps<typeof toastCustomVariants> & {
title?: string;
description?: string;
content?: string;
actions?: ToastAction[];
onClose?: () => void;
className?: string;
};

function getDefaultIcon(variant: ToastCustomProps['variant'] = 'default'): ReactNode {
switch (variant) {
case 'info':
return <Icon name="info" size={20} />;
case 'success':
return <Icon name="checkCircleSolid" size={20} />;
case 'warning':
return <Icon name="info" size={20} />;
case 'error':
return <Icon name="xCircleSolid" size={20} />;
default:
return <Icon name="info" size={20} />;
}
}

export function ToastCustom({
variant = 'default',
title,
description,
content,
actions,
onClose,
className,
}: ToastCustomProps) {
const hasTitle = Boolean(title);
const hasDescription = Boolean(description);
const hasContent = Boolean(content);
const hasActions = Boolean(actions && actions.length > 0);
const isSimple = hasContent && !hasTitle && !hasDescription;

return (
<div data-slot="toast-custom" className={cn(toastCustomVariants({variant}), className)}>
<div className="w-20 h-20 rounded-full flex items-start justify-center shrink-0 pt-1">
{getDefaultIcon(variant)}
</div>

<div className="flex-1 min-w-0">
{hasTitle && (
<div
data-slot="toast-custom-title"
className="font-medium text-sm leading-20 text-foreground-neutral-base mb-4"
>
{title}
</div>
)}
{hasDescription && (
<div
data-slot="toast-custom-description"
className="text-xs leading-20 text-foreground-neutral-muted mb-8"
>
{description}
</div>
)}
{isSimple ? (
<div className="flex items-center justify-between gap-16 min-w-400 mr-30">
<div
data-slot="toast-custom-content"
className="text-sm leading-20 text-foreground-neutral-base"
>
{content}
</div>
{hasActions && (
<div data-slot="toast-custom-actions" className="flex items-center gap-16">
{actions?.map((action) => (
<button
key={action.label}
data-slot="toast-custom-action"
type="button"
onClick={action.onClick}
className="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"
>
{action.label}
</button>
))}
</div>
)}
</div>
) : (
<>
{hasContent && !hasTitle && (
<div
data-slot="toast-custom-content"
className="text-sm leading-20 text-foreground-neutral-base mb-8"
>
{content}
</div>
)}
{hasActions && (
<div data-slot="toast-custom-actions" className="flex items-center gap-16 mt-8">
{actions?.map((action) => (
<button
key={action.label}
data-slot="toast-custom-action"
type="button"
onClick={action.onClick}
className="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"
>
{action.label}
</button>
))}
</div>
)}
</>
)}
</div>

{onClose && (
<button
data-slot="toast-custom-close"
type="button"
onClick={onClose}
className="absolute cursor-pointer -top-2 -right-2 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 shrink-0"
aria-label="Close"
>
<Icon name="close" className="w-16 h-16" />
</button>
)}
</div>
);
}
Loading