@umituz/web-design-system 1.3.1 → 1.7.1

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": "@umituz/web-design-system",
3
- "version": "1.3.1",
3
+ "version": "1.7.1",
4
4
  "private": false,
5
5
  "description": "Web Design System - Atomic Design components (Atoms, Molecules, Organisms, Templates) for React applications",
6
6
  "main": "./src/index.ts",
@@ -52,15 +52,33 @@
52
52
  "url": "git+https://github.com/umituz/web-design-system.git"
53
53
  },
54
54
  "peerDependencies": {
55
+ "@radix-ui/react-accordion": ">=1.0.0",
56
+ "@radix-ui/react-collapsible": ">=1.0.0",
57
+ "@radix-ui/react-dialog": ">=1.0.0",
58
+ "@radix-ui/react-hover-card": ">=1.0.0",
59
+ "@radix-ui/react-label": ">=2.0.0",
60
+ "@radix-ui/react-popover": ">=1.0.0",
61
+ "@radix-ui/react-scroll-area": ">=1.0.0",
62
+ "@radix-ui/react-select": ">=2.0.0",
63
+ "clsx": ">=2.0.0",
64
+ "lucide-react": ">=0.400.0",
55
65
  "react": ">=18.0.0",
56
66
  "react-dom": ">=18.0.0",
57
- "clsx": ">=2.0.0",
58
67
  "tailwind-merge": ">=2.0.0"
59
68
  },
