@connectycube/react-ui-kit 0.0.20 → 0.0.22

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.
Files changed (68) hide show
  1. package/configs/dependencies.json +19 -4
  2. package/configs/imports.json +7 -2
  3. package/dist/index.cjs +1 -1
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.js +1 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/types/components/attachment.d.ts +5 -6
  8. package/dist/types/components/attachment.d.ts.map +1 -1
  9. package/dist/types/components/chat-bubble.d.ts +32 -0
  10. package/dist/types/components/chat-bubble.d.ts.map +1 -0
  11. package/dist/types/components/chat-input.d.ts +27 -0
  12. package/dist/types/components/chat-input.d.ts.map +1 -0
  13. package/dist/types/components/chat-list.d.ts +30 -0
  14. package/dist/types/components/chat-list.d.ts.map +1 -0
  15. package/dist/types/components/checkbox.d.ts +11 -0
  16. package/dist/types/components/checkbox.d.ts.map +1 -0
  17. package/dist/types/components/dialogs-list.d.ts +14 -0
  18. package/dist/types/components/dialogs-list.d.ts.map +1 -0
  19. package/dist/types/components/file-picker.d.ts +1 -1
  20. package/dist/types/components/file-picker.d.ts.map +1 -1
  21. package/dist/types/components/placeholder-text.d.ts.map +1 -1
  22. package/dist/types/components/quick-actions.d.ts +14 -0
  23. package/dist/types/components/quick-actions.d.ts.map +1 -0
  24. package/dist/types/components/status-call.d.ts +8 -0
  25. package/dist/types/components/status-call.d.ts.map +1 -0
  26. package/dist/types/index.d.ts +7 -2
  27. package/dist/types/index.d.ts.map +1 -1
  28. package/gen/components/attachment.jsx +6 -6
  29. package/gen/components/button.jsx +1 -1
  30. package/gen/components/{chat-message.jsx → chat-bubble.jsx} +69 -48
  31. package/gen/components/chat-input.jsx +152 -0
  32. package/gen/components/chat-list.jsx +151 -0
  33. package/gen/components/checkbox.jsx +30 -0
  34. package/gen/components/dialog-item.jsx +1 -1
  35. package/gen/components/dialogs-list.jsx +73 -0
  36. package/gen/components/dismiss-layer.jsx +1 -1
  37. package/gen/components/file-picker.jsx +2 -2
  38. package/gen/components/placeholder-text.jsx +5 -1
  39. package/gen/components/quick-actions.jsx +62 -0
  40. package/gen/components/search.jsx +1 -1
  41. package/gen/components/status-call.jsx +18 -0
  42. package/gen/components/stream-view.jsx +8 -8
  43. package/gen/index.js +14 -2
  44. package/package.json +11 -8
  45. package/src/components/attachment.tsx +16 -14
  46. package/src/components/button.tsx +1 -1
  47. package/src/components/chat-bubble.tsx +176 -0
  48. package/src/components/chat-input.tsx +172 -0
  49. package/src/components/chat-list.tsx +164 -0
  50. package/src/components/checkbox.tsx +40 -0
  51. package/src/components/connectycube-ui/chat-input.tsx +174 -0
  52. package/src/components/dialog-item.tsx +1 -1
  53. package/src/components/dialogs-list.tsx +84 -0
  54. package/src/components/dismiss-layer.tsx +1 -1
  55. package/src/components/file-picker.tsx +3 -3
  56. package/src/components/placeholder-text.tsx +5 -1
  57. package/src/components/quick-actions.tsx +74 -0
  58. package/src/components/search.tsx +1 -1
  59. package/src/components/status-call.tsx +23 -0
  60. package/src/components/stream-view.tsx +8 -8
  61. package/src/index.ts +17 -2
  62. package/dist/types/components/call-message.d.ts +0 -17
  63. package/dist/types/components/call-message.d.ts.map +0 -1
  64. package/dist/types/components/chat-message.d.ts +0 -30
  65. package/dist/types/components/chat-message.d.ts.map +0 -1
  66. package/gen/components/call-message.jsx +0 -62
  67. package/src/components/call-message.tsx +0 -75
  68. package/src/components/chat-message.tsx +0 -138
