@connectycube/react-ui-kit 0.0.12 → 0.0.14
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 +3 -0
- package/gen/components/animated-loader.jsx +2 -2
- package/gen/components/avatar.jsx +23 -13
- package/gen/components/dismiss-layer.jsx +4 -4
- package/gen/components/placeholder-text.jsx +7 -3
- package/gen/components/presence.jsx +19 -14
- package/gen/components/status-indicator.jsx +76 -0
- package/gen/components/stream-view.jsx +14 -20
- package/gen/components/utils.js +0 -9
- package/package.json +2 -1
- package/src/components/animated-loader.tsx +2 -2
- package/src/components/avatar.tsx +41 -16
- package/src/components/connectycube-ui/status-indicator.tsx +100 -0
- package/src/components/dismiss-layer.tsx +4 -12
- package/src/components/placeholder-text.tsx +11 -3
- package/src/components/presence.tsx +26 -19
- package/src/components/status-indicator.tsx +100 -0
- package/src/components/stream-view.tsx +15 -27
- package/src/components/utils.ts +0 -7
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { LoaderCircle } from 'lucide-react';
|
|
2
2
|
import { cn } from './utils';
|
|
3
3
|
|
|
4
|
-
function AnimatedLoader({ loading = true,
|
|
4
|
+
function AnimatedLoader({ loading = true, ...props }) {
|
|
5
5
|
return loading ? (
|
|
6
|
-
<LoaderCircle strokeWidth={3} className={cn('animate-spin mx-auto text-gray-600', className)} />
|
|
6
|
+
<LoaderCircle strokeWidth={3} {...props} className={cn('animate-spin mx-auto text-gray-600', props?.className)} />
|
|
7
7
|
) : null;
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -1,29 +1,39 @@
|
|
|
1
1
|
import { forwardRef, memo } from 'react';
|
|
2
|
-
import
|
|
2
|
+
import { Avatar as AvatarRoot, Fallback as AvatarFallback, Image as AvatarImage } from '@radix-ui/react-avatar';
|
|
3
3
|
import { PresenceBadge } from './presence';
|
|
4
4
|
import { cn, getInitialsFromName } from './utils';
|
|
5
5
|
|
|
6
6
|
function AvatarBase(
|
|
7
|
-
{ src, name = 'NA', online, presence, className,
|
|
7
|
+
{ src, name = 'NA', online, presence, className, onlineProps, presenceProps, imageProps, fallbackProps, ...props },
|
|
8
8
|
ref
|
|
9
9
|
) {
|
|
10
10
|
const initials = getInitialsFromName(name);
|
|
11
11
|
|
|
12
12
|
return (
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<
|
|
13
|
+
<AvatarRoot ref={ref} {...props} className={cn('relative flex size-8 shrink-0 rounded-full', className)}>
|
|
14
|
+
<AvatarImage
|
|
15
|
+
{...imageProps}
|
|
16
|
+
src={src}
|
|
17
|
+
className={cn('aspect-square size-full rounded-full overflow-hidden object-cover', imageProps?.className)}
|
|
18
|
+
/>
|
|
19
|
+
<AvatarFallback
|
|
20
|
+
{...fallbackProps}
|
|
21
|
+
className={cn('bg-muted flex size-full items-center justify-center', fallbackProps?.className)}
|
|
22
|
+
>
|
|
20
23
|
{initials}
|
|
21
|
-
</
|
|
24
|
+
</AvatarFallback>
|
|
22
25
|
{online && (
|
|
23
|
-
<div
|
|
26
|
+
<div
|
|
27
|
+
{...onlineProps}
|
|
28
|
+
className={cn('rounded-full border-2 bg-green-600 border-green-200 size-3.5', onlineProps?.className)}
|
|
29
|
+
/>
|
|
24
30
|
)}
|
|
25
|
-
<PresenceBadge
|
|
26
|
-
|
|
31
|
+
<PresenceBadge
|
|
32
|
+
status={presence}
|
|
33
|
+
{...presenceProps}
|
|
34
|
+
className={cn('absolute -bottom-0.5 -right-1', presenceProps?.className)}
|
|
35
|
+
/>
|
|
36
|
+
</AvatarRoot>
|
|
27
37
|
);
|
|
28
38
|
}
|
|
29
39
|
|
|
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useRef, useImperativeHandle, memo, forwardRef }
|
|
|
2
2
|
import { cn } from './utils';
|
|
3
3
|
|
|
4
4
|
function DismissLayerBase(
|
|
5
|
-
{ active, onDismiss, disableClickOutside = false, disableEscKeyPress = false, disabled,
|
|
5
|
+
{ active, onDismiss, disableClickOutside = false, disableEscKeyPress = false, disabled, ...props },
|
|
6
6
|
ref
|
|
7
7
|
) {
|
|
8
8
|
const innerRef = useRef(null);
|
|
@@ -41,11 +41,11 @@ function DismissLayerBase(
|
|
|
41
41
|
return (
|
|
42
42
|
<div
|
|
43
43
|
ref={innerRef}
|
|
44
|
-
onClick={handleClickOrTouch}
|
|
45
|
-
onTouchStart={handleClickOrTouch}
|
|
46
|
-
className={cn('fixed top-0 left-0 z-40 size-full bg-black/20', className)}
|
|
47
44
|
aria-hidden
|
|
48
45
|
{...props}
|
|
46
|
+
onClick={handleClickOrTouch}
|
|
47
|
+
onTouchStart={handleClickOrTouch}
|
|
48
|
+
className={cn('fixed top-0 left-0 z-40 size-full bg-black/20', props?.className)}
|
|
49
49
|
/>
|
|
50
50
|
);
|
|
51
51
|
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { forwardRef, memo } from 'react';
|
|
2
2
|
import { cn } from './utils';
|
|
3
3
|
|
|
4
|
-
function PlaceholderTextBase({ title, titles = [],
|
|
4
|
+
function PlaceholderTextBase({ title, titles = [], rowProps, ...props }, ref) {
|
|
5
5
|
const rows = typeof title === 'string' ? [title, ...titles] : titles;
|
|
6
6
|
|
|
7
7
|
return (
|
|
8
|
-
<div
|
|
8
|
+
<div
|
|
9
|
+
ref={ref}
|
|
10
|
+
{...props}
|
|
11
|
+
className={cn('absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2', props?.className)}
|
|
12
|
+
>
|
|
9
13
|
{rows.map((row, index) => (
|
|
10
|
-
<div key={`placeholder-text-${index}`} className=
|
|
14
|
+
<div key={`placeholder-text-${index}`} {...rowProps} className={cn('text-center', rowProps?.className)}>
|
|
11
15
|
{row}
|
|
12
16
|
</div>
|
|
13
17
|
))}
|
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
import { memo } from 'react';
|
|
2
2
|
import { CircleCheck, CircleMinus, CircleQuestionMark, Clock } from 'lucide-react';
|
|
3
|
-
import { capitalize, cn
|
|
3
|
+
import { capitalize, cn } from './utils';
|
|
4
4
|
|
|
5
|
-
function PresenceBadgeBase({ status,
|
|
5
|
+
function PresenceBadgeBase({ status, ...props }) {
|
|
6
6
|
switch (status) {
|
|
7
|
-
case
|
|
8
|
-
return <CircleCheck className={cn('rounded-full text-white bg-green-600 size-4.5', className)} {...props} />;
|
|
9
|
-
case UserPresence.BUSY || 'busy':
|
|
10
|
-
return <CircleMinus className={cn('rounded-full text-white bg-red-600 size-4.5', className)} {...props} />;
|
|
11
|
-
case UserPresence.AWAY || 'away':
|
|
12
|
-
return <Clock className={cn('rounded-full text-white bg-yellow-500 size-4.5', className)} {...props} />;
|
|
13
|
-
case UserPresence.UNKNOWN || 'unknown':
|
|
7
|
+
case 'available':
|
|
14
8
|
return (
|
|
15
|
-
<
|
|
9
|
+
<CircleCheck {...props} className={cn('rounded-full text-white bg-green-600 size-4.5', props?.className)} />
|
|
10
|
+
);
|
|
11
|
+
case 'busy':
|
|
12
|
+
return <CircleMinus {...props} className={cn('rounded-full text-white bg-red-600 size-4.5', props?.className)} />;
|
|
13
|
+
case 'away':
|
|
14
|
+
return <Clock {...props} className={cn('rounded-full text-white bg-yellow-500 size-4.5', props?.className)} />;
|
|
15
|
+
case 'unknown':
|
|
16
|
+
return (
|
|
17
|
+
<CircleQuestionMark
|
|
18
|
+
{...props}
|
|
19
|
+
className={cn('rounded-full text-white bg-gray-500 size-4.5', props?.className)}
|
|
20
|
+
/>
|
|
16
21
|
);
|
|
17
22
|
default:
|
|
18
23
|
return null;
|
|
@@ -23,13 +28,13 @@ const PresenceBadge = memo(PresenceBadgeBase);
|
|
|
23
28
|
|
|
24
29
|
PresenceBadge.displayName = 'PresenceBadge';
|
|
25
30
|
|
|
26
|
-
function PresenceBase({ badge = true, status, label,
|
|
31
|
+
function PresenceBase({ badge = true, status, label, badgeProps, labelProps, ...props }) {
|
|
27
32
|
const presence = capitalize(label || status);
|
|
28
33
|
|
|
29
34
|
return (
|
|
30
|
-
<div className={cn('flex items-center gap-2', className)}
|
|
31
|
-
{badge && <PresenceBadge status={status} />}
|
|
32
|
-
<span>{presence}</span>
|
|
35
|
+
<div {...props} className={cn('flex items-center gap-2', props?.className)}>
|
|
36
|
+
{badge && <PresenceBadge status={status} {...badgeProps} />}
|
|
37
|
+
<span {...labelProps}>{presence}</span>
|
|
33
38
|
</div>
|
|
34
39
|
);
|
|
35
40
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { forwardRef, memo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Provider as TooltipProvider,
|
|
4
|
+
Root as TooltipRoot,
|
|
5
|
+
Trigger as TooltipTrigger,
|
|
6
|
+
Portal as TooltipPortal,
|
|
7
|
+
Content as TooltipContent,
|
|
8
|
+
Arrow as TooltipArrow,
|
|
9
|
+
} from '@radix-ui/react-tooltip';
|
|
10
|
+
import { cn } from './utils';
|
|
11
|
+
|
|
12
|
+
function StatusIndicatorBase(
|
|
13
|
+
{
|
|
14
|
+
status = 'unknown',
|
|
15
|
+
statusColorConfig = {
|
|
16
|
+
unknown: 'bg-gray-500 border-gray-600',
|
|
17
|
+
},
|
|
18
|
+
statusProps,
|
|
19
|
+
tooltipProviderProps,
|
|
20
|
+
tooltipProps,
|
|
21
|
+
tooltipTriggerProps,
|
|
22
|
+
tooltipPortalProps,
|
|
23
|
+
tooltipContentProps,
|
|
24
|
+
tooltipArrowProps,
|
|
25
|
+
tooltip,
|
|
26
|
+
disabled,
|
|
27
|
+
className,
|
|
28
|
+
...props
|
|
29
|
+
},
|
|
30
|
+
ref
|
|
31
|
+
) {
|
|
32
|
+
if (disabled) return null;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div ref={ref} {...props} className={cn('absolute top-0 left-0', className)}>
|
|
36
|
+
<TooltipProvider {...tooltipProviderProps}>
|
|
37
|
+
<TooltipRoot {...tooltipProps}>
|
|
38
|
+
<TooltipTrigger asChild {...tooltipTriggerProps}>
|
|
39
|
+
<div
|
|
40
|
+
{...statusProps}
|
|
41
|
+
className={cn(
|
|
42
|
+
'rounded-full size-4 border border-primary-foreground',
|
|
43
|
+
statusColorConfig[status],
|
|
44
|
+
statusProps?.className
|
|
45
|
+
)}
|
|
46
|
+
/>
|
|
47
|
+
</TooltipTrigger>
|
|
48
|
+
<TooltipPortal {...tooltipPortalProps}>
|
|
49
|
+
<TooltipContent
|
|
50
|
+
{...tooltipContentProps}
|
|
51
|
+
className={cn(
|
|
52
|
+
'bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
|
|
53
|
+
tooltipContentProps?.className
|
|
54
|
+
)}
|
|
55
|
+
>
|
|
56
|
+
<span>{tooltip || status || 'unknown'}</span>
|
|
57
|
+
<TooltipArrow
|
|
58
|
+
{...tooltipArrowProps}
|
|
59
|
+
className={cn(
|
|
60
|
+
'bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px]',
|
|
61
|
+
tooltipArrowProps?.className
|
|
62
|
+
)}
|
|
63
|
+
/>
|
|
64
|
+
</TooltipContent>
|
|
65
|
+
</TooltipPortal>
|
|
66
|
+
</TooltipRoot>
|
|
67
|
+
</TooltipProvider>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const StatusIndicator = memo(forwardRef(StatusIndicatorBase));
|
|
73
|
+
|
|
74
|
+
StatusIndicator.displayName = 'StatusIndicator';
|
|
75
|
+
|
|
76
|
+
export { StatusIndicator };
|
|
@@ -38,8 +38,8 @@ function StreamViewBase({ id, stream, mirror, className, muted, ...props }, ref)
|
|
|
38
38
|
autoPlay
|
|
39
39
|
playsInline
|
|
40
40
|
muted={isMuted}
|
|
41
|
-
className={cn(defaultClassName, mirrorClassName, className)}
|
|
42
41
|
{...props}
|
|
42
|
+
className={cn(defaultClassName, mirrorClassName, className)}
|
|
43
43
|
/>
|
|
44
44
|
);
|
|
45
45
|
}
|
|
@@ -77,18 +77,12 @@ function FullscreenStreamViewBase(
|
|
|
77
77
|
navElement,
|
|
78
78
|
hideIconElement,
|
|
79
79
|
showIconElement,
|
|
80
|
-
containerClassName,
|
|
81
|
-
fullscreenButtonClassName,
|
|
82
|
-
fullscreenButtonIconClassName,
|
|
83
|
-
pipContainerClassName,
|
|
84
|
-
pipButtonClassName,
|
|
85
|
-
pipButtonIconClassName,
|
|
86
|
-
containerProps,
|
|
87
80
|
fullscreenButtonProps,
|
|
88
81
|
fullscreenButtonIconProps,
|
|
89
|
-
|
|
82
|
+
pipProps,
|
|
90
83
|
pipButtonProps,
|
|
91
84
|
pipButtonIconProps,
|
|
85
|
+
...props
|
|
92
86
|
},
|
|
93
87
|
ref
|
|
94
88
|
) {
|
|
@@ -146,45 +140,45 @@ function FullscreenStreamViewBase(
|
|
|
146
140
|
return (
|
|
147
141
|
<div
|
|
148
142
|
ref={innerRef}
|
|
149
|
-
|
|
150
|
-
{
|
|
143
|
+
{...props}
|
|
144
|
+
className={cn('relative flex items-center justify-center size-full', props?.className)}
|
|
151
145
|
>
|
|
152
146
|
{element}
|
|
153
147
|
<button
|
|
154
148
|
onClick={toggleFullscreen}
|
|
149
|
+
{...fullscreenButtonProps}
|
|
155
150
|
className={cn(
|
|
156
151
|
'absolute top-2 right-2 p-1 rounded-md bg-black/50 text-white hover:bg-black/80 z-10 shadow-xs shadow-white/25',
|
|
157
|
-
|
|
152
|
+
fullscreenButtonProps?.className
|
|
158
153
|
)}
|
|
159
|
-
{...fullscreenButtonProps}
|
|
160
154
|
>
|
|
161
155
|
{isFullscreen
|
|
162
|
-
? hideIconElement || <Minimize
|
|
163
|
-
: showIconElement || <Maximize
|
|
156
|
+
? hideIconElement || <Minimize {...fullscreenButtonIconProps} />
|
|
157
|
+
: showIconElement || <Maximize {...fullscreenButtonIconProps} />}
|
|
164
158
|
</button>
|
|
165
159
|
<div className="absolute size-full p-2 flex flex-col justify-end items-center">
|
|
166
160
|
{isFullscreen && pipElement && (
|
|
167
161
|
<div className="relative size-full flex items-end justify-end">
|
|
168
162
|
{isPictureInPicture && (
|
|
169
163
|
<div
|
|
164
|
+
{...pipProps}
|
|
170
165
|
className={cn(
|
|
171
166
|
'max-w-1/4 max-h-1/4 aspect-4/3 overflow-hidden rounded-md shadow-md shadow-white/25',
|
|
172
|
-
|
|
167
|
+
pipProps?.className
|
|
173
168
|
)}
|
|
174
|
-
{...pipContainerProps}
|
|
175
169
|
>
|
|
176
170
|
{pipElement}
|
|
177
171
|
</div>
|
|
178
172
|
)}
|
|
179
173
|
<button
|
|
180
174
|
onClick={togglePictureInPicture}
|
|
175
|
+
{...pipButtonProps}
|
|
181
176
|
className={cn(
|
|
182
177
|
'absolute bottom-2 right-2 p-1 rounded-md bg-black/50 text-white hover:bg-black/80 z-10 shadow-xs shadow-white/25',
|
|
183
|
-
|
|
178
|
+
pipButtonProps?.className
|
|
184
179
|
)}
|
|
185
|
-
{...pipButtonProps}
|
|
186
180
|
>
|
|
187
|
-
<PictureInPicture2
|
|
181
|
+
<PictureInPicture2 {...pipButtonIconProps} />
|
|
188
182
|
</button>
|
|
189
183
|
</div>
|
|
190
184
|
)}
|
package/gen/components/utils.js
CHANGED
|
@@ -19,12 +19,3 @@ export function getInitialsFromName(name) {
|
|
|
19
19
|
export function capitalize(str) {
|
|
20
20
|
return typeof str === 'string' && str.length > 0 ? `${str[0]?.toUpperCase()}${str.slice(1)}` : '';
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
export let UserPresence = /*#__PURE__*/ (function (UserPresence) {
|
|
24
|
-
UserPresence['AVAILABLE'] = 'available';
|
|
25
|
-
UserPresence['BUSY'] = 'busy';
|
|
26
|
-
UserPresence['AWAY'] = 'away';
|
|
27
|
-
UserPresence['UNKNOWN'] = 'unknown';
|
|
28
|
-
|
|
29
|
-
return UserPresence;
|
|
30
|
-
})({});
|
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.14",
|
|
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": {
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
],
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@radix-ui/react-avatar": "^1.1.11",
|
|
64
|
+
"@radix-ui/react-tooltip": "^1.2.8",
|
|
64
65
|
"clsx": "^2.1.1",
|
|
65
66
|
"execa": "^9.6.0",
|
|
66
67
|
"fs-extra": "^11.3.2",
|
|
@@ -6,9 +6,9 @@ interface AnimatedLoaderProps extends LucideProps {
|
|
|
6
6
|
loading?: boolean;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
function AnimatedLoader({ loading = true,
|
|
9
|
+
function AnimatedLoader({ loading = true, ...props }: AnimatedLoaderProps) {
|
|
10
10
|
return loading ? (
|
|
11
|
-
<LoaderCircle strokeWidth={3} className={cn('animate-spin mx-auto text-gray-600', className)} />
|
|
11
|
+
<LoaderCircle strokeWidth={3} {...props} className={cn('animate-spin mx-auto text-gray-600', props?.className)} />
|
|
12
12
|
) : null;
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -1,40 +1,65 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
+
import type { AvatarProps as AvatarRootProps, AvatarFallbackProps, AvatarImageProps } from '@radix-ui/react-avatar';
|
|
2
3
|
import type { PresenceStatus } from './presence';
|
|
4
|
+
import type { PresenceBadgeProps } from './presence';
|
|
3
5
|
import { forwardRef, memo } from 'react';
|
|
4
|
-
import
|
|
6
|
+
import { Avatar as AvatarRoot, Fallback as AvatarFallback, Image as AvatarImage } from '@radix-ui/react-avatar';
|
|
5
7
|
import { PresenceBadge } from './presence';
|
|
6
8
|
import { cn, getInitialsFromName } from './utils';
|
|
7
9
|
|
|
8
|
-
interface AvatarProps extends
|
|
10
|
+
interface AvatarProps extends AvatarRootProps {
|
|
9
11
|
src?: string;
|
|
10
12
|
name?: string;
|
|
11
13
|
online?: boolean;
|
|
12
14
|
presence?: PresenceStatus;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
onlineProps?: React.ComponentProps<'div'>;
|
|
16
|
+
presenceProps?: PresenceBadgeProps;
|
|
17
|
+
imageProps?: AvatarImageProps;
|
|
18
|
+
fallbackProps?: AvatarFallbackProps;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
function AvatarBase(
|
|
18
|
-
{
|
|
22
|
+
{
|
|
23
|
+
src,
|
|
24
|
+
name = 'NA',
|
|
25
|
+
online,
|
|
26
|
+
presence,
|
|
27
|
+
className,
|
|
28
|
+
onlineProps,
|
|
29
|
+
presenceProps,
|
|
30
|
+
imageProps,
|
|
31
|
+
fallbackProps,
|
|
32
|
+
...props
|
|
33
|
+
}: AvatarProps,
|
|
19
34
|
ref: React.Ref<HTMLDivElement>
|
|
20
35
|
) {
|
|
21
36
|
const initials = getInitialsFromName(name);
|
|
22
37
|
|
|
23
38
|
return (
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<
|
|
39
|
+
<AvatarRoot ref={ref} {...props} className={cn('relative flex size-8 shrink-0 rounded-full', className)}>
|
|
40
|
+
<AvatarImage
|
|
41
|
+
{...imageProps}
|
|
42
|
+
src={src}
|
|
43
|
+
className={cn('aspect-square size-full rounded-full overflow-hidden object-cover', imageProps?.className)}
|
|
44
|
+
/>
|
|
45
|
+
<AvatarFallback
|
|
46
|
+
{...fallbackProps}
|
|
47
|
+
className={cn('bg-muted flex size-full items-center justify-center', fallbackProps?.className)}
|
|
48
|
+
>
|
|
31
49
|
{initials}
|
|
32
|
-
</
|
|
50
|
+
</AvatarFallback>
|
|
33
51
|
{online && (
|
|
34
|
-
<div
|
|
52
|
+
<div
|
|
53
|
+
{...onlineProps}
|
|
54
|
+
className={cn('rounded-full border-2 bg-green-600 border-green-200 size-3.5', onlineProps?.className)}
|
|
55
|
+
/>
|
|
35
56
|
)}
|
|
36
|
-
<PresenceBadge
|
|
37
|
-
|
|
57
|
+
<PresenceBadge
|
|
58
|
+
status={presence}
|
|
59
|
+
{...presenceProps}
|
|
60
|
+
className={cn('absolute -bottom-0.5 -right-1', presenceProps?.className)}
|
|
61
|
+
/>
|
|
62
|
+
</AvatarRoot>
|
|
38
63
|
);
|
|
39
64
|
}
|
|
40
65
|
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
TooltipProviderProps,
|
|
4
|
+
TooltipProps,
|
|
5
|
+
TooltipTriggerProps,
|
|
6
|
+
TooltipPortalProps,
|
|
7
|
+
TooltipContentProps,
|
|
8
|
+
TooltipArrowProps,
|
|
9
|
+
} from '@radix-ui/react-tooltip';
|
|
10
|
+
import { forwardRef, memo } from 'react';
|
|
11
|
+
import {
|
|
12
|
+
Provider as TooltipProvider,
|
|
13
|
+
Root as TooltipRoot,
|
|
14
|
+
Trigger as TooltipTrigger,
|
|
15
|
+
Portal as TooltipPortal,
|
|
16
|
+
Content as TooltipContent,
|
|
17
|
+
Arrow as TooltipArrow,
|
|
18
|
+
} from '@radix-ui/react-tooltip';
|
|
19
|
+
import { cn } from './utils';
|
|
20
|
+
|
|
21
|
+
type StatusName = string | 'unknown';
|
|
22
|
+
|
|
23
|
+
interface StatusIndicatorProps extends React.ComponentProps<'div'> {
|
|
24
|
+
status?: StatusName;
|
|
25
|
+
statusColorConfig?: Record<StatusName, string>;
|
|
26
|
+
statusProps?: React.ComponentProps<'div'>;
|
|
27
|
+
tooltipProviderProps?: TooltipProviderProps;
|
|
28
|
+
tooltipProps?: TooltipProps;
|
|
29
|
+
tooltipTriggerProps?: TooltipTriggerProps;
|
|
30
|
+
tooltipPortalProps?: TooltipPortalProps;
|
|
31
|
+
tooltipContentProps?: TooltipContentProps;
|
|
32
|
+
tooltipArrowProps?: TooltipArrowProps;
|
|
33
|
+
tooltip?: string;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
className?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function StatusIndicatorBase(
|
|
39
|
+
{
|
|
40
|
+
status = 'unknown',
|
|
41
|
+
statusColorConfig = { unknown: 'bg-gray-500 border-gray-600' },
|
|
42
|
+
statusProps,
|
|
43
|
+
tooltipProviderProps,
|
|
44
|
+
tooltipProps,
|
|
45
|
+
tooltipTriggerProps,
|
|
46
|
+
tooltipPortalProps,
|
|
47
|
+
tooltipContentProps,
|
|
48
|
+
tooltipArrowProps,
|
|
49
|
+
tooltip,
|
|
50
|
+
disabled,
|
|
51
|
+
className,
|
|
52
|
+
...props
|
|
53
|
+
}: StatusIndicatorProps,
|
|
54
|
+
ref: React.Ref<HTMLDivElement>
|
|
55
|
+
) {
|
|
56
|
+
if (disabled) return null;
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div ref={ref} {...props} className={cn('absolute top-0 left-0', className)}>
|
|
60
|
+
<TooltipProvider {...tooltipProviderProps}>
|
|
61
|
+
<TooltipRoot {...tooltipProps}>
|
|
62
|
+
<TooltipTrigger asChild {...tooltipTriggerProps}>
|
|
63
|
+
<div
|
|
64
|
+
{...statusProps}
|
|
65
|
+
className={cn(
|
|
66
|
+
'rounded-full size-4 border border-primary-foreground',
|
|
67
|
+
statusColorConfig[status],
|
|
68
|
+
statusProps?.className
|
|
69
|
+
)}
|
|
70
|
+
/>
|
|
71
|
+
</TooltipTrigger>
|
|
72
|
+
<TooltipPortal {...tooltipPortalProps}>
|
|
73
|
+
<TooltipContent
|
|
74
|
+
{...tooltipContentProps}
|
|
75
|
+
className={cn(
|
|
76
|
+
'bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
|
|
77
|
+
tooltipContentProps?.className
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
<span>{tooltip || status || 'unknown'}</span>
|
|
81
|
+
<TooltipArrow
|
|
82
|
+
{...tooltipArrowProps}
|
|
83
|
+
className={cn(
|
|
84
|
+
'bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px]',
|
|
85
|
+
tooltipArrowProps?.className
|
|
86
|
+
)}
|
|
87
|
+
/>
|
|
88
|
+
</TooltipContent>
|
|
89
|
+
</TooltipPortal>
|
|
90
|
+
</TooltipRoot>
|
|
91
|
+
</TooltipProvider>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const StatusIndicator = memo(forwardRef(StatusIndicatorBase));
|
|
97
|
+
|
|
98
|
+
StatusIndicator.displayName = 'StatusIndicator';
|
|
99
|
+
|
|
100
|
+
export { StatusIndicator };
|
|
@@ -11,15 +11,7 @@ interface DismissLayerProps extends React.ComponentProps<'div'> {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function DismissLayerBase(
|
|
14
|
-
{
|
|
15
|
-
active,
|
|
16
|
-
onDismiss,
|
|
17
|
-
disableClickOutside = false,
|
|
18
|
-
disableEscKeyPress = false,
|
|
19
|
-
disabled,
|
|
20
|
-
className,
|
|
21
|
-
...props
|
|
22
|
-
}: DismissLayerProps,
|
|
14
|
+
{ active, onDismiss, disableClickOutside = false, disableEscKeyPress = false, disabled, ...props }: DismissLayerProps,
|
|
23
15
|
ref: React.Ref<HTMLDivElement>
|
|
24
16
|
) {
|
|
25
17
|
const innerRef = useRef<HTMLDivElement>(null);
|
|
@@ -58,11 +50,11 @@ function DismissLayerBase(
|
|
|
58
50
|
return (
|
|
59
51
|
<div
|
|
60
52
|
ref={innerRef}
|
|
61
|
-
onClick={handleClickOrTouch}
|
|
62
|
-
onTouchStart={handleClickOrTouch}
|
|
63
|
-
className={cn('fixed top-0 left-0 z-40 size-full bg-black/20', className)}
|
|
64
53
|
aria-hidden
|
|
65
54
|
{...props}
|
|
55
|
+
onClick={handleClickOrTouch}
|
|
56
|
+
onTouchStart={handleClickOrTouch}
|
|
57
|
+
className={cn('fixed top-0 left-0 z-40 size-full bg-black/20', props?.className)}
|
|
66
58
|
/>
|
|
67
59
|
);
|
|
68
60
|
}
|
|
@@ -5,15 +5,23 @@ import { cn } from './utils';
|
|
|
5
5
|
interface PlaceholderTextProps extends React.ComponentProps<'div'> {
|
|
6
6
|
title?: string;
|
|
7
7
|
titles?: string[];
|
|
8
|
+
rowProps?: React.ComponentProps<'div'>;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
function PlaceholderTextBase(
|
|
11
|
+
function PlaceholderTextBase(
|
|
12
|
+
{ title, titles = [], rowProps, ...props }: PlaceholderTextProps,
|
|
13
|
+
ref: React.Ref<HTMLDivElement>
|
|
14
|
+
) {
|
|
11
15
|
const rows = typeof title === 'string' ? [title, ...titles] : titles;
|
|
12
16
|
|
|
13
17
|
return (
|
|
14
|
-
<div
|
|
18
|
+
<div
|
|
19
|
+
ref={ref}
|
|
20
|
+
{...props}
|
|
21
|
+
className={cn('absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2', props?.className)}
|
|
22
|
+
>
|
|
15
23
|
{rows.map((row, index) => (
|
|
16
|
-
<div key={`placeholder-text-${index}`} className=
|
|
24
|
+
<div key={`placeholder-text-${index}`} {...rowProps} className={cn('text-center', rowProps?.className)}>
|
|
17
25
|
{row}
|
|
18
26
|
</div>
|
|
19
27
|
))}
|
|
@@ -2,31 +2,38 @@ import type React from 'react';
|
|
|
2
2
|
import type { LucideProps } from 'lucide-react';
|
|
3
3
|
import { memo } from 'react';
|
|
4
4
|
import { CircleCheck, CircleMinus, CircleQuestionMark, Clock } from 'lucide-react';
|
|
5
|
-
import { capitalize, cn
|
|
5
|
+
import { capitalize, cn } from './utils';
|
|
6
6
|
|
|
7
|
-
type PresenceStatus =
|
|
8
|
-
|
|
9
|
-
interface PresenceBadgeProps extends LucideProps {
|
|
10
|
-
status: PresenceStatus;
|
|
11
|
-
}
|
|
7
|
+
type PresenceStatus = 'available' | 'busy' | 'away' | 'unknown' | undefined;
|
|
12
8
|
|
|
13
9
|
interface PresenceProps extends React.ComponentProps<'div'> {
|
|
14
10
|
badge?: boolean;
|
|
15
11
|
status: PresenceStatus;
|
|
16
12
|
label: string;
|
|
13
|
+
badgeProps?: PresenceBadgeProps;
|
|
14
|
+
labelProps?: React.ComponentProps<'span'>;
|
|
17
15
|
}
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
interface PresenceBadgeProps extends LucideProps {
|
|
18
|
+
status?: PresenceStatus;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function PresenceBadgeBase({ status, ...props }: PresenceBadgeProps) {
|
|
20
22
|
switch (status) {
|
|
21
|
-
case
|
|
22
|
-
return
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
case
|
|
26
|
-
return <
|
|
27
|
-
case
|
|
23
|
+
case 'available':
|
|
24
|
+
return (
|
|
25
|
+
<CircleCheck {...props} className={cn('rounded-full text-white bg-green-600 size-4.5', props?.className)} />
|
|
26
|
+
);
|
|
27
|
+
case 'busy':
|
|
28
|
+
return <CircleMinus {...props} className={cn('rounded-full text-white bg-red-600 size-4.5', props?.className)} />;
|
|
29
|
+
case 'away':
|
|
30
|
+
return <Clock {...props} className={cn('rounded-full text-white bg-yellow-500 size-4.5', props?.className)} />;
|
|
31
|
+
case 'unknown':
|
|
28
32
|
return (
|
|
29
|
-
<CircleQuestionMark
|
|
33
|
+
<CircleQuestionMark
|
|
34
|
+
{...props}
|
|
35
|
+
className={cn('rounded-full text-white bg-gray-500 size-4.5', props?.className)}
|
|
36
|
+
/>
|
|
30
37
|
);
|
|
31
38
|
default:
|
|
32
39
|
return null;
|
|
@@ -37,13 +44,13 @@ const PresenceBadge = memo(PresenceBadgeBase);
|
|
|
37
44
|
|
|
38
45
|
PresenceBadge.displayName = 'PresenceBadge';
|
|
39
46
|
|
|
40
|
-
function PresenceBase({ badge = true, status, label,
|
|
47
|
+
function PresenceBase({ badge = true, status, label, badgeProps, labelProps, ...props }: PresenceProps) {
|
|
41
48
|
const presence = capitalize(label || status);
|
|
42
49
|
|
|
43
50
|
return (
|
|
44
|
-
<div className={cn('flex items-center gap-2', className)}
|
|
45
|
-
{badge && <PresenceBadge status={status} />}
|
|
46
|
-
<span>{presence}</span>
|
|
51
|
+
<div {...props} className={cn('flex items-center gap-2', props?.className)}>
|
|
52
|
+
{badge && <PresenceBadge status={status} {...badgeProps} />}
|
|
53
|
+
<span {...labelProps}>{presence}</span>
|
|
47
54
|
</div>
|
|
48
55
|
);
|
|
49
56
|
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
TooltipProviderProps,
|
|
4
|
+
TooltipProps,
|
|
5
|
+
TooltipTriggerProps,
|
|
6
|
+
TooltipPortalProps,
|
|
7
|
+
TooltipContentProps,
|
|
8
|
+
TooltipArrowProps,
|
|
9
|
+
} from '@radix-ui/react-tooltip';
|
|
10
|
+
import { forwardRef, memo } from 'react';
|
|
11
|
+
import {
|
|
12
|
+
Provider as TooltipProvider,
|
|
13
|
+
Root as TooltipRoot,
|
|
14
|
+
Trigger as TooltipTrigger,
|
|
15
|
+
Portal as TooltipPortal,
|
|
16
|
+
Content as TooltipContent,
|
|
17
|
+
Arrow as TooltipArrow,
|
|
18
|
+
} from '@radix-ui/react-tooltip';
|
|
19
|
+
import { cn } from './utils';
|
|
20
|
+
|
|
21
|
+
type StatusName = string | 'unknown';
|
|
22
|
+
|
|
23
|
+
interface StatusIndicatorProps extends React.ComponentProps<'div'> {
|
|
24
|
+
status?: StatusName;
|
|
25
|
+
statusColorConfig?: Record<StatusName, string>;
|
|
26
|
+
statusProps?: React.ComponentProps<'div'>;
|
|
27
|
+
tooltipProviderProps?: TooltipProviderProps;
|
|
28
|
+
tooltipProps?: TooltipProps;
|
|
29
|
+
tooltipTriggerProps?: TooltipTriggerProps;
|
|
30
|
+
tooltipPortalProps?: TooltipPortalProps;
|
|
31
|
+
tooltipContentProps?: TooltipContentProps;
|
|
32
|
+
tooltipArrowProps?: TooltipArrowProps;
|
|
33
|
+
tooltip?: string;
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
className?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function StatusIndicatorBase(
|
|
39
|
+
{
|
|
40
|
+
status = 'unknown',
|
|
41
|
+
statusColorConfig = { unknown: 'bg-gray-500 border-gray-600' },
|
|
42
|
+
statusProps,
|
|
43
|
+
tooltipProviderProps,
|
|
44
|
+
tooltipProps,
|
|
45
|
+
tooltipTriggerProps,
|
|
46
|
+
tooltipPortalProps,
|
|
47
|
+
tooltipContentProps,
|
|
48
|
+
tooltipArrowProps,
|
|
49
|
+
tooltip,
|
|
50
|
+
disabled,
|
|
51
|
+
className,
|
|
52
|
+
...props
|
|
53
|
+
}: StatusIndicatorProps,
|
|
54
|
+
ref: React.Ref<HTMLDivElement>
|
|
55
|
+
) {
|
|
56
|
+
if (disabled) return null;
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div ref={ref} {...props} className={cn('absolute top-0 left-0', className)}>
|
|
60
|
+
<TooltipProvider {...tooltipProviderProps}>
|
|
61
|
+
<TooltipRoot {...tooltipProps}>
|
|
62
|
+
<TooltipTrigger asChild {...tooltipTriggerProps}>
|
|
63
|
+
<div
|
|
64
|
+
{...statusProps}
|
|
65
|
+
className={cn(
|
|
66
|
+
'rounded-full size-4 border border-primary-foreground',
|
|
67
|
+
statusColorConfig[status],
|
|
68
|
+
statusProps?.className
|
|
69
|
+
)}
|
|
70
|
+
/>
|
|
71
|
+
</TooltipTrigger>
|
|
72
|
+
<TooltipPortal {...tooltipPortalProps}>
|
|
73
|
+
<TooltipContent
|
|
74
|
+
{...tooltipContentProps}
|
|
75
|
+
className={cn(
|
|
76
|
+
'bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance',
|
|
77
|
+
tooltipContentProps?.className
|
|
78
|
+
)}
|
|
79
|
+
>
|
|
80
|
+
<span>{tooltip || status || 'unknown'}</span>
|
|
81
|
+
<TooltipArrow
|
|
82
|
+
{...tooltipArrowProps}
|
|
83
|
+
className={cn(
|
|
84
|
+
'bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px]',
|
|
85
|
+
tooltipArrowProps?.className
|
|
86
|
+
)}
|
|
87
|
+
/>
|
|
88
|
+
</TooltipContent>
|
|
89
|
+
</TooltipPortal>
|
|
90
|
+
</TooltipRoot>
|
|
91
|
+
</TooltipProvider>
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const StatusIndicator = memo(forwardRef(StatusIndicatorBase));
|
|
97
|
+
|
|
98
|
+
StatusIndicator.displayName = 'StatusIndicator';
|
|
99
|
+
|
|
100
|
+
export { StatusIndicator, type StatusIndicatorProps };
|
|
@@ -48,8 +48,8 @@ function StreamViewBase(
|
|
|
48
48
|
autoPlay
|
|
49
49
|
playsInline
|
|
50
50
|
muted={isMuted}
|
|
51
|
-
className={cn(defaultClassName, mirrorClassName, className)}
|
|
52
51
|
{...props}
|
|
52
|
+
className={cn(defaultClassName, mirrorClassName, className)}
|
|
53
53
|
/>
|
|
54
54
|
);
|
|
55
55
|
}
|
|
@@ -86,16 +86,10 @@ interface FullscreenStreamViewProps extends React.ComponentProps<'div'> {
|
|
|
86
86
|
navElement?: React.ReactElement<HTMLElement>;
|
|
87
87
|
hideIconElement?: React.ReactElement;
|
|
88
88
|
showIconElement?: React.ReactElement;
|
|
89
|
-
containerClassName?: string;
|
|
90
|
-
fullscreenButtonClassName?: string;
|
|
91
|
-
fullscreenButtonIconClassName?: string;
|
|
92
|
-
pipContainerClassName?: string;
|
|
93
|
-
pipButtonClassName?: string;
|
|
94
|
-
pipButtonIconClassName?: string;
|
|
95
89
|
containerProps?: React.ComponentProps<'div'>;
|
|
96
90
|
fullscreenButtonProps?: React.ComponentProps<'button'>;
|
|
97
91
|
fullscreenButtonIconProps?: LucideProps;
|
|
98
|
-
|
|
92
|
+
pipProps?: React.ComponentProps<'div'>;
|
|
99
93
|
pipButtonProps?: React.ComponentProps<'button'>;
|
|
100
94
|
pipButtonIconProps?: LucideProps;
|
|
101
95
|
}
|
|
@@ -114,18 +108,12 @@ function FullscreenStreamViewBase(
|
|
|
114
108
|
navElement,
|
|
115
109
|
hideIconElement,
|
|
116
110
|
showIconElement,
|
|
117
|
-
containerClassName,
|
|
118
|
-
fullscreenButtonClassName,
|
|
119
|
-
fullscreenButtonIconClassName,
|
|
120
|
-
pipContainerClassName,
|
|
121
|
-
pipButtonClassName,
|
|
122
|
-
pipButtonIconClassName,
|
|
123
|
-
containerProps,
|
|
124
111
|
fullscreenButtonProps,
|
|
125
112
|
fullscreenButtonIconProps,
|
|
126
|
-
|
|
113
|
+
pipProps,
|
|
127
114
|
pipButtonProps,
|
|
128
115
|
pipButtonIconProps,
|
|
116
|
+
...props
|
|
129
117
|
}: FullscreenStreamViewProps,
|
|
130
118
|
ref: React.Ref<FullscreenStreamViewRef>
|
|
131
119
|
) {
|
|
@@ -183,45 +171,45 @@ function FullscreenStreamViewBase(
|
|
|
183
171
|
return (
|
|
184
172
|
<div
|
|
185
173
|
ref={innerRef}
|
|
186
|
-
|
|
187
|
-
{
|
|
174
|
+
{...props}
|
|
175
|
+
className={cn('relative flex items-center justify-center size-full', props?.className)}
|
|
188
176
|
>
|
|
189
177
|
{element}
|
|
190
178
|
<button
|
|
191
179
|
onClick={toggleFullscreen}
|
|
180
|
+
{...fullscreenButtonProps}
|
|
192
181
|
className={cn(
|
|
193
182
|
'absolute top-2 right-2 p-1 rounded-md bg-black/50 text-white hover:bg-black/80 z-10 shadow-xs shadow-white/25',
|
|
194
|
-
|
|
183
|
+
fullscreenButtonProps?.className
|
|
195
184
|
)}
|
|
196
|
-
{...fullscreenButtonProps}
|
|
197
185
|
>
|
|
198
186
|
{isFullscreen
|
|
199
|
-
? hideIconElement || <Minimize
|
|
200
|
-
: showIconElement || <Maximize
|
|
187
|
+
? hideIconElement || <Minimize {...fullscreenButtonIconProps} />
|
|
188
|
+
: showIconElement || <Maximize {...fullscreenButtonIconProps} />}
|
|
201
189
|
</button>
|
|
202
190
|
<div className="absolute size-full p-2 flex flex-col justify-end items-center">
|
|
203
191
|
{isFullscreen && pipElement && (
|
|
204
192
|
<div className="relative size-full flex items-end justify-end">
|
|
205
193
|
{isPictureInPicture && (
|
|
206
194
|
<div
|
|
195
|
+
{...pipProps}
|
|
207
196
|
className={cn(
|
|
208
197
|
'max-w-1/4 max-h-1/4 aspect-4/3 overflow-hidden rounded-md shadow-md shadow-white/25',
|
|
209
|
-
|
|
198
|
+
pipProps?.className
|
|
210
199
|
)}
|
|
211
|
-
{...pipContainerProps}
|
|
212
200
|
>
|
|
213
201
|
{pipElement}
|
|
214
202
|
</div>
|
|
215
203
|
)}
|
|
216
204
|
<button
|
|
217
205
|
onClick={togglePictureInPicture}
|
|
206
|
+
{...pipButtonProps}
|
|
218
207
|
className={cn(
|
|
219
208
|
'absolute bottom-2 right-2 p-1 rounded-md bg-black/50 text-white hover:bg-black/80 z-10 shadow-xs shadow-white/25',
|
|
220
|
-
|
|
209
|
+
pipButtonProps?.className
|
|
221
210
|
)}
|
|
222
|
-
{...pipButtonProps}
|
|
223
211
|
>
|
|
224
|
-
<PictureInPicture2
|
|
212
|
+
<PictureInPicture2 {...pipButtonIconProps} />
|
|
225
213
|
</button>
|
|
226
214
|
</div>
|
|
227
215
|
)}
|
package/src/components/utils.ts
CHANGED
|
@@ -19,10 +19,3 @@ export function getInitialsFromName(name?: string): string {
|
|
|
19
19
|
export function capitalize(str?: string): string {
|
|
20
20
|
return typeof str === 'string' && str.length > 0 ? `${str[0]?.toUpperCase()}${str.slice(1)}` : '';
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
export enum UserPresence {
|
|
24
|
-
AVAILABLE = 'available',
|
|
25
|
-
BUSY = 'busy',
|
|
26
|
-
AWAY = 'away',
|
|
27
|
-
UNKNOWN = 'unknown',
|
|
28
|
-
}
|