@dilipod/ui 0.2.13 → 0.2.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dilipod/ui",
3
- "version": "0.2.13",
3
+ "version": "0.2.15",
4
4
  "description": "Dilipod Design System - Shared UI components and styles",
5
5
  "author": "Dilipod <hello@dilipod.com>",
6
6
  "license": "MIT",
@@ -70,7 +70,8 @@
70
70
  "@radix-ui/react-toast": "^1.2.15",
71
71
  "class-variance-authority": "^0.7.1",
72
72
  "clsx": "^2.1.1",
73
- "tailwind-merge": "^3.3.0"
73
+ "tailwind-merge": "^3.3.0",
74
+ "tailwindcss-animate": "^1.0.7"
74
75
  },
75
76
  "devDependencies": {
76
77
  "@radix-ui/react-label": "^2.1.8",
@@ -85,6 +86,7 @@
85
86
  "@types/react": "^19.0.0",
86
87
  "@types/react-dom": "^19.0.0",
87
88
  "autoprefixer": "^10.4.21",
89
+ "playwright": "^1.57.0",
88
90
  "postcss": "^8.5.4",
89
91
  "react": "^19.0.0",
90
92
  "react-dom": "^19.0.0",
@@ -0,0 +1,91 @@
1
+ 'use client'
2
+
3
+ import type { Meta, StoryObj } from '@storybook/react'
4
+ import {
5
+ Dialog,
6
+ DialogTrigger,
7
+ DialogContent,
8
+ DialogHeader,
9
+ DialogTitle,
10
+ DialogDescription,
11
+ DialogFooter,
12
+ } from './dialog'
13
+ import { Button } from './button'
14
+
15
+ const meta: Meta<typeof Dialog> = {
16
+ title: 'Components/Dialog',
17
+ component: Dialog,
18
+ parameters: {
19
+ layout: 'centered',
20
+ },
21
+ tags: ['autodocs'],
22
+ }
23
+
24
+ export default meta
25
+ type Story = StoryObj<typeof Dialog>
26
+
27
+ export const Default: Story = {
28
+ render: () => (
29
+ <Dialog>
30
+ <DialogTrigger asChild>
31
+ <Button>Open Dialog</Button>
32
+ </DialogTrigger>
33
+ <DialogContent>
34
+ <DialogHeader>
35
+ <DialogTitle>Dialog Title</DialogTitle>
36
+ <DialogDescription>
37
+ This is a dialog description. The dialog should open and close with a subtle fade and zoom animation.
38
+ </DialogDescription>
39
+ </DialogHeader>
40
+ <div className="py-4">
41
+ <p className="text-sm text-gray-600">
42
+ Click outside or press the X button to close and see the exit animation.
43
+ </p>
44
+ </div>
45
+ <DialogFooter>
46
+ <Button variant="outline">Cancel</Button>
47
+ <Button>Confirm</Button>
48
+ </DialogFooter>
49
+ </DialogContent>
50
+ </Dialog>
51
+ ),
52
+ }
53
+
54
+ export const WithForm: Story = {
55
+ render: () => (
56
+ <Dialog>
57
+ <DialogTrigger asChild>
58
+ <Button variant="outline">Edit Profile</Button>
59
+ </DialogTrigger>
60
+ <DialogContent>
61
+ <DialogHeader>
62
+ <DialogTitle>Edit Profile</DialogTitle>
63
+ <DialogDescription>
64
+ Make changes to your profile here. Click save when you're done.
65
+ </DialogDescription>
66
+ </DialogHeader>
67
+ <div className="grid gap-4 py-4">
68
+ <div className="grid gap-2">
69
+ <label htmlFor="name" className="text-sm font-medium">Name</label>
70
+ <input
71
+ id="name"
72
+ defaultValue="John Doe"
73
+ className="flex h-10 w-full rounded-md border border-gray-200 bg-white px-3 py-2 text-sm"
74
+ />
75
+ </div>
76
+ <div className="grid gap-2">
77
+ <label htmlFor="email" className="text-sm font-medium">Email</label>
78
+ <input
79
+ id="email"
80
+ defaultValue="john@example.com"
81
+ className="flex h-10 w-full rounded-md border border-gray-200 bg-white px-3 py-2 text-sm"
82
+ />
83
+ </div>
84
+ </div>
85
+ <DialogFooter>
86
+ <Button>Save changes</Button>
87
+ </DialogFooter>
88
+ </DialogContent>
89
+ </Dialog>
90
+ ),
91
+ }
@@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
21
21
  <DialogPrimitive.Overlay
22
22
  ref={ref}
23
23
  className={cn(
24
- 'fixed inset-0 z-50 bg-black/60 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 duration-150',
24
+ 'fixed inset-0 z-50 bg-black/60',
25
25
  className
26
26
  )}
27
27
  {...props}
@@ -38,7 +38,7 @@ const DialogContent = React.forwardRef<
38
38
  <DialogPrimitive.Content
39
39
  ref={ref}
40
40
  className={cn(
41
- 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-white p-6 shadow-lg duration-150 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-[0.98] data-[state=open]:zoom-in-[0.98] sm:rounded-lg',
41
+ 'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg gap-4 border border-border bg-white p-6 shadow-lg rounded-sm',
42
42
  className
43
43
  )}
44
44
  {...props}
@@ -0,0 +1,102 @@
1
+ 'use client'
2
+
3
+ import type { Meta, StoryObj } from '@storybook/react'
4
+ import {
5
+ DropdownMenu,
6
+ DropdownMenuTrigger,
7
+ DropdownMenuContent,
8
+ DropdownMenuItem,
9
+ DropdownMenuLabel,
10
+ DropdownMenuSeparator,
11
+ DropdownMenuCheckboxItem,
12
+ DropdownMenuRadioGroup,
13
+ DropdownMenuRadioItem,
14
+ DropdownMenuSub,
15
+ DropdownMenuSubTrigger,
16
+ DropdownMenuSubContent,
17
+ } from './dropdown-menu'
18
+ import { Button } from './button'
19
+
20
+ const meta: Meta<typeof DropdownMenu> = {
21
+ title: 'Components/DropdownMenu',
22
+ component: DropdownMenu,
23
+ parameters: {
24
+ layout: 'centered',
25
+ },
26
+ tags: ['autodocs'],
27
+ }
28
+
29
+ export default meta
30
+ type Story = StoryObj<typeof DropdownMenu>
31
+
32
+ export const Default: Story = {
33
+ render: () => (
34
+ <DropdownMenu>
35
+ <DropdownMenuTrigger asChild>
36
+ <Button variant="outline">Open Menu</Button>
37
+ </DropdownMenuTrigger>
38
+ <DropdownMenuContent>
39
+ <DropdownMenuLabel>My Account</DropdownMenuLabel>
40
+ <DropdownMenuSeparator />
41
+ <DropdownMenuItem>Profile</DropdownMenuItem>
42
+ <DropdownMenuItem>Settings</DropdownMenuItem>
43
+ <DropdownMenuItem>Billing</DropdownMenuItem>
44
+ <DropdownMenuSeparator />
45
+ <DropdownMenuItem>Log out</DropdownMenuItem>
46
+ </DropdownMenuContent>
47
+ </DropdownMenu>
48
+ ),
49
+ }
50
+
51
+ export const UserMenu: Story = {
52
+ render: () => (
53
+ <DropdownMenu>
54
+ <DropdownMenuTrigger asChild>
55
+ <button className="flex items-center gap-2 rounded-full hover:ring-2 hover:ring-gray-200 transition-all focus-visible:outline-none">
56
+ <div className="h-8 w-8 rounded-full bg-cyan-400 flex items-center justify-center text-sm font-semibold">
57
+ JD
58
+ </div>
59
+ </button>
60
+ </DropdownMenuTrigger>
61
+ <DropdownMenuContent align="end" className="w-64">
62
+ <DropdownMenuLabel className="px-3 py-3">
63
+ <div className="flex flex-col space-y-0.5">
64
+ <p className="text-sm font-semibold">John Doe</p>
65
+ <p className="text-xs text-gray-500 font-normal">john@example.com</p>
66
+ </div>
67
+ </DropdownMenuLabel>
68
+ <DropdownMenuSeparator />
69
+ <DropdownMenuItem className="px-3 py-2">Your profile</DropdownMenuItem>
70
+ <DropdownMenuItem className="px-3 py-2">Terms & policies</DropdownMenuItem>
71
+ <DropdownMenuItem className="px-3 py-2">Help</DropdownMenuItem>
72
+ <DropdownMenuSeparator />
73
+ <DropdownMenuItem className="px-3 py-2">Log out</DropdownMenuItem>
74
+ </DropdownMenuContent>
75
+ </DropdownMenu>
76
+ ),
77
+ }
78
+
79
+ export const WithSubMenu: Story = {
80
+ render: () => (
81
+ <DropdownMenu>
82
+ <DropdownMenuTrigger asChild>
83
+ <Button variant="outline">Options</Button>
84
+ </DropdownMenuTrigger>
85
+ <DropdownMenuContent>
86
+ <DropdownMenuItem>New Tab</DropdownMenuItem>
87
+ <DropdownMenuItem>New Window</DropdownMenuItem>
88
+ <DropdownMenuSeparator />
89
+ <DropdownMenuSub>
90
+ <DropdownMenuSubTrigger>Share</DropdownMenuSubTrigger>
91
+ <DropdownMenuSubContent>
92
+ <DropdownMenuItem>Email</DropdownMenuItem>
93
+ <DropdownMenuItem>Messages</DropdownMenuItem>
94
+ <DropdownMenuItem>Slack</DropdownMenuItem>
95
+ </DropdownMenuSubContent>
96
+ </DropdownMenuSub>
97
+ <DropdownMenuSeparator />
98
+ <DropdownMenuItem>Print</DropdownMenuItem>
99
+ </DropdownMenuContent>
100
+ </DropdownMenu>
101
+ ),
102
+ }
@@ -47,7 +47,7 @@ const DropdownMenuSubContent = React.forwardRef<
47
47
  <DropdownMenuPrimitive.SubContent
48
48
  ref={ref}
49
49
  className={cn(
50
- "z-50 min-w-[8rem] overflow-hidden rounded-sm border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-98 data-[state=open]:zoom-in-98 origin-[--radix-dropdown-menu-content-transform-origin]",
50
+ "z-50 min-w-[8rem] overflow-hidden rounded-sm border bg-popover p-1 text-popover-foreground shadow-lg",
51
51
  className
52
52
  )}
53
53
  {...props}
@@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
65
65
  ref={ref}
66
66
  sideOffset={sideOffset}
67
67
  className={cn(
68
- "z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-sm border bg-popover p-1.5 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-98 data-[state=open]:zoom-in-98 origin-[--radix-dropdown-menu-content-transform-origin]",
68
+ "z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-sm border bg-popover p-1.5 text-popover-foreground shadow-md",
69
69
  className
70
70
  )}
71
71
  {...props}
@@ -0,0 +1,36 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { cn } from '../lib/utils'
5
+
6
+ export interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
7
+ error?: boolean
8
+ }
9
+
10
+ const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
11
+ ({ className, error, children, ...props }, ref) => {
12
+ return (
13
+ <select
14
+ ref={ref}
15
+ className={cn(
16
+ 'h-10 w-full rounded-sm border bg-white px-3 py-2 text-base text-[var(--black)] ring-offset-background',
17
+ 'placeholder:text-gray-500',
18
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
19
+ 'disabled:cursor-not-allowed disabled:opacity-50 md:text-sm transition-colors',
20
+ 'appearance-none bg-[url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' fill=\'none\' viewBox=\'0 0 24 24\' stroke=\'%23374151\'%3E%3Cpath stroke-linecap=\'round\' stroke-linejoin=\'round\' stroke-width=\'2\' d=\'M19 9l-7 7-7-7\'/%3E%3C/svg%3E")] bg-[length:16px_16px] bg-[right_12px_center] bg-no-repeat pr-10',
21
+ error
22
+ ? 'border-red-500 focus-visible:ring-red-500'
23
+ : 'border-gray-300 focus-visible:ring-[var(--cyan)]',
24
+ className
25
+ )}
26
+ aria-invalid={error ? 'true' : undefined}
27
+ {...props}
28
+ >
29
+ {children}
30
+ </select>
31
+ )
32
+ }
33
+ )
34
+ Select.displayName = 'Select'
35
+
36
+ export { Select }
@@ -32,6 +32,7 @@ const toastVariants = cva(
32
32
  success: 'bg-[var(--black)] text-white',
33
33
  error: 'bg-[var(--black)] text-white',
34
34
  warning: 'bg-[var(--black)] text-white',
35
+ info: 'bg-[var(--black)] text-white',
35
36
  },