@@ -0,0 +1,174 @@
1
+ import type React from 'react';
2
+ import { ChangeEvent, forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
3
+ import TextareaAutosize, { type TextareaAutosizeProps, type TextareaHeightChangeMeta } from 'react-textarea-autosize';
4
+ import { SendHorizontal, type LucideProps } from 'lucide-react';
5
+ import { Label, type LabelProps } from './label';
6
+ import { cn } from './utils';
7
+
8
+ interface ChatInputSendProps extends LabelProps {
9
+ onSend: () => void;
10
+ iconElement?: React.ReactNode;
11
+ iconProps?: LucideProps;
12
+ }
13
+
14
+ interface ChatInputProps extends TextareaAutosizeProps, React.ComponentProps<'textarea'> {
15
+ key?: string;
16
+ children?: React.ReactNode;
17
+ pending?: boolean;
18
+ draft?: string;
19
+ onSend?: (value?: string) => void;
20
+ onDraft?: (value?: string) => void;
21
+ onTyping?: (typing?: boolean) => void;
22
+ onHeightGrow?: (data: { height: number; shift: number }) => void;
23
+ hideChatInputSend?: boolean;
24
+ chatInputSendProps?: ChatInputSendProps;
25
+ containerProps?: React.ComponentProps<'div'>;
26
+ }
27
+
28
+ function ChatInputSendBase(
29
+ { onSend = () => {}, iconElement, iconProps, ...props }: ChatInputSendProps,
30
+ ref: React.ForwardedRef<HTMLLabelElement>
31
+ ) {
32
+ return (
33
+ <Label
34
+ ref={ref}
35
+ {...props}
36
+ onClick={onSend}
37
+ className={cn('group rounded-full py-2 pl-2.5 pr-1.5 cursor-pointer bg-ring/90 hover:bg-ring', props?.className)}
38
+ >
39
+ {iconElement || (
40
+ <SendHorizontal
41
+ {...iconProps}
42
+ className={cn(
43
+ 'size-7 text-background group-hover:scale-110 transition-all duration-200 ease-out',
44
+ iconProps?.className
45
+ )}
46
+ />
47
+ )}
48
+ </Label>
49
+ );
50
+ }
51
+
52
+ const ChatInputSend = forwardRef<HTMLLabelElement, ChatInputSendProps>(ChatInputSendBase);
53
+
54
+ ChatInputSend.displayName = 'ChatInputSend';
55
+
56
+ function ChatInputBase(
57
+ {
58
+ key,
59
+ pending = false,
60
+ draft,
61
+ onSend = () => {},
62
+ onDraft = () => {},
63
+ onTyping = () => {},
64
+ onHeightGrow = () => {},
65
+ hideChatInputSend = false,
66
+ chatInputSendProps,
67
+ containerProps,
68
+ children,
69
+ ...props
70
+ }: ChatInputProps,
71
+ ref: React.ForwardedRef<HTMLTextAreaElement>
72
+ ) {
73
+ const [value, setValue] = useState<string>();
74
+ const textareaRef = useRef<HTMLTextAreaElement>(null);
75
+ const textareaHeightRef = useRef<number>(0);
76
+ const typingRef = useRef<boolean>(false);
77
+ const typingTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
78
+ const handleStopTyping = () => {
79
+ typingRef.current = false;
80
+ onTyping(false);
81
+
82
+ if (typingTimeoutRef.current) {
83
+ clearTimeout(typingTimeoutRef.current);
84
+ typingTimeoutRef.current = undefined;
85
+ }
86
+ };
87
+ const handleOnChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
88
+ const { data } = event.nativeEvent as InputEvent;
89
+
90
+ setValue(event.target.value);
91
+
92
+ if (!typingRef.current && typeof data === 'string' && data.length > 0) {
93
+ typingRef.current = true;
94
+ onTyping(true);
95
+ }
96
+
97
+ clearTimeout(typingTimeoutRef.current);
98
+ typingTimeoutRef.current = setTimeout(handleStopTyping, 5000);
99
+ };
100
+ const handleOnSend = async () => {
101
+ handleStopTyping();
102
+
103
+ if (typeof value === 'string' && value.length > 0) {
104
+ onSend(value.trim());
105
+ setValue('');
106
+ textareaRef.current?.focus();
107
+ }
108
+ };
109
+ const handleOnHeightChange = (height: number, meta: TextareaHeightChangeMeta) => {
110
+ if (!height && !meta) return;
111
+
112
+ if (height !== textareaHeightRef.current) {
113
+ const shift = height > textareaHeightRef.current ? meta.rowHeight : -meta.rowHeight;
114
+
115
+ onHeightGrow({ height, shift });
116
+ }
117
+
118
+ textareaHeightRef.current = height;
119
+ };
120
+ const handleOnKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {
121
+ if (event.key === 'Enter' && event.shiftKey === false) {
122
+ event.preventDefault();
123
+ props.onKeyDown?.(event);
124
+ handleOnSend();
125
+ }
126
+ };
127
+
128
+ useEffect(() => {
129
+ const textarea = textareaRef.current;
130
+
131
+ handleStopTyping();
132
+ queueMicrotask(() => setValue(draft));
133
+ textarea?.focus();
134
+
135
+ return () => {
136
+ onDraft(textarea?.value);
137
+ };
138
+ }, [key]);
139
+
140
+ useImperativeHandle(ref, () => textareaRef.current!, [textareaRef]);
141
+
142
+ return (
143
+ <div {...containerProps} className={cn('flex items-end gap-2', containerProps?.className)}>
144
+ <TextareaAutosize
145
+ key={key}
146
+ ref={textareaRef}
147
+ autoFocus
148
+ minRows={1}
149
+ maxRows={8}
150
+ {...props}
151
+ value={value}
152
+ onChange={handleOnChange}
153
+ onKeyDown={handleOnKeyDown}
154
+ onHeightChange={handleOnHeightChange}
155
+ className={cn(
156
+ 'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-11 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
157
+ 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring',
158
+ 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
159
+ 'resize-none py-2.5',
160
+ pending && 'pointer-events-none bg-muted-foreground border-ring ring-ring/50 animate-pulse',
161
+ props?.className
162
+ )}
163
+ />
164
+ {!hideChatInputSend && <ChatInputSend onSend={handleOnSend} {...chatInputSendProps} />}
165
+ {children}
166
+ </div>
167
+ );
168
+ }
169
+
170
+ const ChatInput = forwardRef<HTMLTextAreaElement, ChatInputProps>(ChatInputBase);
171
+
172
+ ChatInput.displayName = 'ChatInput';
173
+
174
+ export { ChatInput };
@@ -99,7 +99,7 @@ function DialogItemBase(
99
99
  onClick={onSelect}
100
100
  className={cn(
101
101
  'flex items-start gap-2 px-2 flex-1 cursor-pointer',
102
- 'transition-colors duration-200 ease-linear',
102
+ 'transition-colors duration-200 ease-out',
103
103
  `${selected ? 'border-l-[0.25em] pl-1 border-l-ring bg-ring/20' : 'hover:bg-ring/5'}`,
104
104
  props?.className
105
105
  )}
@@ -0,0 +1,84 @@
1
+ import type React from 'react';
2
+ import { forwardRef, useImperativeHandle, useRef } from 'react';
3
+ import { VList, type VListHandle, type VListProps } from 'virtua';
4
+ import { PlaceholderText } from './placeholder-text';
5
+
6
+ interface DialogsListProps extends VListProps {
7
+ pending?: boolean;
8
+ pendingListLength?: number;
9
+ offsetToReach?: number;
10
+ placeholderVisible?: boolean;
11
+ placeholderTitles?: string[];
12
+ onScrollStartReached?: () => void;
13
+ onScrollEndReached?: () => void;
14
+ }
15
+
16
+ const PendingItem = () => (
17
+ <div className="flex flex-row gap-2 mx-2 border-b">
18
+ <div className="size-13 my-2 rounded-full bg-muted animate-pulse" />
19
+ <div className="flex-1 text-muted">
20
+ <div className="flex flex-row items-center justify-between h-6">
21
+ <div className="w-2/3 h-4 rounded-full bg-muted animate-pulse" />
22
+ <div className="w-1/6 h-3.5 rounded-full bg-muted animate-pulse" />
23
+ </div>
24
+ <div className="flex flex-col items-start justify-around h-10">
25
+ <div className="w-7/8 h-3.5 rounded-full bg-muted animate-pulse" />
26
+ <div className="w-5/6 h-3.5 rounded-full bg-muted animate-pulse" />
27
+ </div>
28
+ </div>
29
+ </div>
30
+ );
31
+
32
+ function DialogsListBase(
33
+ {
34
+ children,
35
+ pending,
36
+ pendingListLength = 5,
37
+ offsetToReach = 50,
38
+ placeholderVisible,
39
+ placeholderTitles = ['No dialogs yet.', 'Start a conversation!'],
40
+ onScrollStartReached,
41
+ onScrollEndReached,
42
+ ...props
43
+ }: DialogsListProps,
44
+ ref: React.ForwardedRef<VListHandle>
45
+ ) {
46
+ const vListRef = useRef<VListHandle>(null);
47
+ const skeletonList = Array.from({ length: pendingListLength }).map((_, i) => (
48
+ <PendingItem key={`pending_dialog_item_${i}`} />
49
+ ));
50
+ const handleOnScroll = async (offset: number) => {
51
+ props.onScroll?.(offset);
52
+
53
+ if (!vListRef.current) return;
54
+
55
+ if (typeof onScrollStartReached === 'function' && offset < offsetToReach) {
56
+ onScrollStartReached();
57
+ }
58
+
59
+ if (
60
+ typeof onScrollEndReached === 'function' &&
61
+ vListRef.current.viewportSize + offset + offsetToReach > vListRef.current.scrollSize
62
+ ) {
63
+ onScrollEndReached();
64
+ }
65
+ };
66
+
67
+ useImperativeHandle(ref, () => vListRef.current || ({} as VListHandle), []);
68
+
69
+ if (placeholderVisible) {
70
+ return <PlaceholderText titles={placeholderTitles} className="text-base text-muted" />;
71
+ }
72
+
73
+ return (
74
+ <VList ref={vListRef} onScroll={handleOnScroll} className="size-full overflow-y-scroll" {...props}>
75
+ {pending ? skeletonList : children}
76
+ </VList>
77
+ );
78
+ }
79
+
80
+ const DialogsList = forwardRef<VListHandle, DialogsListProps>(DialogsListBase);
81
+
82
+ DialogsList.displayName = 'DialogsList';
83
+
84
+ export { DialogsList, type DialogsListProps };
@@ -16,7 +16,7 @@ function DismissLayerBase(
16
16
  ) {
17
17
  const innerRef = useRef<HTMLDivElement>(null);
18
18
 
19
- useImperativeHandle(ref, () => innerRef.current!, []);
19
+ useImperativeHandle(ref, () => innerRef.current || ({} as HTMLDivElement), []);
20
20
 
21
21
  const handleClickOrTouch = useCallback(
22
22
  (e: React.MouseEvent | React.TouchEvent) => {
@@ -58,7 +58,7 @@ const handleFiles = (
58
58
  };
59
59
 
60
60
  interface FilePickerInputProps extends React.ComponentProps<'input'> {
61
- onSelectFile: (files: File[]) => void;
61
+ onSelectFile?: (files: File[]) => void;
62
62
  onInvalidFile?: () => void;
63
63
  iconElement?: React.ReactNode;
64
64
  labelProps?: LabelProps;
@@ -97,7 +97,7 @@ function FilePickerInputBase(
97
97
  {...labelProps}
98
98
  htmlFor="file-uploader"
99
99
  className={cn(
100
- 'group p-2 rounded-full hover:bg-ring/10 transition-all duration-200 ease-in-out cursor-pointer',
100
+ 'group p-2 rounded-full hover:bg-ring/10 transition-all duration-200 ease-out cursor-pointer',
101
101
  labelProps?.className
102
102
  )}
103
103
  >
@@ -105,7 +105,7 @@ function FilePickerInputBase(
105
105
  <Paperclip
106
106
  {...iconProps}
107
107
  className={cn(
108
- 'text-foreground group-hover:scale-110 transition-all duration-200 ease-in-out',
108
+ 'text-foreground group-hover:scale-110 transition-all duration-200 ease-out',
109
109
  iconProps?.className
110
110
  )}
111
111
  />
@@ -21,7 +21,11 @@ function PlaceholderTextBase(
21
21
  className={cn('absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2', props?.className)}
22
22
  >
23
23
  {rows.map((row, index) => (
24
- <div key={`placeholder-text-${index}`} {...rowProps} className={cn('text-center', rowProps?.className)}>
24
+ <div
25
+ key={`placeholder-text-${index}`}
26
+ {...rowProps}
27
+ className={cn('text-center text-muted-foreground', rowProps?.className)}
28
+ >
25
29
  {row}
26
30
  </div>
27
31
  ))}
@@ -0,0 +1,74 @@
1
+ import type React from 'react';
2
+ import { forwardRef } from 'react';
3
+ import { cn } from './utils';
4
+
5
+ interface QuickActionsProps extends React.HTMLAttributes<HTMLDivElement> {
6
+ title?: string;
7
+ description?: string;
8
+ actions?: string[];
9
+ onAction?: (action: string) => void;
10
+ containerProps?: React.ComponentProps<'div'>;
11
+ titleProps?: React.ComponentProps<'h2'>;
12
+ descriptionProps?: React.ComponentProps<'span'>;
13
+ actionProps?: React.ComponentProps<'div'>;
14
+ }
15
+
16
+ function QuickActionsBase(
17
+ {
18
+ title,
19
+ description,
20
+ actions = [],
21
+ onAction = () => {},
22
+ containerProps,
23
+ titleProps,
24
+ descriptionProps,
25
+ actionProps,
26
+ ...props
27
+ }: QuickActionsProps,
28
+ ref: React.ForwardedRef<HTMLDivElement>
29
+ ) {
30
+ if (!actions.length) {
31
+ return null;
32
+ }
33
+
34
+ return (
35
+ <div
36
+ ref={ref}
37
+ {...containerProps}
38
+ className={cn('flex flex-col h-full overflow-y-auto py-2', containerProps?.className)}
39
+ >
40
+ <div className="grow" />
41
+ <div {...props} className={cn('grid gap-2 m-4', props?.className)}>
42
+ {title && (
43
+ <h2 {...titleProps} className={cn('font-medium text-foreground text-lg', titleProps?.className)}>
44
+ {title}
45
+ </h2>
46
+ )}
47
+ {description && (
48
+ <span {...descriptionProps} className={cn('text-sm text-muted-foreground mb-4', descriptionProps?.className)}>
49
+ {description}
50
+ </span>
51
+ )}
52
+ {actions.map((action, index) => (
53
+ <div
54
+ key={index}
55
+ {...actionProps}
56
+ className={cn(
57
+ 'w-full border p-2 rounded-md wrap-break-word cursor-pointer overflow-hidden text-ellipsis bg-ring/5 hover:bg-ring/20 transition-colors duration-200 ease-out',
58
+ actionProps?.className
59
+ )}
60
+ onClick={() => onAction(action)}
61
+ >
62
+ {action}
63
+ </div>
64
+ ))}
65
+ </div>
66
+ </div>
67
+ );
68
+ }
69
+
70
+ const QuickActions = forwardRef<HTMLDivElement, QuickActionsProps>(QuickActionsBase);
71
+
72
+ QuickActions.displayName = 'QuickActions';
73
+
74
+ export { QuickActions, type QuickActionsProps };
@@ -69,7 +69,7 @@ function SearchBase(
69
69
  onClick={handleOnCancel}
70
70
  {...cancelIconProps}
71
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',
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-out',
73
73
  value.length > 0 ? 'opacity-100 scale-100' : 'opacity-0 scale-75 pointer-events-none',
74
74
  cancelIconProps?.className
75
75
  )}
@@ -0,0 +1,23 @@
1
+ import { Phone, PhoneIncoming, PhoneMissed, PhoneOutgoing, type LucideProps } from 'lucide-react';
2
+ import { cn } from './utils';
3
+
4
+ interface StatusCallProps extends LucideProps {
5
+ fromMe?: boolean;
6
+ status?: 'reject' | 'notAnswer' | 'hungUp' | 'cancel' | undefined;
7
+ }
8
+
9
+ const StatusCall: React.FC<StatusCallProps> = ({ fromMe, status, ...props }) => {
10
+ const CallIcon =
11
+ status === 'hungUp' ? Phone : fromMe ? PhoneOutgoing : status === 'reject' ? PhoneIncoming : PhoneMissed;
12
+
13
+ return (
14
+ <CallIcon
15
+ {...props}
16
+ className={cn('size-4', status === 'hungUp' ? 'text-green-500' : 'text-red-500', props?.className)}
17
+ />
18
+ );
19
+ };
20
+
21
+ StatusCall.displayName = 'StatusCall';
22
+
23
+ export { StatusCall, type StatusCallProps };
@@ -18,7 +18,7 @@ function StreamViewBase(
18
18
  const defaultClassName = 'size-full object-contain';
19
19
  const mirrorClassName = mirror ? 'scale-x-[-1]' : '';
20
20
 
21
- useImperativeHandle(ref, () => innerRef.current!);
21
+ useImperativeHandle(ref, () => innerRef.current || ({} as HTMLVideoElement), []);
22
22
 
23
23
  useEffect(() => {
24
24
  if (innerRef.current && stream) {
@@ -146,13 +146,13 @@ function FullscreenStreamViewBase(
146
146
 
147
147
  useImperativeHandle(
148
148
  ref,
149
- () =>
150
- Object.assign(innerRef.current!, {
151
- isFullscreen,
152
- isPictureInPicture,
153
- toggleFullscreen,
154
- togglePictureInPicture,
155
- }),
149
+ () => ({
150
+ ...(innerRef.current || ({} as HTMLDivElement)),
151
+ isFullscreen,
152
+ isPictureInPicture,
153
+ toggleFullscreen,
154
+ togglePictureInPicture,
155
+ }),
156
156
  [isFullscreen, isPictureInPicture, toggleFullscreen, togglePictureInPicture]
157
157
  );
158
158
 
package/src/index.ts CHANGED
@@ -23,12 +23,23 @@ export { Badge, type BadgeProps } from './components/badge';
23
23
 
24
24
  export { Button, type ButtonProps } from './components/button';
25
25
 
26
- export { CallMessage, type CallMessageProps } from './components/call-message';
26
+ export {
27
+ ChatBubbleMessage,
28
+ ChatBubbleInfo,
29
+ type ChatBubbleMessageProps,
30
+ type ChatBubbleInfoProps,
31
+ } from './components/chat-bubble';
32
+
33
+ export { ChatInput, type ChatInputProps } from './components/chat-input';
34
+
35
+ export { ChatList, type ChatListProps, type ChatListHandle } from './components/chat-list';
27
36
 
28
- export { ChatMessage, type ChatMessageProps } from './components/chat-message';
37
+ export { Checkbox, type CheckboxProps } from './components/checkbox';
29
38
 
30
39
  export { DialogItem, type DialogItemProps } from './components/dialog-item';
31
40
 
41
+ export { DialogsList, type DialogsListProps } from './components/dialogs-list';
42
+
32
43
  export { DismissLayer, type DismissLayerProps } from './components/dismiss-layer';
33
44
 
34
45
  export {
@@ -58,10 +69,14 @@ export {
58
69
  type PresenceBadgeProps,
59
70
  } from './components/presence';
60
71
 
72
+ export { QuickActions, type QuickActionsProps } from './components/quick-actions';
73
+
61
74
  export { Search, type SearchProps } from './components/search';
62
75
 
63
76
  export { Spinner, type SpinnerProps } from './components/spinner';
64
77
 
78
+ export { StatusCall, type StatusCallProps } from './components/status-call';
79
+
65
80
  export { StatusIndicator, type StatusName, type StatusIndicatorProps } from './components/status-indicator';
66
81
 
67
82
  export { StatusSent, type StatusSentProps } from './components/status-sent';
@@ -1,17 +0,0 @@
1
- import type React from 'react';
2
- import { type LucideProps } from 'lucide-react';
3
- import { type FormattedDateProps } from './formatted-date';
4
- interface CallMessageProps extends React.ComponentProps<'div'> {
5
- signal?: 'reject' | 'notAnswer' | 'hungUp' | 'cancel' | undefined;
6
- info?: string | undefined;
7
- duration?: number | undefined;
8
- fromMe: boolean;
9
- isLast: boolean;
10
- iconElement?: React.ReactNode;
11
- iconProps?: LucideProps;
12
- infoProps?: React.ComponentProps<'span'>;
13
- formattedDateProps?: FormattedDateProps;
14
- }
15
- declare const CallMessage: React.NamedExoticComponent<Omit<CallMessageProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
16
- export { CallMessage, type CallMessageProps };
17
- //# sourceMappingURL=call-message.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"call-message.d.ts","sourceRoot":"","sources":["../../../src/components/call-message.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAoD,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAClG,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAG1E,UAAU,gBAAiB,SAAQ,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC;IAC5D,MAAM,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IAClE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,SAAS,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IACzC,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC;AAsDD,QAAA,MAAM,WAAW,iGAAsE,CAAC;AAIxF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,CAAC"}
@@ -1,30 +0,0 @@
1
- import type React from 'react';
2
- import { type AttachmentProps } from './attachment';
3
- import { type AvatarProps } from './avatar';
4
- import { type FormattedDateProps } from './formatted-date';
5
- import { type LinkifyTextProps } from './linkify-text';
6
- import { type LinkPreviewProps } from './link-preview';
7
- import { type StatusSentProps } from './status-sent';
8
- interface ChatMessageProps extends React.ComponentProps<'div'> {
9
- isLast: boolean;
10
- fromMe: boolean;
11
- sameSenderAbove: boolean;
12
- title?: string;
13
- senderName?: string;
14
- senderAvatar?: string;
15
- attachmentElement?: React.ReactNode;
16
- linkifyTextElement?: React.ReactNode;
17
- linkPreviewElement?: React.ReactNode;
18
- onView?: () => void;
19
- avatarProps?: AvatarProps;
20
- bubbleProps?: React.ComponentProps<'div'>;
21
- titleProps?: React.ComponentProps<'span'>;
22
- formattedDateProps?: FormattedDateProps;
23
- statusSentProps?: StatusSentProps;
24
- attachmentProps?: AttachmentProps;
25
- linkifyTextProps?: LinkifyTextProps;
26
- linkPreviewProps?: LinkPreviewProps;
27
- }
28
- declare const ChatMessage: React.NamedExoticComponent<Omit<ChatMessageProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
29
- export { ChatMessage, type ChatMessageProps };
30
- //# sourceMappingURL=chat-message.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../../../src/components/chat-message.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAiB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAe,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAc,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAGjE,UAAU,gBAAiB,SAAQ,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC;IAC5D,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,EAAE,OAAO,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC,kBAAkB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACrC,kBAAkB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,WAAW,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;IAC1C,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAyGD,QAAA,MAAM,WAAW,iGAAsE,CAAC;AAExF,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,CAAC"}
@@ -1,62 +0,0 @@
1
- import { forwardRef, memo } from 'react';
2
- import { Phone, PhoneIncoming, PhoneMissed, PhoneOutgoing } from 'lucide-react';
3
- import { FormattedDate } from './formatted-date';
4
- import { cn } from './utils';
5
-
6
- function formatDuration(seconds) {
7
- const h = Math.floor(seconds / 3600);
8
- const m = Math.floor((seconds % 3600) / 60);
9
- const s = seconds % 60;
10
- const pad = (num) => String(num).padStart(2, '0');
11
-
12
- return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;
13
- }
14
-
15
- function CallMessageBase(
16
- {
17
- signal,
18
- info,
19
- duration = 0,
20
- fromMe = false,
21
- isLast = false,
22
- iconElement,
23
- iconProps,
24
- infoProps,
25
- formattedDateProps,
26
- ...props
27
- },
28
- ref
29
- ) {
30
- const CallIcon =
31
- signal === 'hungUp' ? Phone : fromMe ? (signal === 'reject' ? PhoneIncoming : PhoneOutgoing) : PhoneMissed;
32
-
33
- return (
34
- <div
35
- ref={ref}
36
- {...props}
37
- className={cn(
38
- 'flex items-center justify-center gap-2 rounded-full w-fit bg-ring/20 mx-auto px-3 py-1.5',
39
- isLast ? 'my-2' : 'mt-2',
40
- props?.className
41
- )}
42
- >
43
- {iconElement || (
44
- <CallIcon
45
- {...iconProps}
46
- className={cn('size-4', signal === 'hungUp' ? 'text-green-500' : 'text-red-500', iconProps?.className)}
47
- />
48
- )}
49
- <span
50
- {...infoProps}
51
- className={cn('text-sm mb-px', infoProps?.className)}
52
- >{`${info}${duration ? ` - ${formatDuration(duration)}` : ''}`}</span>
53
- <FormattedDate distanceToNow {...formattedDateProps} />
54
- </div>
55
- );
56
- }
57
-
58
- const CallMessage = memo(forwardRef(CallMessageBase));
59
-
60
- CallMessage.displayName = 'CallMessage';
61
-
62
- export { CallMessage };