60
69
  "devDependencies": {
70
+ "@radix-ui/react-accordion": "^1.2.12",
71
+ "@radix-ui/react-collapsible": "^1.1.12",
72
+ "@radix-ui/react-dialog": "^1.1.15",
73
+ "@radix-ui/react-hover-card": "^1.1.8",
74
+ "@radix-ui/react-label": "^2.1.8",
75
+ "@radix-ui/react-popover": "^1.1.15",
76
+ "@radix-ui/react-scroll-area": "^1.2.10",
77
+ "@radix-ui/react-select": "^2.2.6",
61
78
  "@types/react": "^18.0.0",
62
79
  "@types/react-dom": "^18.0.0",
63
80
  "clsx": "^2.1.1",
81
+ "lucide-react": "^0.577.0",
64
82
  "react": "^18.0.0",
65
83
  "react-dom": "^18.0.0",
66
84
  "tailwind-merge": "^3.5.0",
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Label Component (Atom)
3
+ * @description Form label (Shadcn/ui compatible)
4
+ */
5
+
6
+ import * as React from 'react';
7
+ import * as LabelPrimitive from '@radix-ui/react-label';
8
+ import { cn } from '../../infrastructure/utils';
9
+
10
+ const Label = React.forwardRef<
11
+ React.ElementRef<typeof LabelPrimitive.Root>,
12
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
13
+ >(({ className, ...props }, ref) => (
14
+ <LabelPrimitive.Root
15
+ ref={ref}
16
+ className={cn(
17
+ 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
18
+ className
19
+ )}
20
+ {...props}
21
+ />
22
+ ));
23
+ Label.displayName = LabelPrimitive.Root.displayName;
24
+
25
+ export { Label };
@@ -45,3 +45,5 @@ export type { TooltipProps } from './Tooltip';
45
45
 
46
46
  export { Progress } from './Progress';
47
47
  export type { ProgressProps } from './Progress';
48
+
49
+ export { Label } from './Label';
@@ -1,16 +1,13 @@
1
1
  /**
2
2
  * Avatar Component (Molecule)
3
- * @description User avatar with fallback
3
+ * @description User avatar with image and fallback (shadcn/ui compatible)
4
4
  */
5
5
 
6
- import { forwardRef, type HTMLAttributes } from 'react';
6
+ import { forwardRef, type HTMLAttributes, type ElementType, type ComponentPropsWithoutRef } from 'react';
7
7
  import { cn } from '../../infrastructure/utils';
8
8
  import type { BaseProps, SizeVariant } from '../../domain/types';
9
9
 
10
10
  export interface AvatarProps extends HTMLAttributes<HTMLDivElement>, BaseProps {
11
- src?: string;
12
- alt?: string;
13
- fallback?: string;
14
11
  size?: Extract<SizeVariant, 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'>;
15
12
  }
16
13
 
@@ -23,30 +20,61 @@ const sizeStyles: Record<'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl', string> = {
23
20
  '2xl': 'h-20 w-20 text-2xl',
24
21
  };
25
22
 
26
- export const Avatar = forwardRef<HTMLDivElement, AvatarProps>(
27
- ({ className, src, alt, fallback, size = 'md', ...props }, ref) => {
28
- const hasError = !src;
29
-
23
+ const Avatar = forwardRef<HTMLDivElement, AvatarProps>(
24
+ ({ className, size = 'md', ...props }, ref) => {
30
25
  return (
31
26
  <div
32
27
  ref={ref}
33
28
  className={cn(
34
- 'relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-muted',
29
+ 'relative flex shrink-0 overflow-hidden rounded-full',
35
30
  sizeStyles[size],
36
31
  className
37
32
  )}
38
33
  {...props}
39
- >
40
- {hasError ? (
41
- <span className="font-medium text-muted-foreground">
42
- {fallback || '?'}
43
- </span>
44
- ) : (
45
- <img src={src} alt={alt || 'Avatar'} className="h-full w-full object-cover" />
46
- )}
47
- </div>
34
+ />
48
35
  );
49
36
  }
50
37
  );
51
38
 
52
39
  Avatar.displayName = 'Avatar';
40
+
41
+ export interface AvatarImageProps extends ComponentPropsWithoutRef<'img'> {
42
+ asChild?: boolean;
43
+ }
44
+
45
+ const AvatarImage = forwardRef<HTMLImageElement, AvatarImageProps>(
46
+ ({ className, src, alt, ...props }, ref) => {
47
+ return (
48
+ <img
49
+ ref={ref}
50
+ src={src}
51
+ alt={alt}
52
+ className={cn('aspect-square h-full w-full object-cover', className)}
53
+ {...props}
54
+ />
55
+ );
56
+ }
57
+ );
58
+
59
+ AvatarImage.displayName = 'AvatarImage';
60
+
61
+ export interface AvatarFallbackProps extends HTMLAttributes<HTMLDivElement> {}
62
+
63
+ const AvatarFallback = forwardRef<HTMLDivElement, AvatarFallbackProps>(
64
+ ({ className, ...props }, ref) => {
65
+ return (
66
+ <div
67
+ ref={ref}
68
+ className={cn(
69
+ 'flex h-full w-full items-center justify-center rounded-full bg-muted',
70
+ className
71
+ )}
72
+ {...props}
73
+ />
74
+ );
75
+ }
76
+ );
77
+
78
+ AvatarFallback.displayName = 'AvatarFallback';
79
+
80
+ export { Avatar, AvatarImage, AvatarFallback };
@@ -0,0 +1,128 @@
1
+ /**
2
+ * ListItem Component (Molecule)
3
+ * @description Reusable list item with icon, content, and actions
4
+ * Reduces boilerplate in list components throughout the app
5
+ */
6
+
7
+ import { forwardRef, type ReactNode, type MouseEvent } from 'react';
8
+ import { cn } from '../../infrastructure/utils';
9
+ import { Button } from '../atoms';
10
+ import type { BaseProps } from '../../domain/types';
11
+
12
+ export interface ListItemProps extends BaseProps {
13
+ title: string;
14
+ description?: string;
15
+ icon?: ReactNode;
16
+ actions?: ReactNode;
17
+ leftContent?: ReactNode;
18
+ rightContent?: ReactNode;
19
+ onClick?: () => void;
20
+ href?: string;
21
+ disabled?: boolean;
22
+ selected?: boolean;
23
+ size?: 'sm' | 'md' | 'lg';
24
+ variant?: 'default' | 'bordered' | 'ghost';
25
+ }
26
+
27
+ const sizeStyles = {
28
+ sm: 'p-3 gap-3',
29
+ md: 'p-4 gap-4',
30
+ lg: 'p-5 gap-5',
31
+ };
32
+
33
+ const titleSizeStyles = {
34
+ sm: 'text-sm',
35
+ md: 'text-base',
36
+ lg: 'text-lg',
37
+ };
38
+
39
+ const descriptionSizeStyles = {
40
+ sm: 'text-xs',
41
+ md: 'text-sm',
42
+ lg: 'text-base',
43
+ };
44
+
45
+ const iconSizeStyles = {
46
+ sm: 'h-4 w-4',
47
+ md: 'h-5 w-5',
48
+ lg: 'h-6 w-6',
49
+ };
50
+
51
+ export const ListItem = forwardRef<HTMLDivElement, ListItemProps>(
52
+ (
53
+ {
54
+ className,
55
+ title,
56
+ description,
57
+ icon,
58
+ actions,
59
+ leftContent,
60
+ rightContent,
61
+ onClick,
62
+ href,
63
+ disabled = false,
64
+ selected = false,
65
+ size = 'md',
66
+ variant = 'default',
67
+ ...props
68
+ },
69
+ ref
70
+ ) => {
71
+ const baseClasses = cn(
72
+ 'flex items-center justify-between w-full transition-all duration-200',
73
+ sizeStyles[size],
74
+ !disabled && onClick && 'cursor-pointer hover:bg-muted/50 active:scale-[0.98]',
75
+ selected && 'bg-muted/50 border-primary',
76
+ variant === 'bordered' && 'border border-border rounded-lg',
77
+ variant === 'ghost' && 'hover:bg-muted/30',
78
+ disabled && 'opacity-50 cursor-not-allowed',
79
+ className
80
+ );
81
+
82
+ const content = (
83
+ <>
84
+ {leftContent || icon ? (
85
+ <div className="flex items-center gap-3 flex-shrink-0">
86
+ {icon && <div className={cn(iconSizeStyles[size], 'text-muted-foreground')}>{icon}</div>}
87
+ {!icon && leftContent}
88
+ </div>
89
+ ) : null}
90
+
91
+ <div className="flex-1 min-w-0">
92
+ <p className={cn('font-medium text-foreground truncate', titleSizeStyles[size])}>{title}</p>
93
+ {description && (
94
+ <p className={cn('text-muted-foreground truncate', descriptionSizeStyles[size])}>{description}</p>
95
+ )}
96
+ </div>
97
+
98
+ {rightContent || actions ? (
99
+ <div className="flex items-center gap-2 flex-shrink-0">
100
+ {rightContent}
101
+ {actions}
102
+ </div>
103
+ ) : null}
104
+ </>
105
+ );
106
+
107
+ if (href && !disabled) {
108
+ return (
109
+ <a href={href} ref={ref as any} className={baseClasses} {...(props as any)}>
110
+ {content}
111
+ </a>
112
+ );
113
+ }
114
+
115
+ return (
116
+ <div
117
+ ref={ref}
118
+ className={baseClasses}
119
+ onClick={disabled ? undefined : onClick}
120
+ {...props}
121
+ >
122
+ {content}
123
+ </div>
124
+ );
125
+ }
126
+ );
127
+
128
+ ListItem.displayName = 'ListItem';
@@ -0,0 +1,50 @@
1
+ /**
2
+ * ScrollArea Component (Molecule)
3
+ * @description Custom scrollable area (Shadcn/ui compatible)
4
+ */
5
+
6
+ import * as React from 'react';
7
+ import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
8
+ import { cn } from '../../infrastructure/utils';
9
+
10
+ const ScrollArea = React.forwardRef<
11
+ React.ElementRef<typeof ScrollAreaPrimitive.Root>,
12
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>
13
+ >(({ className, children, ...props }, ref) => (
14
+ <ScrollAreaPrimitive.Root
15
+ ref={ref}
16
+ className={cn('relative overflow-hidden', className)}
17
+ {...props}
18
+ >
19
+ <ScrollAreaPrimitive.Viewport className="h-full w-full rounded-[inherit]">
20
+ {children}
21
+ </ScrollAreaPrimitive.Viewport>
22
+ <ScrollBar />
23
+ <ScrollAreaPrimitive.Corner />
24
+ </ScrollAreaPrimitive.Root>
25
+ ));
26
+ ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
27
+
28
+ const ScrollBar = React.forwardRef<
29
+ React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,
30
+ React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>
31
+ >(({ className, orientation = 'vertical', ...props }, ref) => (
32
+ <ScrollAreaPrimitive.ScrollAreaScrollbar
33
+ ref={ref}
34
+ orientation={orientation}
35
+ className={cn(
36
+ 'flex touch-none select-none transition-colors',
37
+ orientation === 'vertical' &&
38
+ 'h-full w-2.5 border-l border-l-transparent p-[1px]',
39
+ orientation === 'horizontal' &&
40
+ 'h-2.5 flex-col border-t border-t-transparent p-[1px]',
41
+ className
42
+ )}
43
+ {...props}
44
+ >
45
+ <ScrollAreaPrimitive.ScrollAreaThumb className="relative flex-1 rounded-full bg-border" />
46
+ </ScrollAreaPrimitive.ScrollAreaScrollbar>
47
+ ));
48
+ ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
49
+
50
+ export { ScrollArea, ScrollBar };
@@ -1,41 +1,160 @@
1
1
  /**
2
2
  * Select Component (Molecule)
3
- * @description Dropdown select input
3
+ * @description Dropdown select (Shadcn/Radix UI compatible)
4
4
  */
5
5
 
6
- import { forwardRef, type SelectHTMLAttributes } from 'react';
6
+ import * as React from 'react';
7
+ import * as SelectPrimitive from '@radix-ui/react-select';
8
+ import { Check, ChevronDown, ChevronUp } from 'lucide-react';
7
9
  import { cn } from '../../infrastructure/utils';
8
- import type { BaseProps } from '../../domain/types';
9
-
10
- export interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement>, BaseProps {
11
- error?: boolean;
12
- options: Array<{ value: string; label: string; disabled?: boolean }>;
13
- }
14
-
15
- export const Select = forwardRef<HTMLSelectElement, SelectProps>(
16
- ({ className, error, options, disabled, ...props }, ref) => {
17
- return (
18
- <select
19
- ref={ref}
20
- disabled={disabled}
10
+
11
+ const Select = SelectPrimitive.Root;
12
+
13
+ const SelectGroup = SelectPrimitive.Group;
14
+
15
+ const SelectValue = SelectPrimitive.Value;
16
+
17
+ const SelectTrigger = React.forwardRef<
18
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
19
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
20
+ >(({ className, children, ...props }, ref) => (
21
+ <SelectPrimitive.Trigger
22
+ ref={ref}
23
+ className={cn(
24
+ 'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
25
+ className
26
+ )}
27
+ {...props}
28
+ >
29
+ {children}
30
+ <SelectPrimitive.Icon asChild>
31
+ <ChevronDown className="h-4 w-4 opacity-50" />
32
+ </SelectPrimitive.Icon>
33
+ </SelectPrimitive.Trigger>
34
+ ));
35
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
36
+
37
+ const SelectScrollUpButton = React.forwardRef<
38
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
39
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
40
+ >(({ className, ...props }, ref) => (
41
+ <SelectPrimitive.ScrollUpButton
42
+ ref={ref}
43
+ className={cn(
44
+ 'flex cursor-default items-center justify-center py-1',
45
+ className
46
+ )}
47
+ {...props}
48
+ >
49
+ <ChevronUp className="h-4 w-4" />
50
+ </SelectPrimitive.ScrollUpButton>
51
+ ));
52
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
53
+
54
+ const SelectScrollDownButton = React.forwardRef<
55
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
56
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
57
+ >(({ className, ...props }, ref) => (
58
+ <SelectPrimitive.ScrollDownButton
59
+ ref={ref}
60
+ className={cn(
61
+ 'flex cursor-default items-center justify-center py-1',
62
+ className
63
+ )}
64
+ {...props}
65
+ >
66
+ <ChevronDown className="h-4 w-4" />
67
+ </SelectPrimitive.ScrollDownButton>
68
+ ));
69
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
70
+
71
+ const SelectContent = React.forwardRef<
72
+ React.ElementRef<typeof SelectPrimitive.Content>,
73
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
74
+ >(({ className, children, position = 'popper', ...props }, ref) => (
75
+ <SelectPrimitive.Portal>
76
+ <SelectPrimitive.Content
77
+ ref={ref}
78
+ className={cn(
79
+ 'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover 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-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
80
+ position === 'popper' &&
81
+ 'data-[side=bottom]:translate-y-1 data-[side=left]:translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:translate-y-1',
82
+ className
83
+ )}
84
+ position={position}
85
+ {...props}
86
+ >
87
+ <SelectScrollUpButton />
88
+ <SelectPrimitive.Viewport
21
89
  className={cn(
22
- 'flex h-9 w-full rounded-md border border-input bg-background px-3 py-2',
23
- 'text-sm ring-offset-background',
24
- 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
25
- 'disabled:cursor-not-allowed disabled:opacity-50',
26
- error && 'border-destructive',
27
- className
90
+ 'p-1',
91
+ position === 'popper' &&
92
+ 'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
28
93
  )}
29
- {...props}
30
94
  >
31
- {options.map((option) => (
32
- <option key={option.value} value={option.value} disabled={option.disabled}>
33
- {option.label}
34
- </option>
35
- ))}
36
- </select>
37
- );
38
- }
39
- );
40
-
41
- Select.displayName = 'Select';
95
+ {children}
96
+ </SelectPrimitive.Viewport>
97
+ <SelectScrollDownButton />
98
+ </SelectPrimitive.Content>
99
+ </SelectPrimitive.Portal>
100
+ ));
101
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
102
+
103
+ const SelectLabel = React.forwardRef<
104
+ React.ElementRef<typeof SelectPrimitive.Label>,
105
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
106
+ >(({ className, ...props }, ref) => (
107
+ <SelectPrimitive.Label
108
+ ref={ref}
109
+ className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
110
+ {...props}
111
+ />
112
+ ));
113
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
114
+
115
+ const SelectItem = React.forwardRef<
116
+ React.ElementRef<typeof SelectPrimitive.Item>,
117
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
118
+ >(({ className, children, ...props }, ref) => (
119
+ <SelectPrimitive.Item
120
+ ref={ref}
121
+ className={cn(
122
+ 'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
123
+ className
124
+ )}
125
+ {...props}
126
+ >
127
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
128
+ <SelectPrimitive.ItemIndicator>
129
+ <Check className="h-4 w-4" />
130
+ </SelectPrimitive.ItemIndicator>
131
+ </span>
132
+ {children}
133
+ </SelectPrimitive.Item>
134
+ ));
135
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
136
+
137
+ const SelectSeparator = React.forwardRef<
138
+ React.ElementRef<typeof SelectPrimitive.Separator>,
139
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
140
+ >(({ className, ...props }, ref) => (
141
+ <SelectPrimitive.Separator
142
+ ref={ref}
143
+ className={cn('-mx-1 my-1 h-px bg-muted', className)}
144
+ {...props}
145
+ />
146
+ ));
147
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
148
+
149
+ export {
150
+ Select,
151
+ SelectGroup,
152
+ SelectValue,
153
+ SelectTrigger,
154
+ SelectContent,
155
+ SelectLabel,
156
+ SelectItem,
157
+ SelectSeparator,
158
+ SelectScrollUpButton,
159
+ SelectScrollDownButton,
160
+ };
@@ -10,8 +10,8 @@ export type { FormFieldProps } from './FormField';
10
10
  export { SearchBox } from './SearchBox';
11
11
  export type { SearchBoxProps } from './SearchBox';
12
12
 
13
- export { Avatar } from './Avatar';
14
- export type { AvatarProps } from './Avatar';
13
+ export { Avatar, AvatarImage, AvatarFallback } from './Avatar';
14
+ export type { AvatarProps, AvatarImageProps, AvatarFallbackProps } from './Avatar';
15
15
 
16
16
  export { Chip } from './Chip';
17
17
  export type { ChipProps } from './Chip';
@@ -19,7 +19,7 @@ export type { ChipProps } from './Chip';
19
19
  export { Toggle } from './Toggle';
20
20
  export type { ToggleProps } from './Toggle';
21
21
 
22
- export { Select } from './Select';
22
+ export { Select, SelectGroup, SelectValue, SelectTrigger, SelectContent, SelectLabel, SelectItem, SelectSeparator, SelectScrollUpButton, SelectScrollDownButton } from './Select';
23
23
  export type { SelectProps } from './Select';
24
24
 
25
25
  export { Textarea } from './Textarea';
@@ -33,3 +33,8 @@ export type { CheckboxGroupProps, CheckboxOption } from './CheckboxGroup';
33
33
 
34
34
  export { InputGroup, GroupedInput } from './InputGroup';
35
35
  export type { InputGroupProps } from './InputGroup';
36
+
37
+ export { ScrollArea, ScrollBar } from './ScrollArea';
38
+
39
+ export { ListItem } from './ListItem';
40
+ export type { ListItemProps } from './ListItem';