@connectycube/react-ui-kit 0.0.16 → 0.0.18
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/configs/dependencies.json +32 -3
- package/configs/imports.json +13 -2
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/types/components/alert-dialog.d.ts.map +1 -1
- package/dist/types/components/avatar.d.ts +2 -2
- package/dist/types/components/avatar.d.ts.map +1 -1
- package/dist/types/components/badge.d.ts +11 -0
- package/dist/types/components/badge.d.ts.map +1 -0
- package/dist/types/components/button.d.ts +3 -5
- package/dist/types/components/button.d.ts.map +1 -1
- package/dist/types/components/dialog-item.d.ts +46 -0
- package/dist/types/components/dialog-item.d.ts.map +1 -0
- package/dist/types/components/file-picker.d.ts +22 -0
- package/dist/types/components/file-picker.d.ts.map +1 -0
- package/dist/types/components/formatted-date.d.ts +8 -0
- package/dist/types/components/formatted-date.d.ts.map +1 -0
- package/dist/types/components/input.d.ts +5 -0
- package/dist/types/components/input.d.ts.map +1 -0
- package/dist/types/components/label.d.ts +5 -0
- package/dist/types/components/label.d.ts.map +1 -0
- package/dist/types/components/link-preview.d.ts +21 -0
- package/dist/types/components/link-preview.d.ts.map +1 -0
- package/dist/types/components/linkify-text.d.ts +9 -0
- package/dist/types/components/linkify-text.d.ts.map +1 -0
- package/dist/types/components/search.d.ts +15 -0
- package/dist/types/components/search.d.ts.map +1 -0
- package/dist/types/components/spinner.d.ts +12 -0
- package/dist/types/components/spinner.d.ts.map +1 -0
- package/dist/types/components/status-sent.d.ts +7 -0
- package/dist/types/components/status-sent.d.ts.map +1 -0
- package/dist/types/components/stream-view.d.ts.map +1 -1
- package/dist/types/components/utils.d.ts +0 -2
- package/dist/types/components/utils.d.ts.map +1 -1
- package/gen/components/alert-dialog.jsx +3 -1
- package/gen/components/avatar.jsx +13 -3
- package/gen/components/badge.jsx +45 -0
- package/gen/components/button.jsx +6 -9
- package/gen/components/dialog-item.jsx +149 -0
- package/gen/components/file-picker.jsx +200 -0
- package/gen/components/formatted-date.jsx +57 -0
- package/gen/components/input.jsx +23 -0
- package/gen/components/label.jsx +22 -0
- package/gen/components/link-preview.jsx +131 -0
- package/gen/components/linkify-text.jsx +31 -0
- package/gen/components/search.jsx +75 -0
- package/gen/components/spinner.jsx +36 -0
- package/gen/components/status-sent.jsx +21 -0
- package/gen/components/stream-view.jsx +5 -1
- package/gen/components/utils.js +0 -11
- package/package.json +5 -2
- package/src/components/alert-dialog.tsx +3 -1
- package/src/components/avatar.tsx +16 -6
- package/src/components/badge.tsx +42 -0
- package/src/components/button.tsx +12 -14
- package/src/components/connectycube-ui/avatar.jsx +54 -0
- package/src/components/connectycube-ui/avatar.tsx +77 -0
- package/src/components/connectycube-ui/badge.jsx +45 -0
- package/src/components/connectycube-ui/badge.tsx +42 -0
- package/src/components/connectycube-ui/dialog-item.jsx +149 -0
- package/src/components/connectycube-ui/dialog-item.tsx +188 -0
- package/src/components/connectycube-ui/file-picker.jsx +200 -0
- package/src/components/connectycube-ui/file-picker.tsx +231 -0
- package/src/components/connectycube-ui/formatted-date.jsx +57 -0
- package/src/components/connectycube-ui/formatted-date.tsx +57 -0
- package/src/components/connectycube-ui/label.jsx +22 -0
- package/src/components/connectycube-ui/label.tsx +23 -0
- package/src/components/connectycube-ui/linkify-text.tsx +40 -0
- package/src/components/connectycube-ui/presence.jsx +81 -0
- package/src/components/connectycube-ui/presence.tsx +96 -0
- package/src/components/connectycube-ui/status-sent.jsx +21 -0
- package/src/components/connectycube-ui/status-sent.tsx +25 -0
- package/src/components/connectycube-ui/utils.js +10 -0
- package/src/components/connectycube-ui/utils.ts +10 -0
- package/src/components/dialog-item.tsx +188 -0
- package/src/components/file-picker.tsx +231 -0
- package/src/components/formatted-date.tsx +57 -0
- package/src/components/input.tsx +26 -0
- package/src/components/label.tsx +23 -0
- package/src/components/link-preview.tsx +149 -0
- package/src/components/linkify-text.tsx +41 -0
- package/src/components/placeholder-text.tsx +1 -1
- package/src/components/search.tsx +86 -0
- package/src/components/spinner.tsx +42 -0
- package/src/components/status-sent.tsx +25 -0
- package/src/components/stream-view.tsx +9 -5
- package/src/components/utils.ts +0 -11
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/types/components/animated-loader.d.ts +0 -10
- package/dist/types/components/animated-loader.d.ts.map +0 -1
- package/gen/components/animated-loader.jsx +0 -12
- package/src/components/animated-loader.tsx +0 -16
|
@@ -2,11 +2,11 @@ import type React from 'react';
|
|
|
2
2
|
import { memo, forwardRef } from 'react';
|
|
3
3
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
4
4
|
import { PresenceBadge, type PresenceStatus, type PresenceBadgeProps } from './presence';
|
|
5
|
-
import { cn
|
|
5
|
+
import { cn } from './utils';
|
|
6
6
|
|
|
7
7
|
interface AvatarProps extends AvatarPrimitive.AvatarProps {
|
|
8
|
-
src?: string;
|
|
9
|
-
name?: string;
|
|
8
|
+
src?: string | undefined;
|
|
9
|
+
name?: string | undefined;
|
|
10
10
|
online?: boolean;
|
|
11
11
|
presence?: PresenceStatus;
|
|
12
12
|
onlineProps?: React.ComponentProps<'div'>;
|
|
@@ -15,6 +15,13 @@ interface AvatarProps extends AvatarPrimitive.AvatarProps {
|
|
|
15
15
|
fallbackProps?: AvatarPrimitive.AvatarFallbackProps;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function getInitialsFromName(name?: string): string {
|
|
19
|
+
const words = name?.trim().split(/\s+/).filter(Boolean) ?? [];
|
|
20
|
+
const result = words.length > 1 ? `${words[0]?.[0]}${words[1]?.[0]}` : (words[0]?.slice(0, 2) ?? 'NA');
|
|
21
|
+
|
|
22
|
+
return result.toUpperCase();
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
function AvatarBase(
|
|
19
26
|
{
|
|
20
27
|
src,
|
|
@@ -28,7 +35,7 @@ function AvatarBase(
|
|
|
28
35
|
fallbackProps,
|
|
29
36
|
...props
|
|
30
37
|
}: AvatarProps,
|
|
31
|
-
ref: React.
|
|
38
|
+
ref: React.ForwardedRef<HTMLDivElement>
|
|
32
39
|
) {
|
|
33
40
|
const initials = getInitialsFromName(name);
|
|
34
41
|
|
|
@@ -48,13 +55,16 @@ function AvatarBase(
|
|
|
48
55
|
{online && (
|
|
49
56
|
<div
|
|
50
57
|
{...onlineProps}
|
|
51
|
-
className={cn(
|
|
58
|
+
className={cn(
|
|
59
|
+
'absolute top-0 right-0 rounded-full border-2 bg-green-600 border-green-200 size-3.5',
|
|
60
|
+
onlineProps?.className
|
|
61
|
+
)}
|
|
52
62
|
/>
|
|
53
63
|
)}
|
|
54
64
|
<PresenceBadge
|
|
55
65
|
status={presence}
|
|
56
66
|
{...presenceProps}
|
|
57
|
-
className={cn('absolute
|
|
67
|
+
className={cn('absolute bottom-0 right-0', presenceProps?.className)}
|
|
58
68
|
/>
|
|
59
69
|
</AvatarPrimitive.Root>
|
|
60
70
|
);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
4
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
5
|
+
import { cn } from './utils';
|
|
6
|
+
|
|
7
|
+
interface BadgeProps extends React.HTMLAttributes<HTMLElement>, VariantProps<typeof badgeVariants> {
|
|
8
|
+
asChild?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const badgeVariants = cva(
|
|
12
|
+
'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
|
|
13
|
+
{
|
|
14
|
+
variants: {
|
|
15
|
+
variant: {
|
|
16
|
+
default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
|
|
17
|
+
secondary: 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
|
|
18
|
+
destructive:
|
|
19
|
+
'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
|
20
|
+
outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
variant: 'default',
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
function BadgeBase(
|
|
30
|
+
{ className, variant, asChild = false, ...props }: BadgeProps,
|
|
31
|
+
ref?: React.ForwardedRef<HTMLElement>
|
|
32
|
+
) {
|
|
33
|
+
const Comp = asChild ? Slot : 'span';
|
|
34
|
+
|
|
35
|
+
return <Comp ref={ref} {...props} className={cn(badgeVariants({ variant }), className)} />;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const Badge = forwardRef<HTMLElement, BadgeProps>(BadgeBase);
|
|
39
|
+
|
|
40
|
+
Badge.displayName = 'Badge';
|
|
41
|
+
|
|
42
|
+
export { Badge, type BadgeProps };
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
2
|
import { forwardRef } from 'react';
|
|
3
|
-
import
|
|
3
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
4
4
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
5
5
|
import { cn } from './utils';
|
|
6
6
|
|
|
7
|
+
interface ButtonProps extends React.ComponentProps<'button'>, VariantProps<typeof buttonVariants> {
|
|
8
|
+
asChild?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
7
11
|
const buttonVariants = cva(
|
|
8
12
|
'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*=size-])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
|
9
13
|
{
|
|
@@ -32,24 +36,18 @@ const buttonVariants = cva(
|
|
|
32
36
|
}
|
|
33
37
|
);
|
|
34
38
|
|
|
35
|
-
interface ButtonProps extends React.ComponentProps<'button'>, VariantProps<typeof buttonVariants> {
|
|
36
|
-
asChild?: boolean;
|
|
37
|
-
leftElement?: React.ReactElement;
|
|
38
|
-
rightElement?: React.ReactElement;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
39
|
function ButtonBase(
|
|
42
|
-
{ asChild = false, variant, size, className,
|
|
40
|
+
{ asChild = false, variant, size, className, ...props }: ButtonProps,
|
|
43
41
|
ref?: React.ForwardedRef<HTMLButtonElement>
|
|
44
42
|
) {
|
|
45
|
-
const Comp = asChild ?
|
|
43
|
+
const Comp = asChild ? Slot : 'button';
|
|
46
44
|
|
|
47
45
|
return (
|
|
48
|
-
<Comp
|
|
49
|
-
{
|
|
50
|
-
|
|
51
|
-
{
|
|
52
|
-
|
|
46
|
+
<Comp
|
|
47
|
+
ref={ref}
|
|
48
|
+
{...props}
|
|
49
|
+
className={cn(buttonVariants({ variant, size, className }), 'transition-all ease-in-out duration-300')}
|
|
50
|
+
/>
|
|
53
51
|
);
|
|
54
52
|
}
|
|
55
53
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { memo, forwardRef } from 'react';
|
|
2
|
+
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
3
|
+
import { PresenceBadge } from './presence';
|
|
4
|
+
import { cn } from './utils';
|
|
5
|
+
|
|
6
|
+
function getInitialsFromName(name) {
|
|
7
|
+
const words = name?.trim().split(/\s+/).filter(Boolean) ?? [];
|
|
8
|
+
const result = words.length > 1 ? `${words[0]?.[0]}${words[1]?.[0]}` : (words[0]?.slice(0, 2) ?? 'NA');
|
|
9
|
+
|
|
10
|
+
return result.toUpperCase();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function AvatarBase(
|
|
14
|
+
{ src, name = 'NA', online, presence, className, onlineProps, presenceProps, imageProps, fallbackProps, ...props },
|
|
15
|
+
ref
|
|
16
|
+
) {
|
|
17
|
+
const initials = getInitialsFromName(name);
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<AvatarPrimitive.Root ref={ref} {...props} className={cn('relative flex size-8 shrink-0 rounded-full', className)}>
|
|
21
|
+
<AvatarPrimitive.Image
|
|
22
|
+
{...imageProps}
|
|
23
|
+
src={src}
|
|
24
|
+
className={cn('aspect-square size-full rounded-full overflow-hidden object-cover', imageProps?.className)}
|
|
25
|
+
/>
|
|
26
|
+
<AvatarPrimitive.Fallback
|
|
27
|
+
{...fallbackProps}
|
|
28
|
+
className={cn('bg-muted size-full rounded-full flex items-center justify-center', fallbackProps?.className)}
|
|
29
|
+
>
|
|
30
|
+
{initials}
|
|
31
|
+
</AvatarPrimitive.Fallback>
|
|
32
|
+
{online && (
|
|
33
|
+
<div
|
|
34
|
+
{...onlineProps}
|
|
35
|
+
className={cn(
|
|
36
|
+
'absolute top-0 right-0 rounded-full border-2 bg-green-600 border-green-200 size-3.5',
|
|
37
|
+
onlineProps?.className
|
|
38
|
+
)}
|
|
39
|
+
/>
|
|
40
|
+
)}
|
|
41
|
+
<PresenceBadge
|
|
42
|
+
status={presence}
|
|
43
|
+
{...presenceProps}
|
|
44
|
+
className={cn('absolute bottom-0 right-0', presenceProps?.className)}
|
|
45
|
+
/>
|
|
46
|
+
</AvatarPrimitive.Root>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const Avatar = memo(forwardRef(AvatarBase));
|
|
51
|
+
|
|
52
|
+
Avatar.displayName = 'Avatar';
|
|
53
|
+
|
|
54
|
+
export { Avatar };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { memo, forwardRef } from 'react';
|
|
3
|
+
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
4
|
+
import { PresenceBadge, type PresenceStatus, type PresenceBadgeProps } from './presence';
|
|
5
|
+
import { cn } from './utils';
|
|
6
|
+
|
|
7
|
+
interface AvatarProps extends AvatarPrimitive.AvatarProps {
|
|
8
|
+
src?: string;
|
|
9
|
+
name?: string;
|
|
10
|
+
online?: boolean;
|
|
11
|
+
presence?: PresenceStatus;
|
|
12
|
+
onlineProps?: React.ComponentProps<'div'>;
|
|
13
|
+
presenceProps?: PresenceBadgeProps;
|
|
14
|
+
imageProps?: AvatarPrimitive.AvatarImageProps;
|
|
15
|
+
fallbackProps?: AvatarPrimitive.AvatarFallbackProps;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getInitialsFromName(name?: string): string {
|
|
19
|
+
const words = name?.trim().split(/\s+/).filter(Boolean) ?? [];
|
|
20
|
+
const result = words.length > 1 ? `${words[0]?.[0]}${words[1]?.[0]}` : (words[0]?.slice(0, 2) ?? 'NA');
|
|
21
|
+
|
|
22
|
+
return result.toUpperCase();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function AvatarBase(
|
|
26
|
+
{
|
|
27
|
+
src,
|
|
28
|
+
name = 'NA',
|
|
29
|
+
online,
|
|
30
|
+
presence,
|
|
31
|
+
className,
|
|
32
|
+
onlineProps,
|
|
33
|
+
presenceProps,
|
|
34
|
+
imageProps,
|
|
35
|
+
fallbackProps,
|
|
36
|
+
...props
|
|
37
|
+
}: AvatarProps,
|
|
38
|
+
ref: React.ForwardedRef<HTMLDivElement>
|
|
39
|
+
) {
|
|
40
|
+
const initials = getInitialsFromName(name);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<AvatarPrimitive.Root ref={ref} {...props} className={cn('relative flex size-8 shrink-0 rounded-full', className)}>
|
|
44
|
+
<AvatarPrimitive.Image
|
|
45
|
+
{...imageProps}
|
|
46
|
+
src={src}
|
|
47
|
+
className={cn('aspect-square size-full rounded-full overflow-hidden object-cover', imageProps?.className)}
|
|
48
|
+
/>
|
|
49
|
+
<AvatarPrimitive.Fallback
|
|
50
|
+
{...fallbackProps}
|
|
51
|
+
className={cn('bg-muted size-full rounded-full flex items-center justify-center', fallbackProps?.className)}
|
|
52
|
+
>
|
|
53
|
+
{initials}
|
|
54
|
+
</AvatarPrimitive.Fallback>
|
|
55
|
+
{online && (
|
|
56
|
+
<div
|
|
57
|
+
{...onlineProps}
|
|
58
|
+
className={cn(
|
|
59
|
+
'absolute top-0 right-0 rounded-full border-2 bg-green-600 border-green-200 size-3.5',
|
|
60
|
+
onlineProps?.className
|
|
61
|
+
)}
|
|
62
|
+
/>
|
|
63
|
+
)}
|
|
64
|
+
<PresenceBadge
|
|
65
|
+
status={presence}
|
|
66
|
+
{...presenceProps}
|
|
67
|
+
className={cn('absolute bottom-0 right-0', presenceProps?.className)}
|
|
68
|
+
/>
|
|
69
|
+
</AvatarPrimitive.Root>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const Avatar = memo(forwardRef<HTMLDivElement, AvatarProps>(AvatarBase));
|
|
74
|
+
|
|
75
|
+
Avatar.displayName = 'Avatar';
|
|
76
|
+
|
|
77
|
+
export { Avatar, type AvatarProps };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { forwardRef } from 'react';
|
|
2
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
3
|
+
import { cva } from 'class-variance-authority';
|
|
4
|
+
import { cn } from './utils';
|
|
5
|
+
|
|
6
|
+
const badgeVariants = cva(
|
|
7
|
+
'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
|
|
12
|
+
secondary: 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
|
|
13
|
+
destructive:
|
|
14
|
+
'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
|
15
|
+
outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
variant: 'default',
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
function BadgeBase({ className, variant, asChild = false, ...props }, ref) {
|
|
25
|
+
const Comp = asChild ? Slot : 'span';
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Comp
|
|
29
|
+
ref={ref}
|
|
30
|
+
{...props}
|
|
31
|
+
className={cn(
|
|
32
|
+
badgeVariants({
|
|
33
|
+
variant,
|
|
34
|
+
}),
|
|
35
|
+
className
|
|
36
|
+
)}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const Badge = forwardRef(BadgeBase);
|
|
42
|
+
|
|
43
|
+
Badge.displayName = 'Badge';
|
|
44
|
+
|
|
45
|
+
export { Badge };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
4
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
5
|
+
import { cn } from './utils';
|
|
6
|
+
|
|
7
|
+
interface BadgeProps extends React.HTMLAttributes<HTMLElement>, VariantProps<typeof badgeVariants> {
|
|
8
|
+
asChild?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const badgeVariants = cva(
|
|
12
|
+
'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden',
|
|
13
|
+
{
|
|
14
|
+
variants: {
|
|
15
|
+
variant: {
|
|
16
|
+
default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90',
|
|
17
|
+
secondary: 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90',
|
|
18
|
+
destructive:
|
|
19
|
+
'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
|
20
|
+
outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: {
|
|
24
|
+
variant: 'default',
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
function BadgeBase(
|
|
30
|
+
{ className, variant, asChild = false, ...props }: BadgeProps,
|
|
31
|
+
ref?: React.ForwardedRef<HTMLElement>
|
|
32
|
+
) {
|
|
33
|
+
const Comp = asChild ? Slot : 'span';
|
|
34
|
+
|
|
35
|
+
return <Comp ref={ref} {...props} className={cn(badgeVariants({ variant }), className)} />;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const Badge = forwardRef<HTMLElement, BadgeProps>(BadgeBase);
|
|
39
|
+
|
|
40
|
+
Badge.displayName = 'Badge';
|
|
41
|
+
|
|
42
|
+
export { Badge, type BadgeProps };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { forwardRef, memo } from 'react';
|
|
2
|
+
import { Users } from 'lucide-react';
|
|
3
|
+
import { cn } from './utils';
|
|
4
|
+
import { Avatar } from './avatar';
|
|
5
|
+
import { StatusSent } from './status-sent';
|
|
6
|
+
import { FormattedDate } from './formatted-date';
|
|
7
|
+
import { Badge } from './badge';
|
|
8
|
+
|
|
9
|
+
function DialogItemBase(
|
|
10
|
+
{
|
|
11
|
+
index = -1,
|
|
12
|
+
isPrivateDialog,
|
|
13
|
+
selected,
|
|
14
|
+
onSelect = () => {},
|
|
15
|
+
name,
|
|
16
|
+
photo,
|
|
17
|
+
userOnline,
|
|
18
|
+
userPresence,
|
|
19
|
+
hasGroupIcon,
|
|
20
|
+
lastSentStatus,
|
|
21
|
+
lastSentDate,
|
|
22
|
+
lastSenderName,
|
|
23
|
+
lastMessage,
|
|
24
|
+
typingStatusText,
|
|
25
|
+
draft,
|
|
26
|
+
draftLabel = 'Draft',
|
|
27
|
+
unreadCount = 0,
|
|
28
|
+
language = 'en',
|
|
29
|
+
divider = 'bottom',
|
|
30
|
+
avatarProps,
|
|
31
|
+
contentProps,
|
|
32
|
+
headerProps,
|
|
33
|
+
headerLeftProps,
|
|
34
|
+
groupIconProps,
|
|
35
|
+
nameProps,
|
|
36
|
+
headerRightProps,
|
|
37
|
+
statusSentIconProps,
|
|
38
|
+
formattedDateProps,
|
|
39
|
+
footerProps,
|
|
40
|
+
lastMessageProps,
|
|
41
|
+
draftLabelProps,
|
|
42
|
+
lastSenderNameProps,
|
|
43
|
+
unreadBadgeProps,
|
|
44
|
+
dividerProps,
|
|
45
|
+
...props
|
|
46
|
+
},
|
|
47
|
+
ref
|
|
48
|
+
) {
|
|
49
|
+
const avatarSource = photo || avatarProps?.src;
|
|
50
|
+
const avatarFallback = name || avatarProps?.name;
|
|
51
|
+
const avatarOnline = isPrivateDialog ? userOnline || avatarProps?.online : false;
|
|
52
|
+
const avatarPresence = isPrivateDialog ? userPresence || avatarProps?.presence : undefined;
|
|
53
|
+
const showTopDivider = divider === 'top' || (divider === 'both' && index === 0);
|
|
54
|
+
const showBottomDivider = divider === 'bottom' || divider === 'both';
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div
|
|
58
|
+
ref={ref}
|
|
59
|
+
{...props}
|
|
60
|
+
onClick={onSelect}
|
|
61
|
+
className={cn(
|
|
62
|
+
'flex items-start gap-2 px-2 flex-1 cursor-pointer',
|
|
63
|
+
'transition-colors duration-200 ease-linear',
|
|
64
|
+
`${selected ? 'border-l-[0.25em] pl-1 border-l-ring bg-ring/20' : 'hover:bg-ring/5'}`,
|
|
65
|
+
props?.className
|
|
66
|
+
)}
|
|
67
|
+
>
|
|
68
|
+
<Avatar
|
|
69
|
+
src={avatarSource}
|
|
70
|
+
name={avatarFallback}
|
|
71
|
+
online={avatarOnline}
|
|
72
|
+
presence={avatarPresence}
|
|
73
|
+
{...avatarProps}
|
|
74
|
+
className={cn(`size-13 my-2 ${photo ? 'bg-ring/10' : 'bg-ring/30'}`, avatarProps?.className)}
|
|
75
|
+
/>
|
|
76
|
+
|
|
77
|
+
<div
|
|
78
|
+
{...contentProps}
|
|
79
|
+
className={cn('relative self-stretch flex-1 flex-col text-foreground', contentProps?.className)}
|
|
80
|
+
>
|
|
81
|
+
<div {...headerProps} className={cn('flex items-center justify-between gap-1', headerProps?.className)}>
|
|
82
|
+
<div {...headerLeftProps} className={cn('flex items-center gap-1', headerLeftProps?.className)}>
|
|
83
|
+
{!isPrivateDialog && hasGroupIcon && (
|
|
84
|
+
<Users
|
|
85
|
+
{...groupIconProps}
|
|
86
|
+
className={cn('text-muted-foreground size-4 min-w-4', groupIconProps?.className)}
|
|
87
|
+
/>
|
|
88
|
+
)}
|
|
89
|
+
<span {...nameProps} className={cn('font-medium text-left line-clamp-1', nameProps?.className)}>
|
|
90
|
+
{name}
|
|
91
|
+
</span>
|
|
92
|
+
</div>
|
|
93
|
+
<div {...headerRightProps} className={cn('flex items-center gap-1', headerRightProps?.className)}>
|
|
94
|
+
<StatusSent status={lastSentStatus} {...statusSentIconProps} />
|
|
95
|
+
<FormattedDate date={lastSentDate} language={language} {...formattedDateProps} />
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
<div {...footerProps} className={cn('flex items-start justify-between gap-1', footerProps?.className)}>
|
|
99
|
+
<span
|
|
100
|
+
{...lastMessageProps}
|
|
101
|
+
className={cn('text-sm text-left text-muted-foreground line-clamp-2', lastMessageProps?.className)}
|
|
102
|
+
>
|
|
103
|
+
{typingStatusText ||
|
|
104
|
+
(draft ? (
|
|
105
|
+
<span>
|
|
106
|
+
<span
|
|
107
|
+
{...draftLabelProps}
|
|
108
|
+
className={cn('text-red-500 font-medium', draftLabelProps?.className)}
|
|
109
|
+
>{`${draftLabel}: `}</span>
|
|
110
|
+
{draft}
|
|
111
|
+
</span>
|
|
112
|
+
) : (
|
|
113
|
+
<span>
|
|
114
|
+
{lastSenderName && (
|
|
115
|
+
<span
|
|
116
|
+
{...lastSenderNameProps}
|
|
117
|
+
className={cn('font-semibold', lastSenderNameProps?.className)}
|
|
118
|
+
>{`${lastSenderName}: `}</span>
|
|
119
|
+
)}
|
|
120
|
+
{lastMessage}
|
|
121
|
+
</span>
|
|
122
|
+
))}
|
|
123
|
+
</span>
|
|
124
|
+
{unreadCount > 0 && (
|
|
125
|
+
<Badge
|
|
126
|
+
{...unreadBadgeProps}
|
|
127
|
+
className={cn(
|
|
128
|
+
'bg-ring text-background text-xs rounded-full p-1 h-6 min-w-6 mt-0.5',
|
|
129
|
+
unreadBadgeProps?.className
|
|
130
|
+
)}
|
|
131
|
+
>
|
|
132
|
+
{unreadCount}
|
|
133
|
+
</Badge>
|
|
134
|
+
)}
|
|
135
|
+
</div>
|
|
136
|
+
{showTopDivider && (
|
|
137
|
+
<div {...dividerProps} className={cn('absolute -top-px left-0 right-0 border-t', dividerProps?.className)} />
|
|
138
|
+
)}
|
|
139
|
+
{showBottomDivider && <div className="absolute bottom-0 left-0 right-0 border-b" />}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const DialogItem = memo(forwardRef(DialogItemBase));
|
|
146
|
+
|
|
147
|
+
DialogItem.displayName = 'DialogItem';
|
|
148
|
+
|
|
149
|
+
export { DialogItem };
|