@dilipod/ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +226 -0
- package/dist/index.d.mts +133 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.js +888 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +552 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +89 -0
- package/src/components/badge.stories.tsx +99 -0
- package/src/components/badge.tsx +86 -0
- package/src/components/button.stories.tsx +136 -0
- package/src/components/button.tsx +63 -0
- package/src/components/card.stories.tsx +107 -0
- package/src/components/card.tsx +80 -0
- package/src/components/icon-box.stories.tsx +99 -0
- package/src/components/icon-box.tsx +57 -0
- package/src/components/logo.stories.tsx +93 -0
- package/src/components/logo.tsx +91 -0
- package/src/components/progress.stories.tsx +99 -0
- package/src/components/progress.tsx +70 -0
- package/src/components/sheet.stories.tsx +109 -0
- package/src/components/sheet.tsx +140 -0
- package/src/components/stat.stories.tsx +127 -0
- package/src/components/stat.tsx +115 -0
- package/src/components/tag.stories.tsx +107 -0
- package/src/components/tag.tsx +53 -0
- package/src/icons.ts +107 -0
- package/src/index.ts +50 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles/globals.css +187 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { cn } from '../lib/utils'
|
|
5
|
+
|
|
6
|
+
const Card = React.forwardRef<
|
|
7
|
+
HTMLDivElement,
|
|
8
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
9
|
+
>(({ className, ...props }, ref) => (
|
|
10
|
+
<div
|
|
11
|
+
ref={ref}
|
|
12
|
+
className={cn(
|
|
13
|
+
'rounded-sm border border-gray-200 bg-white shadow-sm',
|
|
14
|
+
className
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
))
|
|
19
|
+
Card.displayName = 'Card'
|
|
20
|
+
|
|
21
|
+
const CardHeader = React.forwardRef<
|
|
22
|
+
HTMLDivElement,
|
|
23
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
24
|
+
>(({ className, ...props }, ref) => (
|
|
25
|
+
<div
|
|
26
|
+
ref={ref}
|
|
27
|
+
className={cn('flex flex-col space-y-1.5 p-6', className)}
|
|
28
|
+
{...props}
|
|
29
|
+
/>
|
|
30
|
+
))
|
|
31
|
+
CardHeader.displayName = 'CardHeader'
|
|
32
|
+
|
|
33
|
+
const CardTitle = React.forwardRef<
|
|
34
|
+
HTMLHeadingElement,
|
|
35
|
+
React.HTMLAttributes<HTMLHeadingElement>
|
|
36
|
+
>(({ className, ...props }, ref) => (
|
|
37
|
+
<h3
|
|
38
|
+
ref={ref}
|
|
39
|
+
className={cn(
|
|
40
|
+
'text-lg font-bold leading-none tracking-tight text-[var(--black)]',
|
|
41
|
+
className
|
|
42
|
+
)}
|
|
43
|
+
{...props}
|
|
44
|
+
/>
|
|
45
|
+
))
|
|
46
|
+
CardTitle.displayName = 'CardTitle'
|
|
47
|
+
|
|
48
|
+
const CardDescription = React.forwardRef<
|
|
49
|
+
HTMLParagraphElement,
|
|
50
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
51
|
+
>(({ className, ...props }, ref) => (
|
|
52
|
+
<p
|
|
53
|
+
ref={ref}
|
|
54
|
+
className={cn('text-sm text-gray-500', className)}
|
|
55
|
+
{...props}
|
|
56
|
+
/>
|
|
57
|
+
))
|
|
58
|
+
CardDescription.displayName = 'CardDescription'
|
|
59
|
+
|
|
60
|
+
const CardContent = React.forwardRef<
|
|
61
|
+
HTMLDivElement,
|
|
62
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
63
|
+
>(({ className, ...props }, ref) => (
|
|
64
|
+
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
|
|
65
|
+
))
|
|
66
|
+
CardContent.displayName = 'CardContent'
|
|
67
|
+
|
|
68
|
+
const CardFooter = React.forwardRef<
|
|
69
|
+
HTMLDivElement,
|
|
70
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
71
|
+
>(({ className, ...props }, ref) => (
|
|
72
|
+
<div
|
|
73
|
+
ref={ref}
|
|
74
|
+
className={cn('flex items-center p-6 pt-0', className)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
))
|
|
78
|
+
CardFooter.displayName = 'CardFooter'
|
|
79
|
+
|
|
80
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { Brain, Files, Robot, ChartBar, CheckCircle } from '@phosphor-icons/react'
|
|
3
|
+
import { IconBox } from './icon-box'
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof IconBox> = {
|
|
6
|
+
title: 'Components/IconBox',
|
|
7
|
+
component: IconBox,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
argTypes: {
|
|
10
|
+
variant: {
|
|
11
|
+
control: 'select',
|
|
12
|
+
options: ['default', 'primary', 'outline', 'ghost', 'gradient'],
|
|
13
|
+
},
|
|
14
|
+
size: {
|
|
15
|
+
control: 'select',
|
|
16
|
+
options: ['sm', 'default', 'lg', 'xl'],
|
|
17
|
+
},
|
|
18
|
+
rounded: {
|
|
19
|
+
control: 'select',
|
|
20
|
+
options: ['sm', 'default', 'full'],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default meta
|
|
26
|
+
type Story = StoryObj<typeof IconBox>
|
|
27
|
+
|
|
28
|
+
export const Default: Story = {
|
|
29
|
+
args: {
|
|
30
|
+
children: <Brain size={20} weight="fill" />,
|
|
31
|
+
variant: 'default',
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Primary: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
children: <Brain size={20} weight="fill" />,
|
|
38
|
+
variant: 'primary',
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const Outline: Story = {
|
|
43
|
+
args: {
|
|
44
|
+
children: <Brain size={20} weight="fill" />,
|
|
45
|
+
variant: 'outline',
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const Ghost: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
children: <Brain size={20} weight="fill" />,
|
|
52
|
+
variant: 'ghost',
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const Gradient: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
children: <Brain size={20} weight="fill" />,
|
|
59
|
+
variant: 'gradient',
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const Rounded: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
children: <Brain size={20} weight="fill" />,
|
|
66
|
+
variant: 'default',
|
|
67
|
+
rounded: 'full',
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const Sizes: Story = {
|
|
72
|
+
render: () => (
|
|
73
|
+
<div className="flex gap-4 items-center">
|
|
74
|
+
<IconBox size="sm"><Brain size={16} weight="fill" /></IconBox>
|
|
75
|
+
<IconBox size="default"><Brain size={20} weight="fill" /></IconBox>
|
|
76
|
+
<IconBox size="lg"><Brain size={24} weight="fill" /></IconBox>
|
|
77
|
+
<IconBox size="xl"><Brain size={28} weight="fill" /></IconBox>
|
|
78
|
+
</div>
|
|
79
|
+
),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const AllVariants: Story = {
|
|
83
|
+
render: () => (
|
|
84
|
+
<div className="flex flex-col gap-4">
|
|
85
|
+
<div className="flex gap-4 items-center">
|
|
86
|
+
<IconBox variant="default"><Brain size={20} weight="fill" /></IconBox>
|
|
87
|
+
<IconBox variant="primary"><Files size={20} weight="fill" /></IconBox>
|
|
88
|
+
<IconBox variant="outline"><Robot size={20} weight="fill" /></IconBox>
|
|
89
|
+
<IconBox variant="ghost"><ChartBar size={20} weight="fill" /></IconBox>
|
|
90
|
+
<IconBox variant="gradient"><CheckCircle size={20} weight="fill" /></IconBox>
|
|
91
|
+
</div>
|
|
92
|
+
<div className="flex gap-4 items-center">
|
|
93
|
+
<IconBox variant="default" rounded="full"><Brain size={20} weight="fill" /></IconBox>
|
|
94
|
+
<IconBox variant="primary" rounded="full"><Files size={20} weight="fill" /></IconBox>
|
|
95
|
+
<IconBox variant="outline" rounded="full"><Robot size={20} weight="fill" /></IconBox>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
),
|
|
99
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
5
|
+
import { cn } from '../lib/utils'
|
|
6
|
+
|
|
7
|
+
const iconBoxVariants = cva(
|
|
8
|
+
'inline-flex items-center justify-center shrink-0',
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
default: 'bg-[var(--black)] text-white',
|
|
13
|
+
primary: 'bg-[var(--cyan)] text-[var(--black)]',
|
|
14
|
+
outline: 'border-2 border-gray-200 bg-white text-[var(--black)]',
|
|
15
|
+
ghost: 'bg-gray-100 text-gray-600',
|
|
16
|
+
gradient: 'bg-gradient-to-br from-[var(--black)] to-[var(--cyan-dark)] text-white',
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
sm: 'w-8 h-8',
|
|
20
|
+
default: 'w-10 h-10',
|
|
21
|
+
lg: 'w-12 h-12',
|
|
22
|
+
xl: 'w-14 h-14',
|
|
23
|
+
},
|
|
24
|
+
rounded: {
|
|
25
|
+
sm: 'rounded-sm',
|
|
26
|
+
default: 'rounded-md',
|
|
27
|
+
full: 'rounded-full',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
defaultVariants: {
|
|
31
|
+
variant: 'default',
|
|
32
|
+
size: 'default',
|
|
33
|
+
rounded: 'sm',
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
export interface IconBoxProps
|
|
39
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
40
|
+
VariantProps<typeof iconBoxVariants> {}
|
|
41
|
+
|
|
42
|
+
const IconBox = React.forwardRef<HTMLDivElement, IconBoxProps>(
|
|
43
|
+
({ className, variant, size, rounded, children, ...props }, ref) => {
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
ref={ref}
|
|
47
|
+
className={cn(iconBoxVariants({ variant, size, rounded }), className)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
{children}
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
IconBox.displayName = 'IconBox'
|
|
56
|
+
|
|
57
|
+
export { IconBox, iconBoxVariants }
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { Logo } from './logo'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Logo> = {
|
|
5
|
+
title: 'Components/Logo',
|
|
6
|
+
component: Logo,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
argTypes: {
|
|
9
|
+
variant: {
|
|
10
|
+
control: 'select',
|
|
11
|
+
options: ['dark', 'light'],
|
|
12
|
+
},
|
|
13
|
+
size: {
|
|
14
|
+
control: 'select',
|
|
15
|
+
options: ['sm', 'md', 'lg'],
|
|
16
|
+
},
|
|
17
|
+
href: {
|
|
18
|
+
control: 'text',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default meta
|
|
24
|
+
type Story = StoryObj<typeof Logo>
|
|
25
|
+
|
|
26
|
+
export const Dark: Story = {
|
|
27
|
+
args: {
|
|
28
|
+
variant: 'dark',
|
|
29
|
+
size: 'md',
|
|
30
|
+
},
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const Light: Story = {
|
|
34
|
+
args: {
|
|
35
|
+
variant: 'light',
|
|
36
|
+
size: 'md',
|
|
37
|
+
},
|
|
38
|
+
parameters: {
|
|
39
|
+
backgrounds: { default: 'dark' },
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const Small: Story = {
|
|
44
|
+
args: {
|
|
45
|
+
variant: 'dark',
|
|
46
|
+
size: 'sm',
|
|
47
|
+
},
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const Medium: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
variant: 'dark',
|
|
53
|
+
size: 'md',
|
|
54
|
+
},
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const Large: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
variant: 'dark',
|
|
60
|
+
size: 'lg',
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export const WithLink: Story = {
|
|
65
|
+
args: {
|
|
66
|
+
variant: 'dark',
|
|
67
|
+
size: 'md',
|
|
68
|
+
href: '/',
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const AllSizes: Story = {
|
|
73
|
+
render: () => (
|
|
74
|
+
<div className="flex flex-col gap-8">
|
|
75
|
+
<div>
|
|
76
|
+
<p className="text-sm text-gray-500 mb-2">Dark variant (for light backgrounds)</p>
|
|
77
|
+
<div className="flex items-center gap-8">
|
|
78
|
+
<Logo variant="dark" size="sm" />
|
|
79
|
+
<Logo variant="dark" size="md" />
|
|
80
|
+
<Logo variant="dark" size="lg" />
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<div className="bg-[var(--black)] p-6 rounded-lg">
|
|
84
|
+
<p className="text-sm text-gray-400 mb-2">Light variant (for dark backgrounds)</p>
|
|
85
|
+
<div className="flex items-center gap-8">
|
|
86
|
+
<Logo variant="light" size="sm" />
|
|
87
|
+
<Logo variant="light" size="md" />
|
|
88
|
+
<Logo variant="light" size="lg" />
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
),
|
|
93
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
|
|
5
|
+
interface LogoProps {
|
|
6
|
+
variant?: 'dark' | 'light' // dark = black text, light = white text
|
|
7
|
+
size?: 'sm' | 'md' | 'lg'
|
|
8
|
+
href?: string
|
|
9
|
+
className?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const sizes = {
|
|
13
|
+
sm: { width: 100, height: 30 },
|
|
14
|
+
md: { width: 130, height: 40 },
|
|
15
|
+
lg: { width: 160, height: 50 },
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Logo component that renders the Dilipod logo as inline SVG
|
|
20
|
+
*
|
|
21
|
+
* @param variant - 'dark' for light backgrounds (black text), 'light' for dark backgrounds (white text)
|
|
22
|
+
* @param size - 'sm' | 'md' | 'lg'
|
|
23
|
+
* @param href - Optional link href (wraps logo in anchor tag)
|
|
24
|
+
* @param className - Additional CSS classes
|
|
25
|
+
*/
|
|
26
|
+
export function Logo({
|
|
27
|
+
variant = 'dark',
|
|
28
|
+
size = 'md',
|
|
29
|
+
href,
|
|
30
|
+
className,
|
|
31
|
+
}: LogoProps) {
|
|
32
|
+
const s = sizes[size]
|
|
33
|
+
const textColor = variant === 'light' ? 'white' : '#0A0A0A'
|
|
34
|
+
const iconTextColor = variant === 'light' ? 'white' : '#0A0A0A'
|
|
35
|
+
|
|
36
|
+
const svgContent = (
|
|
37
|
+
<svg
|
|
38
|
+
width={s.width}
|
|
39
|
+
height={s.height}
|
|
40
|
+
viewBox="0 0 130 40"
|
|
41
|
+
fill="none"
|
|
42
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
43
|
+
className={className}
|
|
44
|
+
>
|
|
45
|
+
<defs>
|
|
46
|
+
<linearGradient id={`gradient-${variant}`} x1="0%" y1="0%" x2="100%" y2="100%">
|
|
47
|
+
<stop offset="0%" stopColor="#007A70" />
|
|
48
|
+
<stop offset="50%" stopColor="#00B8A9" />
|
|
49
|
+
<stop offset="100%" stopColor="#00E5D4" />
|
|
50
|
+
</linearGradient>
|
|
51
|
+
</defs>
|
|
52
|
+
{/* Icon */}
|
|
53
|
+
<rect width="40" height="40" rx="4" fill={`url(#gradient-${variant})`} />
|
|
54
|
+
<text
|
|
55
|
+
x="20"
|
|
56
|
+
y="29"
|
|
57
|
+
textAnchor="middle"
|
|
58
|
+
fontFamily="system-ui, -apple-system, sans-serif"
|
|
59
|
+
fontSize="24"
|
|
60
|
+
fontWeight="700"
|
|
61
|
+
fill={iconTextColor}
|
|
62
|
+
>
|
|
63
|
+
D
|
|
64
|
+
</text>
|
|
65
|
+
{/* Text */}
|
|
66
|
+
<text
|
|
67
|
+
x="52"
|
|
68
|
+
y="28"
|
|
69
|
+
fontFamily="system-ui, -apple-system, sans-serif"
|
|
70
|
+
fontSize="20"
|
|
71
|
+
fontWeight="700"
|
|
72
|
+
letterSpacing="1"
|
|
73
|
+
fill={textColor}
|
|
74
|
+
>
|
|
75
|
+
Dilipod
|
|
76
|
+
</text>
|
|
77
|
+
</svg>
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if (href) {
|
|
81
|
+
return (
|
|
82
|
+
<a href={href} className="inline-flex items-center group">
|
|
83
|
+
{svgContent}
|
|
84
|
+
</a>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return <div className="inline-flex items-center">{svgContent}</div>
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export type { LogoProps }
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { Progress } from './progress'
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof Progress> = {
|
|
5
|
+
title: 'Components/Progress',
|
|
6
|
+
component: Progress,
|
|
7
|
+
tags: ['autodocs'],
|
|
8
|
+
argTypes: {
|
|
9
|
+
value: {
|
|
10
|
+
control: { type: 'range', min: 0, max: 100, step: 1 },
|
|
11
|
+
},
|
|
12
|
+
variant: {
|
|
13
|
+
control: 'select',
|
|
14
|
+
options: ['default', 'success', 'warning', 'error', 'gradient'],
|
|
15
|
+
},
|
|
16
|
+
size: {
|
|
17
|
+
control: 'select',
|
|
18
|
+
options: ['sm', 'default', 'lg'],
|
|
19
|
+
},
|
|
20
|
+
showLabel: {
|
|
21
|
+
control: 'boolean',
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default meta
|
|
27
|
+
type Story = StoryObj<typeof Progress>
|
|
28
|
+
|
|
29
|
+
export const Default: Story = {
|
|
30
|
+
args: {
|
|
31
|
+
value: 60,
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const WithLabel: Story = {
|
|
36
|
+
args: {
|
|
37
|
+
value: 75,
|
|
38
|
+
showLabel: true,
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const Gradient: Story = {
|
|
43
|
+
args: {
|
|
44
|
+
value: 84,
|
|
45
|
+
variant: 'gradient',
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const Success: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
value: 100,
|
|
52
|
+
variant: 'success',
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const Warning: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
value: 45,
|
|
59
|
+
variant: 'warning',
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export const Error: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
value: 25,
|
|
66
|
+
variant: 'error',
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const Sizes: Story = {
|
|
71
|
+
render: () => (
|
|
72
|
+
<div className="space-y-4 w-64">
|
|
73
|
+
<div>
|
|
74
|
+
<p className="text-xs text-gray-500 mb-1">Small</p>
|
|
75
|
+
<Progress value={60} size="sm" />
|
|
76
|
+
</div>
|
|
77
|
+
<div>
|
|
78
|
+
<p className="text-xs text-gray-500 mb-1">Default</p>
|
|
79
|
+
<Progress value={60} size="default" />
|
|
80
|
+
</div>
|
|
81
|
+
<div>
|
|
82
|
+
<p className="text-xs text-gray-500 mb-1">Large</p>
|
|
83
|
+
<Progress value={60} size="lg" />
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export const AllVariants: Story = {
|
|
90
|
+
render: () => (
|
|
91
|
+
<div className="space-y-4 w-64">
|
|
92
|
+
<Progress value={84} variant="default" showLabel />
|
|
93
|
+
<Progress value={100} variant="success" />
|
|
94
|
+
<Progress value={45} variant="warning" />
|
|
95
|
+
<Progress value={25} variant="error" />
|
|
96
|
+
<Progress value={70} variant="gradient" />
|
|
97
|
+
</div>
|
|
98
|
+
),
|
|
99
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
5
|
+
import { cn } from '../lib/utils'
|
|
6
|
+
|
|
7
|
+
const progressVariants = cva('h-full rounded-full transition-all duration-300', {
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: 'bg-[var(--cyan)]',
|
|
11
|
+
success: 'bg-green-500',
|
|
12
|
+
warning: 'bg-amber-500',
|
|
13
|
+
error: 'bg-red-500',
|
|
14
|
+
gradient: 'bg-gradient-to-r from-[var(--cyan-dark)] to-[var(--cyan)]',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
variant: 'default',
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export interface ProgressProps
|
|
23
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
24
|
+
VariantProps<typeof progressVariants> {
|
|
25
|
+
/** Progress value (0-100) */
|
|
26
|
+
value: number
|
|
27
|
+
/** Show percentage label */
|
|
28
|
+
showLabel?: boolean
|
|
29
|
+
/** Custom label text (defaults to "Progress") */
|
|
30
|
+
label?: string
|
|
31
|
+
/** Size of the progress bar */
|
|
32
|
+
size?: 'sm' | 'default' | 'lg'
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const Progress = React.forwardRef<HTMLDivElement, ProgressProps>(
|
|
36
|
+
({ className, value, variant, showLabel, label = 'Progress', size = 'default', ...props }, ref) => {
|
|
37
|
+
const clampedValue = Math.min(100, Math.max(0, value))
|
|
38
|
+
|
|
39
|
+
const heightClass = {
|
|
40
|
+
sm: 'h-1',
|
|
41
|
+
default: 'h-1.5',
|
|
42
|
+
lg: 'h-2.5',
|
|
43
|
+
}[size]
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className={cn('w-full', className)} ref={ref} {...props}>
|
|
47
|
+
{showLabel && (
|
|
48
|
+
<div className="flex justify-between text-xs mb-1">
|
|
49
|
+
<span className="text-gray-500">{label}</span>
|
|
50
|
+
<span className="font-semibold text-[var(--black)]">{clampedValue}%</span>
|
|
51
|
+
</div>
|
|
52
|
+
)}
|
|
53
|
+
<div
|
|
54
|
+
className={cn(
|
|
55
|
+
'w-full bg-gray-200 rounded-full overflow-hidden',
|
|
56
|
+
heightClass
|
|
57
|
+
)}
|
|
58
|
+
>
|
|
59
|
+
<div
|
|
60
|
+
className={cn(progressVariants({ variant }))}
|
|
61
|
+
style={{ width: `${clampedValue}%` }}
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
Progress.displayName = 'Progress'
|
|
69
|
+
|
|
70
|
+
export { Progress, progressVariants }
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react'
|
|
2
|
+
import { Button } from './button'
|
|
3
|
+
import {
|
|
4
|
+
Sheet,
|
|
5
|
+
SheetContent,
|
|
6
|
+
SheetDescription,
|
|
7
|
+
SheetHeader,
|
|
8
|
+
SheetTitle,
|
|
9
|
+
SheetTrigger,
|
|
10
|
+
SheetFooter,
|
|
11
|
+
SheetClose,
|
|
12
|
+
} from './sheet'
|
|
13
|
+
|
|
14
|
+
const meta: Meta<typeof Sheet> = {
|
|
15
|
+
title: 'Components/Sheet',
|
|
16
|
+
component: Sheet,
|
|
17
|
+
tags: ['autodocs'],
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default meta
|
|
21
|
+
type Story = StoryObj<typeof Sheet>
|
|
22
|
+
|
|
23
|
+
export const Default: Story = {
|
|
24
|
+
render: () => (
|
|
25
|
+
<Sheet>
|
|
26
|
+
<SheetTrigger asChild>
|
|
27
|
+
<Button variant="outline">Open Sheet</Button>
|
|
28
|
+
</SheetTrigger>
|
|
29
|
+
<SheetContent>
|
|
30
|
+
<SheetHeader>
|
|
31
|
+
<SheetTitle>Sheet Title</SheetTitle>
|
|
32
|
+
<SheetDescription>
|
|
33
|
+
This is a sheet description. It can contain any content you want.
|
|
34
|
+
</SheetDescription>
|
|
35
|
+
</SheetHeader>
|
|
36
|
+
<div className="py-4">
|
|
37
|
+
<p className="text-sm text-gray-600">
|
|
38
|
+
Sheet content goes here. You can add forms, lists, or any other content.
|
|
39
|
+
</p>
|
|
40
|
+
</div>
|
|
41
|
+
<SheetFooter>
|
|
42
|
+
<SheetClose asChild>
|
|
43
|
+
<Button variant="outline">Cancel</Button>
|
|
44
|
+
</SheetClose>
|
|
45
|
+
<Button>Save Changes</Button>
|
|
46
|
+
</SheetFooter>
|
|
47
|
+
</SheetContent>
|
|
48
|
+
</Sheet>
|
|
49
|
+
),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const LeftSide: Story = {
|
|
53
|
+
render: () => (
|
|
54
|
+
<Sheet>
|
|
55
|
+
<SheetTrigger asChild>
|
|
56
|
+
<Button variant="outline">Open Left Sheet</Button>
|
|
57
|
+
</SheetTrigger>
|
|
58
|
+
<SheetContent side="left">
|
|
59
|
+
<SheetHeader>
|
|
60
|
+
<SheetTitle>Navigation</SheetTitle>
|
|
61
|
+
</SheetHeader>
|
|
62
|
+
<nav className="flex flex-col gap-2 py-4">
|
|
63
|
+
<a href="#" className="px-4 py-2 text-sm hover:bg-gray-100 rounded-sm">Home</a>
|
|
64
|
+
<a href="#" className="px-4 py-2 text-sm hover:bg-gray-100 rounded-sm">About</a>
|
|
65
|
+
<a href="#" className="px-4 py-2 text-sm hover:bg-gray-100 rounded-sm">Services</a>
|
|
66
|
+
<a href="#" className="px-4 py-2 text-sm hover:bg-gray-100 rounded-sm">Contact</a>
|
|
67
|
+
</nav>
|
|
68
|
+
</SheetContent>
|
|
69
|
+
</Sheet>
|
|
70
|
+
),
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const TopSide: Story = {
|
|
74
|
+
render: () => (
|
|
75
|
+
<Sheet>
|
|
76
|
+
<SheetTrigger asChild>
|
|
77
|
+
<Button variant="outline">Open Top Sheet</Button>
|
|
78
|
+
</SheetTrigger>
|
|
79
|
+
<SheetContent side="top" className="h-auto">
|
|
80
|
+
<SheetHeader>
|
|
81
|
+
<SheetTitle>Notification</SheetTitle>
|
|
82
|
+
<SheetDescription>
|
|
83
|
+
You have new updates available.
|
|
84
|
+
</SheetDescription>
|
|
85
|
+
</SheetHeader>
|
|
86
|
+
</SheetContent>
|
|
87
|
+
</Sheet>
|
|
88
|
+
),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export const BottomSide: Story = {
|
|
92
|
+
render: () => (
|
|
93
|
+
<Sheet>
|
|
94
|
+
<SheetTrigger asChild>
|
|
95
|
+
<Button variant="outline">Open Bottom Sheet</Button>
|
|
96
|
+
</SheetTrigger>
|
|
97
|
+
<SheetContent side="bottom" className="h-auto">
|
|
98
|
+
<SheetHeader>
|
|
99
|
+
<SheetTitle>Actions</SheetTitle>
|
|
100
|
+
</SheetHeader>
|
|
101
|
+
<div className="flex gap-2 py-4">
|
|
102
|
+
<Button variant="outline" className="flex-1">Share</Button>
|
|
103
|
+
<Button variant="outline" className="flex-1">Download</Button>
|
|
104
|
+
<Button variant="destructive" className="flex-1">Delete</Button>
|
|
105
|
+
</div>
|
|
106
|
+
</SheetContent>
|
|
107
|
+
</Sheet>
|
|
108
|
+
),
|
|
109
|
+
}
|