36
37
  },
37
38
  defaultVariants: {
@@ -117,7 +118,7 @@ type ToastProps = React.ComponentPropsWithoutRef<typeof Toast>
117
118
  type ToastActionElement = React.ReactElement<typeof ToastAction>
118
119
 
119
120
  // Toast icon helper
120
- const ToastIcon = ({ variant }: { variant?: 'default' | 'success' | 'error' | 'warning' }) => {
121
+ const ToastIcon = ({ variant }: { variant?: 'default' | 'success' | 'error' | 'warning' | 'info' }) => {
121
122
  switch (variant) {
122
123
  case 'success':
123
124
  return <Check size={18} weight="bold" className="text-[var(--cyan)]" />
@@ -125,6 +126,8 @@ const ToastIcon = ({ variant }: { variant?: 'default' | 'success' | 'error' | 'w
125
126
  return <WarningCircle size={18} weight="fill" className="text-red-400" />
126
127
  case 'warning':
127
128
  return <WarningCircle size={18} weight="fill" className="text-amber-400" />
129
+ case 'info':
130
+ return <Info size={18} weight="fill" className="text-[var(--cyan)]" />
128
131
  default:
129
132
  return <Info size={18} weight="fill" className="text-white/70" />
130
133
  }
package/src/index.ts CHANGED
@@ -62,6 +62,9 @@ export type { LabelProps } from '@radix-ui/react-label'
62
62
  export { Textarea } from './components/textarea'
63
63
  export type { TextareaProps } from './components/textarea'
64
64
 
65
+ export { Select } from './components/select'
66
+ export type { SelectProps } from './components/select'
67
+
65
68
  export { Checkbox } from './components/checkbox'
66
69
  export type { CheckboxProps } from './components/checkbox'
67
70
 
@@ -1,5 +1,104 @@
1
1
  @import "tailwindcss";
2
2
 
3
+ /* Animation keyframes for dialogs, dropdowns, popovers */
4
+ @keyframes dialogFadeIn {
5
+ from {
6
+ opacity: 0;
7
+ transform: translate(-50%, -50%) scale(0.98);
8
+ }
9
+ to {
10
+ opacity: 1;
11
+ transform: translate(-50%, -50%) scale(1);
12
+ }
13
+ }
14
+
15
+ @keyframes dialogFadeOut {
16
+ from {
17
+ opacity: 1;
18
+ transform: translate(-50%, -50%) scale(1);
19
+ }
20
+ to {
21
+ opacity: 0;
22
+ transform: translate(-50%, -50%) scale(0.98);
23
+ }
24
+ }
25
+
26
+ @keyframes overlayFadeIn {
27
+ from { opacity: 0; }
28
+ to { opacity: 1; }
29
+ }
30
+
31
+ @keyframes overlayFadeOut {
32
+ from { opacity: 1; }
33
+ to { opacity: 0; }
34
+ }
35
+
36
+ @keyframes popoverFadeIn {
37
+ from {
38
+ opacity: 0;
39
+ transform: scale(0.98);
40
+ }
41
+ to {
42
+ opacity: 1;
43
+ transform: scale(1);
44
+ }
45
+ }
46
+
47
+ @keyframes popoverFadeOut {
48
+ from {
49
+ opacity: 1;
50
+ transform: scale(1);
51
+ }
52
+ to {
53
+ opacity: 0;
54
+ transform: scale(0.98);
55
+ }
56
+ }
57
+
58
+ /* Dialog overlay animations */
59
+ [data-radix-dialog-overlay][data-state="open"] {
60
+ animation: overlayFadeIn 100ms ease-out;
61
+ }
62
+
63
+ [data-radix-dialog-overlay][data-state="closed"] {
64
+ animation: overlayFadeOut 100ms ease-in;
65
+ }
66
+
67
+ /* Dialog content animations */
68
+ [data-radix-dialog-content][data-state="open"],
69
+ [role="dialog"][data-state="open"] {
70
+ animation: dialogFadeIn 100ms ease-out;
71
+ }
72
+
73
+ [data-radix-dialog-content][data-state="closed"],
74
+ [role="dialog"][data-state="closed"] {
75
+ animation: dialogFadeOut 100ms ease-in;
76
+ }
77
+
78
+ /* Dropdown menu animations */
79
+ [data-radix-dropdown-menu-content][data-state="open"],
80
+ [role="menu"][data-state="open"] {
81
+ animation: popoverFadeIn 100ms ease-out;
82
+ transform-origin: var(--radix-dropdown-menu-content-transform-origin, top);
83
+ }
84
+
85
+ [data-radix-dropdown-menu-content][data-state="closed"],
86
+ [role="menu"][data-state="closed"] {
87
+ animation: popoverFadeOut 100ms ease-in;
88
+ transform-origin: var(--radix-dropdown-menu-content-transform-origin, top);
89
+ }
90
+
91
+ /* Sub-menu animations */
92
+ [data-radix-dropdown-menu-sub-content][data-state="open"] {
93
+ animation: popoverFadeIn 100ms ease-out;
94
+ transform-origin: var(--radix-dropdown-menu-content-transform-origin, top);
95
+ }
96
+
97
+ [data-radix-dropdown-menu-sub-content][data-state="closed"] {
98
+ animation: popoverFadeOut 100ms ease-in;
99
+ transform-origin: var(--radix-dropdown-menu-content-transform-origin, top);
100
+ }
101
+
3
102
  :root {
4
103
  /* ===========================================
5
104
  DILIPOD COLOR SYSTEM