@connectycube/react-ui-kit 0.0.15 → 0.0.17
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 +17 -2
- package/configs/imports.json +9 -2
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +9 -9
- package/dist/index.js.map +1 -1
- package/dist/types/components/alert-dialog.d.ts +28 -0
- package/dist/types/components/alert-dialog.d.ts.map +1 -0
- package/dist/types/components/avatar.d.ts +2 -1
- 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 +12 -0
- package/dist/types/components/button.d.ts.map +1 -0
- package/dist/types/components/dismiss-layer.d.ts +2 -1
- package/dist/types/components/dismiss-layer.d.ts.map +1 -1
- package/dist/types/components/input.d.ts +5 -0
- package/dist/types/components/input.d.ts.map +1 -0
- package/dist/types/components/placeholder-text.d.ts +2 -1
- package/dist/types/components/placeholder-text.d.ts.map +1 -1
- package/dist/types/components/presence.d.ts +9 -4
- package/dist/types/components/presence.d.ts.map +1 -1
- 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 +10 -0
- package/dist/types/components/spinner.d.ts.map +1 -0
- package/dist/types/components/status-indicator.d.ts +2 -1
- package/dist/types/components/status-indicator.d.ts.map +1 -1
- package/dist/types/components/stream-view.d.ts +5 -4
- package/dist/types/components/stream-view.d.ts.map +1 -1
- package/gen/components/alert-dialog.jsx +94 -0
- package/gen/components/avatar.jsx +2 -2
- package/gen/components/badge.jsx +45 -0
- package/gen/components/button.jsx +57 -0
- package/gen/components/dismiss-layer.jsx +2 -2
- package/gen/components/input.jsx +23 -0
- package/gen/components/placeholder-text.jsx +3 -2
- package/gen/components/presence.jsx +2 -7
- package/gen/components/search.jsx +75 -0
- package/gen/components/spinner.jsx +12 -0
- package/gen/components/status-indicator.jsx +2 -2
- package/package.json +5 -2
- package/src/components/alert-dialog.tsx +118 -0
- package/src/components/avatar.tsx +4 -3
- package/src/components/badge.tsx +42 -0
- package/src/components/button.tsx +58 -0
- package/src/components/dismiss-layer.tsx +4 -3
- package/src/components/input.tsx +26 -0
- package/src/components/placeholder-text.tsx +4 -3
- package/src/components/presence.tsx +3 -7
- package/src/components/search.tsx +86 -0
- package/src/components/spinner.tsx +16 -0
- package/src/components/status-indicator.tsx +4 -3
- package/src/components/stream-view.tsx +5 -4
- 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
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { forwardRef, useState } from 'react';
|
|
2
|
+
import { cn } from './utils';
|
|
3
|
+
import { Input } from './input';
|
|
4
|
+
import { Search as SearchIcon, X as CloseIcon } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
function SearchBase(
|
|
7
|
+
{
|
|
8
|
+
onSearch = () => {},
|
|
9
|
+
onCancel = () => {},
|
|
10
|
+
hasSearchIcon = true,
|
|
11
|
+
hasCancelIcon = true,
|
|
12
|
+
searchIconProps,
|
|
13
|
+
cancelIconProps,
|
|
14
|
+
containerProps,
|
|
15
|
+
...props
|
|
16
|
+
},
|
|
17
|
+
ref
|
|
18
|
+
) {
|
|
19
|
+
const [value, setValue] = useState('');
|
|
20
|
+
const handleOnSearch = (e) => {
|
|
21
|
+
const keyword = e.target.value;
|
|
22
|
+
|
|
23
|
+
setValue(keyword);
|
|
24
|
+
onSearch(keyword.toLowerCase());
|
|
25
|
+
};
|
|
26
|
+
const handleOnCancel = () => {
|
|
27
|
+
setValue('');
|
|
28
|
+
onCancel();
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div {...containerProps} className={cn('group relative', containerProps?.className)}>
|
|
33
|
+
{hasSearchIcon && (
|
|
34
|
+
<SearchIcon
|
|
35
|
+
{...searchIconProps}
|
|
36
|
+
className={cn(
|
|
37
|
+
'absolute top-1/2 left-2 transform -translate-y-1/2 size-5 text-muted-foreground group-focus-within:text-ring',
|
|
38
|
+
searchIconProps?.className
|
|
39
|
+
)}
|
|
40
|
+
/>
|
|
41
|
+
)}
|
|
42
|
+
<Input
|
|
43
|
+
ref={ref}
|
|
44
|
+
name="search"
|
|
45
|
+
placeholder={props?.placeholder || 'Search...'}
|
|
46
|
+
value={value}
|
|
47
|
+
onChange={handleOnSearch}
|
|
48
|
+
{...props}
|
|
49
|
+
className={cn(
|
|
50
|
+
'placeholder:text-muted-foreground focus-visible:ring-ring/50 focus-visible:ring flex',
|
|
51
|
+
hasSearchIcon ? 'pl-10' : 'pl-3',
|
|
52
|
+
hasCancelIcon ? 'pr-10' : 'pr-3',
|
|
53
|
+
props?.className
|
|
54
|
+
)}
|
|
55
|
+
/>
|
|
56
|
+
{hasCancelIcon && (
|
|
57
|
+
<CloseIcon
|
|
58
|
+
onClick={handleOnCancel}
|
|
59
|
+
{...cancelIconProps}
|
|
60
|
+
className={cn(
|
|
61
|
+
'absolute top-1/2 right-2 transform -translate-y-1/2 size-5 text-muted-foreground cursor-pointer hover:text-ring transition-all duration-300 ease-in-out',
|
|
62
|
+
value.length > 0 ? 'opacity-100 scale-100' : 'opacity-0 scale-75 pointer-events-none',
|
|
63
|
+
cancelIconProps?.className
|
|
64
|
+
)}
|
|
65
|
+
/>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const Search = forwardRef(SearchBase);
|
|
72
|
+
|
|
73
|
+
Search.displayName = 'Search';
|
|
74
|
+
|
|
75
|
+
export { Search };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LoaderCircle } from 'lucide-react';
|
|
2
|
+
import { cn } from './utils';
|
|
3
|
+
|
|
4
|
+
function Spinner({ loading = true, ...props }) {
|
|
5
|
+
return loading ? (
|
|
6
|
+
<LoaderCircle strokeWidth={2.5} {...props} className={cn('animate-spin mx-auto text-ring', props?.className)} />
|
|
7
|
+
) : null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
Spinner.displayName = 'Spinner';
|
|
11
|
+
|
|
12
|
+
export { Spinner };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { forwardRef
|
|
1
|
+
import { forwardRef } from 'react';
|
|
2
2
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
3
3
|
import { cn } from './utils';
|
|
4
4
|
|
|
@@ -62,7 +62,7 @@ function StatusIndicatorBase(
|
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
const StatusIndicator =
|
|
65
|
+
const StatusIndicator = forwardRef(StatusIndicatorBase);
|
|
66
66
|
|
|
67
67
|
StatusIndicator.displayName = 'StatusIndicator';
|
|
68
68
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@connectycube/react-ui-kit",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"description": "Simple React UI Kit generator with TSX/JSX",
|
|
5
5
|
"homepage": "https://github.com/ConnectyCube/react-ui-kit#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -62,12 +62,15 @@
|
|
|
62
62
|
"jsx"
|
|
63
63
|
],
|
|
64
64
|
"dependencies": {
|
|
65
|
+
"@radix-ui/react-alert-dialog": "^1.1.15",
|
|
65
66
|
"@radix-ui/react-avatar": "^1.1.11",
|
|
67
|
+
"@radix-ui/react-slot": "^1.2.4",
|
|
66
68
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
69
|
+
"class-variance-authority": "^0.7.1",
|
|
67
70
|
"clsx": "^2.1.1",
|
|
68
71
|
"execa": "^9.6.0",
|
|
69
72
|
"fs-extra": "^11.3.2",
|
|
70
|
-
"lucide-react": "^0.
|
|
73
|
+
"lucide-react": "^0.555.0",
|
|
71
74
|
"prompts": "^2.4.2",
|
|
72
75
|
"tailwind-merge": "^3.4.0"
|
|
73
76
|
},
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
|
|
4
|
+
import { Button, type ButtonProps } from './button';
|
|
5
|
+
import { cn } from './utils';
|
|
6
|
+
|
|
7
|
+
interface AlertDialogProps extends AlertDialogPrimitive.AlertDialogProps {
|
|
8
|
+
title?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
cancelTitle?: string;
|
|
11
|
+
actionTitle?: string;
|
|
12
|
+
onCancel?: () => void;
|
|
13
|
+
onConfirm?: () => void;
|
|
14
|
+
rootProps?: AlertDialogPrimitive.AlertDialogProps;
|
|
15
|
+
triggerElement: React.ReactElement;
|
|
16
|
+
triggerProps?: AlertDialogPrimitive.AlertDialogTriggerProps;
|
|
17
|
+
portalProps?: AlertDialogPrimitive.AlertDialogPortalProps;
|
|
18
|
+
overlayProps?: AlertDialogPrimitive.AlertDialogOverlayProps;
|
|
19
|
+
contentProps?: AlertDialogPrimitive.AlertDialogContentProps;
|
|
20
|
+
headerProps?: React.ComponentProps<'div'>;
|
|
21
|
+
titleProps?: AlertDialogPrimitive.AlertDialogTitleProps;
|
|
22
|
+
descriptionProps?: AlertDialogPrimitive.AlertDialogDescriptionProps;
|
|
23
|
+
footerProps?: React.ComponentProps<'div'>;
|
|
24
|
+
cancelProps?: AlertDialogPrimitive.AlertDialogCancelProps;
|
|
25
|
+
cancelButtonProps?: ButtonProps;
|
|
26
|
+
actionProps?: AlertDialogPrimitive.AlertDialogActionProps;
|
|
27
|
+
actionButtonProps?: ButtonProps;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function AlertDialogBase(
|
|
31
|
+
{
|
|
32
|
+
title = '',
|
|
33
|
+
description = '',
|
|
34
|
+
cancelTitle = 'Cancel',
|
|
35
|
+
actionTitle = 'Confirm',
|
|
36
|
+
onCancel = () => {},
|
|
37
|
+
onConfirm = () => {},
|
|
38
|
+
rootProps,
|
|
39
|
+
triggerElement,
|
|
40
|
+
triggerProps,
|
|
41
|
+
portalProps,
|
|
42
|
+
overlayProps,
|
|
43
|
+
contentProps,
|
|
44
|
+
headerProps,
|
|
45
|
+
titleProps,
|
|
46
|
+
descriptionProps,
|
|
47
|
+
footerProps,
|
|
48
|
+
cancelProps,
|
|
49
|
+
cancelButtonProps,
|
|
50
|
+
actionProps,
|
|
51
|
+
actionButtonProps,
|
|
52
|
+
...props
|
|
53
|
+
}: AlertDialogProps,
|
|
54
|
+
ref?: React.ForwardedRef<HTMLDivElement>
|
|
55
|
+
) {
|
|
56
|
+
return (
|
|
57
|
+
<div ref={ref} {...props}>
|
|
58
|
+
<AlertDialogPrimitive.Root {...rootProps}>
|
|
59
|
+
<AlertDialogPrimitive.Trigger asChild {...triggerProps}>
|
|
60
|
+
{triggerElement}
|
|
61
|
+
</AlertDialogPrimitive.Trigger>
|
|
62
|
+
<AlertDialogPrimitive.Portal {...portalProps}>
|
|
63
|
+
<AlertDialogPrimitive.Overlay
|
|
64
|
+
{...overlayProps}
|
|
65
|
+
className={cn(
|
|
66
|
+
'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
|
|
67
|
+
overlayProps?.className
|
|
68
|
+
)}
|
|
69
|
+
/>
|
|
70
|
+
<AlertDialogPrimitive.Content
|
|
71
|
+
{...contentProps}
|
|
72
|
+
className={cn(
|
|
73
|
+
'bg-background 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 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2em)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
|
|
74
|
+
contentProps?.className
|
|
75
|
+
)}
|
|
76
|
+
>
|
|
77
|
+
<div
|
|
78
|
+
{...headerProps}
|
|
79
|
+
className={cn('flex flex-col gap-2 text-center sm:text-left', headerProps?.className)}
|
|
80
|
+
>
|
|
81
|
+
<AlertDialogPrimitive.Title
|
|
82
|
+
{...titleProps}
|
|
83
|
+
className={cn('text-lg font-semibold', titleProps?.className)}
|
|
84
|
+
>
|
|
85
|
+
{title}
|
|
86
|
+
</AlertDialogPrimitive.Title>
|
|
87
|
+
<AlertDialogPrimitive.Description
|
|
88
|
+
{...descriptionProps}
|
|
89
|
+
className={cn('text-muted-foreground text-sm', descriptionProps?.className)}
|
|
90
|
+
>
|
|
91
|
+
{description}
|
|
92
|
+
</AlertDialogPrimitive.Description>
|
|
93
|
+
</div>
|
|
94
|
+
<div
|
|
95
|
+
{...footerProps}
|
|
96
|
+
className={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', footerProps?.className)}
|
|
97
|
+
>
|
|
98
|
+
<AlertDialogPrimitive.Cancel {...cancelProps} onClick={onCancel}>
|
|
99
|
+
<Button variant="outline" {...cancelButtonProps}>
|
|
100
|
+
{cancelTitle}
|
|
101
|
+
</Button>
|
|
102
|
+
</AlertDialogPrimitive.Cancel>
|
|
103
|
+
<AlertDialogPrimitive.Action {...actionProps} onClick={onConfirm}>
|
|
104
|
+
<Button {...actionButtonProps}>{actionTitle}</Button>
|
|
105
|
+
</AlertDialogPrimitive.Action>
|
|
106
|
+
</div>
|
|
107
|
+
</AlertDialogPrimitive.Content>
|
|
108
|
+
</AlertDialogPrimitive.Portal>
|
|
109
|
+
</AlertDialogPrimitive.Root>
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const AlertDialog = forwardRef<HTMLDivElement, AlertDialogProps>(AlertDialogBase);
|
|
115
|
+
|
|
116
|
+
AlertDialog.displayName = 'AlertDialog';
|
|
117
|
+
|
|
118
|
+
export { AlertDialog, type AlertDialogProps };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { memo, forwardRef } from 'react';
|
|
2
3
|
import * as AvatarPrimitive from '@radix-ui/react-avatar';
|
|
3
4
|
import { PresenceBadge, type PresenceStatus, type PresenceBadgeProps } from './presence';
|
|
4
5
|
import { cn, getInitialsFromName } from './utils';
|
|
@@ -27,7 +28,7 @@ function AvatarBase(
|
|
|
27
28
|
fallbackProps,
|
|
28
29
|
...props
|
|
29
30
|
}: AvatarProps,
|
|
30
|
-
ref: React.
|
|
31
|
+
ref: React.ForwardedRef<HTMLDivElement>
|
|
31
32
|
) {
|
|
32
33
|
const initials = getInitialsFromName(name);
|
|
33
34
|
|
|
@@ -40,7 +41,7 @@ function AvatarBase(
|
|
|
40
41
|
/>
|
|
41
42
|
<AvatarPrimitive.Fallback
|
|
42
43
|
{...fallbackProps}
|
|
43
|
-
className={cn('bg-muted
|
|
44
|
+
className={cn('bg-muted size-full rounded-full flex items-center justify-center', fallbackProps?.className)}
|
|
44
45
|
>
|
|
45
46
|
{initials}
|
|
46
47
|
</AvatarPrimitive.Fallback>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import * as SlotPrimitive 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<HTMLDivElement>, 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<HTMLSpanElement>
|
|
32
|
+
) {
|
|
33
|
+
const Comp = asChild ? SlotPrimitive.Root : 'span';
|
|
34
|
+
|
|
35
|
+
return <Comp ref={ref} className={cn(badgeVariants({ variant }), className)} {...props} />;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const Badge = forwardRef<HTMLSpanElement, BadgeProps>(BadgeBase);
|
|
39
|
+
|
|
40
|
+
Badge.displayName = 'Badge';
|
|
41
|
+
|
|
42
|
+
export { Badge, type BadgeProps };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import * as SlotPrimitive from '@radix-ui/react-slot';
|
|
4
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
5
|
+
import { cn } from './utils';
|
|
6
|
+
|
|
7
|
+
interface ButtonProps extends React.ComponentProps<'button'>, VariantProps<typeof buttonVariants> {
|
|
8
|
+
asChild?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const buttonVariants = cva(
|
|
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',
|
|
13
|
+
{
|
|
14
|
+
variants: {
|
|
15
|
+
variant: {
|
|
16
|
+
default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
|
|
17
|
+
destructive:
|
|
18
|
+
'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
|
|
19
|
+
outline:
|
|
20
|
+
'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
|
|
21
|
+
secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
|
|
22
|
+
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
|
|
23
|
+
link: 'text-primary underline-offset-4 hover:underline',
|
|
24
|
+
},
|
|
25
|
+
size: {
|
|
26
|
+
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
|
|
27
|
+
sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
|
|
28
|
+
lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
|
|
29
|
+
icon: 'size-9',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
defaultVariants: {
|
|
33
|
+
variant: 'default',
|
|
34
|
+
size: 'default',
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
function ButtonBase(
|
|
40
|
+
{ asChild = false, variant, size, className, ...props }: ButtonProps,
|
|
41
|
+
ref?: React.ForwardedRef<HTMLButtonElement>
|
|
42
|
+
) {
|
|
43
|
+
const Comp = asChild ? SlotPrimitive.Root : 'button';
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Comp
|
|
47
|
+
ref={ref}
|
|
48
|
+
{...props}
|
|
49
|
+
className={cn(buttonVariants({ variant, size, className }), 'transition-all ease-in-out duration-300')}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const Button = forwardRef<HTMLButtonElement, ButtonProps>(ButtonBase);
|
|
55
|
+
|
|
56
|
+
Button.displayName = 'Button';
|
|
57
|
+
|
|
58
|
+
export { Button, type ButtonProps };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { useCallback, useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
|
|
2
3
|
import { cn } from './utils';
|
|
3
4
|
|
|
4
5
|
interface DismissLayerProps extends React.ComponentProps<'div'> {
|
|
@@ -11,7 +12,7 @@ interface DismissLayerProps extends React.ComponentProps<'div'> {
|
|
|
11
12
|
|
|
12
13
|
function DismissLayerBase(
|
|
13
14
|
{ active, onDismiss, disableClickOutside = false, disableEscKeyPress = false, disabled, ...props }: DismissLayerProps,
|
|
14
|
-
ref: React.
|
|
15
|
+
ref: React.ForwardedRef<HTMLDivElement>
|
|
15
16
|
) {
|
|
16
17
|
const innerRef = useRef<HTMLDivElement>(null);
|
|
17
18
|
|
|
@@ -58,7 +59,7 @@ function DismissLayerBase(
|
|
|
58
59
|
);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
const DismissLayer =
|
|
62
|
+
const DismissLayer = forwardRef<HTMLDivElement, DismissLayerProps>(DismissLayerBase);
|
|
62
63
|
|
|
63
64
|
DismissLayer.displayName = 'DismissLayer';
|
|
64
65
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
3
|
+
import { cn } from './utils';
|
|
4
|
+
|
|
5
|
+
type InputProps = React.ComponentProps<'input'>;
|
|
6
|
+
|
|
7
|
+
function InputBase(props: InputProps, ref?: React.ForwardedRef<HTMLInputElement>) {
|
|
8
|
+
return (
|
|
9
|
+
<input
|
|
10
|
+
ref={ref}
|
|
11
|
+
{...props}
|
|
12
|
+
className={cn(
|
|
13
|
+
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-10 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
|
14
|
+
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring',
|
|
15
|
+
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
|
16
|
+
props?.className
|
|
17
|
+
)}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const Input = forwardRef(InputBase);
|
|
23
|
+
|
|
24
|
+
Input.displayName = 'Input';
|
|
25
|
+
|
|
26
|
+
export { Input, type InputProps };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
2
3
|
import { cn } from './utils';
|
|
3
4
|
|
|
4
5
|
interface PlaceholderTextProps extends React.ComponentProps<'div'> {
|
|
@@ -9,7 +10,7 @@ interface PlaceholderTextProps extends React.ComponentProps<'div'> {
|
|
|
9
10
|
|
|
10
11
|
function PlaceholderTextBase(
|
|
11
12
|
{ title, titles = [], rowProps, ...props }: PlaceholderTextProps,
|
|
12
|
-
ref: React.
|
|
13
|
+
ref: React.ForwardedRef<HTMLDivElement>
|
|
13
14
|
) {
|
|
14
15
|
const rows = typeof title === 'string' ? [title, ...titles] : titles;
|
|
15
16
|
|
|
@@ -28,7 +29,7 @@ function PlaceholderTextBase(
|
|
|
28
29
|
);
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
const PlaceholderText =
|
|
32
|
+
const PlaceholderText = forwardRef(PlaceholderTextBase);
|
|
32
33
|
|
|
33
34
|
PlaceholderText.displayName = 'PlaceholderText';
|
|
34
35
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type React from 'react';
|
|
2
2
|
import { capitalize, cn } from './utils';
|
|
3
3
|
|
|
4
4
|
type PresenceStatus = 'available' | 'busy' | 'away' | 'unknown' | undefined;
|
|
@@ -15,7 +15,7 @@ interface PresenceBadgeProps extends React.ComponentProps<'svg'> {
|
|
|
15
15
|
status?: PresenceStatus;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
function
|
|
18
|
+
function PresenceBadge({ status, ...props }: PresenceBadgeProps) {
|
|
19
19
|
switch (status) {
|
|
20
20
|
case 'available':
|
|
21
21
|
return (
|
|
@@ -78,11 +78,9 @@ function PresenceBadgeBase({ status, ...props }: PresenceBadgeProps) {
|
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const PresenceBadge = memo(PresenceBadgeBase);
|
|
82
|
-
|
|
83
81
|
PresenceBadge.displayName = 'PresenceBadge';
|
|
84
82
|
|
|
85
|
-
function
|
|
83
|
+
function Presence({ badge = true, status, label, badgeProps, labelProps, ...props }: PresenceProps) {
|
|
86
84
|
const presence = capitalize(label || status);
|
|
87
85
|
|
|
88
86
|
return (
|
|
@@ -93,8 +91,6 @@ function PresenceBase({ badge = true, status, label, badgeProps, labelProps, ...
|
|
|
93
91
|
);
|
|
94
92
|
}
|
|
95
93
|
|
|
96
|
-
const Presence = memo(PresenceBase);
|
|
97
|
-
|
|
98
94
|
Presence.displayName = 'Presence';
|
|
99
95
|
|
|
100
96
|
export { Presence, PresenceBadge, type PresenceStatus, type PresenceProps, type PresenceBadgeProps };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef, useState } from 'react';
|
|
3
|
+
import { cn } from './utils';
|
|
4
|
+
import { Input, type InputProps } from './input';
|
|
5
|
+
import { Search as SearchIcon, X as CloseIcon, type LucideProps } from 'lucide-react';
|
|
6
|
+
|
|
7
|
+
interface SearchProps extends InputProps {
|
|
8
|
+
onSearch?: (value: string) => void;
|
|
9
|
+
onCancel?: () => void;
|
|
10
|
+
hasSearchIcon?: boolean;
|
|
11
|
+
hasCancelIcon?: boolean;
|
|
12
|
+
searchIconProps?: LucideProps;
|
|
13
|
+
cancelIconProps?: LucideProps;
|
|
14
|
+
containerProps?: React.ComponentProps<'div'>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function SearchBase(
|
|
18
|
+
{
|
|
19
|
+
onSearch = () => {},
|
|
20
|
+
onCancel = () => {},
|
|
21
|
+
hasSearchIcon = true,
|
|
22
|
+
hasCancelIcon = true,
|
|
23
|
+
searchIconProps,
|
|
24
|
+
cancelIconProps,
|
|
25
|
+
containerProps,
|
|
26
|
+
...props
|
|
27
|
+
}: SearchProps,
|
|
28
|
+
ref?: React.ForwardedRef<HTMLInputElement>
|
|
29
|
+
) {
|
|
30
|
+
const [value, setValue] = useState<string>('');
|
|
31
|
+
const handleOnSearch = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
|
32
|
+
const keyword = e.target.value;
|
|
33
|
+
|
|
34
|
+
setValue(keyword);
|
|
35
|
+
onSearch(keyword.toLowerCase());
|
|
36
|
+
};
|
|
37
|
+
const handleOnCancel = (): void => {
|
|
38
|
+
setValue('');
|
|
39
|
+
onCancel();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div {...containerProps} className={cn('group relative', containerProps?.className)}>
|
|
44
|
+
{hasSearchIcon && (
|
|
45
|
+
<SearchIcon
|
|
46
|
+
{...searchIconProps}
|
|
47
|
+
className={cn(
|
|
48
|
+
'absolute top-1/2 left-2 transform -translate-y-1/2 size-5 text-muted-foreground group-focus-within:text-ring',
|
|
49
|
+
searchIconProps?.className
|
|
50
|
+
)}
|
|
51
|
+
/>
|
|
52
|
+
)}
|
|
53
|
+
<Input
|
|
54
|
+
ref={ref}
|
|
55
|
+
name="search"
|
|
56
|
+
placeholder={props?.placeholder || 'Search...'}
|
|
57
|
+
value={value}
|
|
58
|
+
onChange={handleOnSearch}
|
|
59
|
+
{...props}
|
|
60
|
+
className={cn(
|
|
61
|
+
'placeholder:text-muted-foreground focus-visible:ring-ring/50 focus-visible:ring flex',
|
|
62
|
+
hasSearchIcon ? 'pl-10' : 'pl-3',
|
|
63
|
+
hasCancelIcon ? 'pr-10' : 'pr-3',
|
|
64
|
+
props?.className
|
|
65
|
+
)}
|
|
66
|
+
/>
|
|
67
|
+
{hasCancelIcon && (
|
|
68
|
+
<CloseIcon
|
|
69
|
+
onClick={handleOnCancel}
|
|
70
|
+
{...cancelIconProps}
|
|
71
|
+
className={cn(
|
|
72
|
+
'absolute top-1/2 right-2 transform -translate-y-1/2 size-5 text-muted-foreground cursor-pointer hover:text-ring transition-all duration-300 ease-in-out',
|
|
73
|
+
value.length > 0 ? 'opacity-100 scale-100' : 'opacity-0 scale-75 pointer-events-none',
|
|
74
|
+
cancelIconProps?.className
|
|
75
|
+
)}
|
|
76
|
+
/>
|
|
77
|
+
)}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const Search = forwardRef<HTMLInputElement, SearchProps>(SearchBase);
|
|
83
|
+
|
|
84
|
+
Search.displayName = 'Search';
|
|
85
|
+
|
|
86
|
+
export { Search, type SearchProps };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { LoaderCircle, type LucideProps } from 'lucide-react';
|
|
2
|
+
import { cn } from './utils';
|
|
3
|
+
|
|
4
|
+
interface SpinnerProps extends LucideProps {
|
|
5
|
+
loading?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function Spinner({ loading = true, ...props }: SpinnerProps) {
|
|
9
|
+
return loading ? (
|
|
10
|
+
<LoaderCircle strokeWidth={2.5} {...props} className={cn('animate-spin mx-auto text-ring', props?.className)} />
|
|
11
|
+
) : null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
Spinner.displayName = 'Spinner';
|
|
15
|
+
|
|
16
|
+
export { Spinner, type SpinnerProps };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import { forwardRef } from 'react';
|
|
2
3
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
3
4
|
import { cn } from './utils';
|
|
4
5
|
|
|
@@ -35,7 +36,7 @@ function StatusIndicatorBase(
|
|
|
35
36
|
className,
|
|
36
37
|
...props
|
|
37
38
|
}: StatusIndicatorProps,
|
|
38
|
-
ref: React.
|
|
39
|
+
ref: React.ForwardedRef<HTMLDivElement>
|
|
39
40
|
) {
|
|
40
41
|
if (disabled) return null;
|
|
41
42
|
|
|
@@ -77,7 +78,7 @@ function StatusIndicatorBase(
|
|
|
77
78
|
);
|
|
78
79
|
}
|
|
79
80
|
|
|
80
|
-
const StatusIndicator =
|
|
81
|
+
const StatusIndicator = forwardRef(StatusIndicatorBase);
|
|
81
82
|
|
|
82
83
|
StatusIndicator.displayName = 'StatusIndicator';
|
|
83
84
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type React from 'react';
|
|
1
2
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
2
3
|
import { Maximize, Minimize, PictureInPicture2, type LucideProps } from 'lucide-react';
|
|
3
4
|
import { cn, getRandomString } from './utils';
|
|
@@ -9,7 +10,7 @@ interface StreamViewProps extends React.ComponentProps<'video'> {
|
|
|
9
10
|
|
|
10
11
|
function StreamViewBase(
|
|
11
12
|
{ id, stream, mirror, className, muted, ...props }: StreamViewProps,
|
|
12
|
-
ref: React.
|
|
13
|
+
ref: React.ForwardedRef<HTMLVideoElement>
|
|
13
14
|
) {
|
|
14
15
|
const innerRef = useRef<HTMLVideoElement>(null);
|
|
15
16
|
const elementId = useMemo(() => id ?? `stream-${getRandomString()}`, [id]);
|
|
@@ -56,7 +57,7 @@ const StreamView = forwardRef<HTMLVideoElement, StreamViewProps>(StreamViewBase)
|
|
|
56
57
|
|
|
57
58
|
StreamView.displayName = 'StreamView';
|
|
58
59
|
|
|
59
|
-
function LocalStreamViewBase({ muted, mirror, ...props }: StreamViewProps, ref: React.
|
|
60
|
+
function LocalStreamViewBase({ muted, mirror, ...props }: StreamViewProps, ref: React.ForwardedRef<HTMLVideoElement>) {
|
|
60
61
|
const isMuted = typeof muted === 'boolean' ? muted : true;
|
|
61
62
|
const isMirror = typeof mirror === 'boolean' ? mirror : true;
|
|
62
63
|
|
|
@@ -67,7 +68,7 @@ const LocalStreamView = forwardRef<HTMLVideoElement, StreamViewProps>(LocalStrea
|
|
|
67
68
|
|
|
68
69
|
LocalStreamView.displayName = 'LocalStreamView';
|
|
69
70
|
|
|
70
|
-
function RemoteStreamViewBase({ muted, mirror, ...props }: StreamViewProps, ref: React.
|
|
71
|
+
function RemoteStreamViewBase({ muted, mirror, ...props }: StreamViewProps, ref: React.ForwardedRef<HTMLVideoElement>) {
|
|
71
72
|
const isMuted = typeof muted === 'boolean' ? muted : false;
|
|
72
73
|
const isMirror = typeof mirror === 'boolean' ? mirror : false;
|
|
73
74
|
|
|
@@ -113,7 +114,7 @@ function FullscreenStreamViewBase(
|
|
|
113
114
|
pipButtonIconProps,
|
|
114
115
|
...props
|
|
115
116
|
}: FullscreenStreamViewProps,
|
|
116
|
-
ref: React.
|
|
117
|
+
ref: React.ForwardedRef<FullscreenStreamViewRef>
|
|
117
118
|
) {
|
|
118
119
|
const innerRef = useRef<HTMLDivElement>(null);
|
|
119
120
|
const [isFullscreen, setIsFullscreen] = useState<boolean>(false);
|