@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,152 @@
1
+ import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
2
+ import TextareaAutosize from 'react-textarea-autosize';
3
+ import { SendHorizontal } from 'lucide-react';
4
+ import { Label } from './label';
5
+ import { cn } from './utils';
6
+
7
+ function ChatInputSendBase({ onSend = () => {}, iconElement, iconProps, ...props }, ref) {
8
+ return (
9
+ <Label
10
+ ref={ref}
11
+ {...props}
12
+ onClick={onSend}
13
+ className={cn('group rounded-full py-2 pl-2.5 pr-1.5 cursor-pointer bg-ring/90 hover:bg-ring', props?.className)}
14
+ >
15
+ {iconElement || (
16
+ <SendHorizontal
17
+ {...iconProps}
18
+ className={cn(
19
+ 'text-background group-hover:scale-110 transition-all duration-200 ease-out',
20
+ iconProps?.className
21
+ )}
22
+ />
23
+ )}
24
+ </Label>
25
+ );
26
+ }
27
+
28
+ const ChatInputSend = forwardRef(ChatInputSendBase);
29
+
30
+ ChatInputSend.displayName = 'ChatInputSend';
31
+
32
+ function ChatInputBase(
33
+ {
34
+ pending = false,
35
+ draft,
36
+ onSend = () => {},
37
+ onDraft = () => {},
38
+ onTyping = () => {},
39
+ onHeightGrow = () => {},
40
+ hideChatInputSend = false,
41
+ chatInputSendProps,
42
+ containerProps,
43
+ children,
44
+ ...props
45
+ },
46
+ ref
47
+ ) {
48
+ const [value, setValue] = useState();
49
+ const textareaRef = useRef(null);
50
+ const textareaHeightRef = useRef(0);
51
+ const typingRef = useRef(false);
52
+ const typingTimeoutRef = useRef(undefined);
53
+ const handleStopTyping = () => {
54
+ typingRef.current = false;
55
+ onTyping(false);
56
+
57
+ if (typingTimeoutRef.current) {
58
+ clearTimeout(typingTimeoutRef.current);
59
+ typingTimeoutRef.current = undefined;
60
+ }
61
+ };
62
+ const handleOnChange = (event) => {
63
+ const { data } = event.nativeEvent;
64
+
65
+ setValue(event.target.value);
66
+
67
+ if (!typingRef.current && typeof data === 'string' && data.length > 0) {
68
+ typingRef.current = true;
69
+ onTyping(true);
70
+ }
71
+
72
+ clearTimeout(typingTimeoutRef.current);
73
+ typingTimeoutRef.current = setTimeout(handleStopTyping, 5000);
74
+ };
75
+ const handleOnSend = async () => {
76
+ handleStopTyping();
77
+
78
+ if (typeof value === 'string' && value.length > 0) {
79
+ onSend(value.trim());
80
+ setValue('');
81
+ textareaRef.current?.focus();
82
+ }
83
+ };
84
+ const handleOnHeightChange = (height, meta) => {
85
+ if (!height && !meta) return;
86
+
87
+ if (height !== textareaHeightRef.current) {
88
+ const shift = height > textareaHeightRef.current ? meta.rowHeight : -meta.rowHeight;
89
+
90
+ onHeightGrow({
91
+ height,
92
+ shift,
93
+ });
94
+ }
95
+
96
+ textareaHeightRef.current = height;
97
+ };
98
+ const handleOnKeyDown = (event) => {
99
+ if (event.key === 'Enter' && event.shiftKey === false) {
100
+ event.preventDefault();
101
+ props.onKeyDown?.(event);
102
+ handleOnSend();
103
+ }
104
+ };
105
+
106
+ useEffect(() => {
107
+ const textarea = textareaRef.current;
108
+
109
+ handleStopTyping();
110
+ queueMicrotask(() => setValue(draft));
111
+ textarea?.focus();
112
+
113
+ return () => {
114
+ onDraft(textarea?.value);
115
+ };
116
+ }, [props.key]);
117
+
118
+ useImperativeHandle(ref, () => textareaRef.current || {}, []);
119
+
120
+ return (
121
+ <div {...containerProps} className={cn('flex items-end gap-2', containerProps?.className)}>
122
+ <TextareaAutosize
123
+ name="textarea-autosize"
124
+ ref={textareaRef}
125
+ autoFocus
126
+ minRows={1}
127
+ maxRows={8}
128
+ {...props}
129
+ value={value}
130
+ onChange={handleOnChange}
131
+ onKeyDown={handleOnKeyDown}
132
+ onHeightChange={handleOnHeightChange}
133
+ className={cn(
134
+ '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',
135
+ 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring',
136
+ 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
137
+ 'resize-none py-2.5',
138
+ pending && 'pointer-events-none bg-muted-foreground border-ring ring-ring/50 animate-pulse',
139
+ props?.className
140
+ )}
141
+ />
142
+ {!hideChatInputSend && <ChatInputSend onSend={handleOnSend} {...chatInputSendProps} />}
143
+ {children}
144
+ </div>
145
+ );
146
+ }
147
+
148
+ const ChatInput = forwardRef(ChatInputBase);
149
+
150
+ ChatInput.displayName = 'ChatInput';
151
+
152
+ export { ChatInput };
@@ -0,0 +1,151 @@
1
+ import React, { useRef, useEffect, useState, useImperativeHandle, forwardRef, useCallback } from 'react';
2
+ import { Virtualizer } from 'virtua';
3
+ import { FilePickerDropzone } from './file-picker';
4
+ import { QuickActions } from './quick-actions';
5
+ import { Spinner } from './spinner';
6
+ import { cn } from './utils';
7
+
8
+ function DefaultChatListWrapper({ children, ...props }) {
9
+ return (
10
+ <div {...props} className={cn('relative size-full min-h-0', props?.className)}>
11
+ {children}
12
+ </div>
13
+ );
14
+ }
15
+
16
+ function ChatListBase(
17
+ {
18
+ key,
19
+ loading = false,
20
+ textareaMeasurement,
21
+ offsetToReach = 200,
22
+ onScrollStartReached,
23
+ onScrollEndReached,
24
+ onListReset,
25
+ onListCreate,
26
+ onListGrow,
27
+ enableFilePickerDropzone = true,
28
+ containerProps,
29
+ filePickerDropzoneProps,
30
+ quickActionsProps,
31
+ children,
32
+ quickActionsVisible,
33
+ minItemsCount = 1,
34
+ ...props
35
+ },
36
+ ref
37
+ ) {
38
+ const ChatListWrapper = enableFilePickerDropzone ? FilePickerDropzone : DefaultChatListWrapper;
39
+ const chatListWrapperProps = enableFilePickerDropzone ? filePickerDropzoneProps : containerProps;
40
+ const itemsCount = Array.isArray(children) ? children.length : Array.isArray(props.data) ? props.data.length : 0;
41
+ const itemsCountRef = useRef(0);
42
+ const keyRef = useRef(undefined);
43
+ const [shouldPrependMessages, setShouldPrependMessages] = useState(false);
44
+ const [isPreparing, setIsPreparing] = useState(false);
45
+ const virtuaRef = useRef(null);
46
+ const prepareNextVirtualizer = () => {
47
+ queueMicrotask(() => {
48
+ setIsPreparing(true);
49
+ queueMicrotask(() => setIsPreparing(false));
50
+ });
51
+ };
52
+ const handleOnScroll = async (offset) => {
53
+ props.onScroll?.(offset);
54
+
55
+ if (!virtuaRef.current) return;
56
+
57
+ if (typeof onScrollStartReached === 'function' && offset < offsetToReach) {
58
+ setShouldPrependMessages(true);
59
+ onScrollStartReached();
60
+ }
61
+
62
+ if (
63
+ typeof onScrollEndReached === 'function' &&
64
+ virtuaRef.current.viewportSize + offset + offsetToReach > virtuaRef.current.scrollSize
65
+ ) {
66
+ onScrollEndReached();
67
+ }
68
+ };
69
+ const scrollToBottom = useCallback(
70
+ (force = false) => {
71
+ if (!virtuaRef.current) return;
72
+
73
+ if (force || virtuaRef.current.scrollSize - virtuaRef.current.scrollOffset < virtuaRef.current.viewportSize * 2) {
74
+ queueMicrotask(() =>
75
+ virtuaRef.current?.scrollToIndex(itemsCount - 1, {
76
+ align: 'start',
77
+ })
78
+ );
79
+ }
80
+ },
81
+ [itemsCount]
82
+ );
83
+
84
+ useEffect(() => {
85
+ if (key !== keyRef.current) {
86
+ prepareNextVirtualizer();
87
+ onListReset?.(keyRef.current, key);
88
+ keyRef.current = key;
89
+ itemsCountRef.current = 0;
90
+ }
91
+ }, [key, onListReset]);
92
+
93
+ useEffect(() => {
94
+ if (itemsCountRef.current === 0 && itemsCount > 0) {
95
+ prepareNextVirtualizer();
96
+ onListCreate?.();
97
+ itemsCountRef.current = itemsCount;
98
+ scrollToBottom(true);
99
+ }
100
+
101
+ if (itemsCount > itemsCountRef.current) {
102
+ onListGrow?.();
103
+ itemsCountRef.current = itemsCount;
104
+
105
+ if (shouldPrependMessages) {
106
+ queueMicrotask(() => setShouldPrependMessages(false));
107
+ }
108
+
109
+ scrollToBottom();
110
+ }
111
+ }, [itemsCount, shouldPrependMessages, onListCreate, onListGrow, scrollToBottom]);
112
+
113
+ useEffect(() => {
114
+ virtuaRef.current?.scrollBy(textareaMeasurement.shift);
115
+ }, [textareaMeasurement]);
116
+
117
+ useImperativeHandle(
118
+ ref,
119
+ () => ({
120
+ ...(virtuaRef.current || {}),
121
+ scrollToBottom,
122
+ }),
123
+ [scrollToBottom]
124
+ );
125
+
126
+ if (isPreparing) {
127
+ return null;
128
+ }
129
+
130
+ if (minItemsCount >= itemsCount) {
131
+ return quickActionsVisible ? <QuickActions {...quickActionsProps} /> : <Spinner loading layout="centered" />;
132
+ }
133
+
134
+ return (
135
+ <ChatListWrapper {...chatListWrapperProps}>
136
+ <Spinner loading={loading} className="my-5" />
137
+ <div className="flex flex-col h-full overflow-y-auto px-2">
138
+ <div className="grow" />
139
+ <Virtualizer ref={virtuaRef} onScroll={handleOnScroll} shift={shouldPrependMessages} {...props}>
140
+ {children}
141
+ </Virtualizer>
142
+ </div>
143
+ </ChatListWrapper>
144
+ );
145
+ }
146
+
147
+ const ChatList = forwardRef(ChatListBase);
148
+
149
+ ChatList.displayName = 'ChatList';
150
+
151
+ export { ChatList };
@@ -0,0 +1,30 @@
1
+ import { forwardRef } from 'react';
2
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
3
+ import { CheckIcon } from 'lucide-react';
4
+ import { cn } from './utils';
5
+
6
+ function CheckboxBase({ iconElement, iconProps, indicatorProps, ...props }, ref) {
7
+ return (
8
+ <CheckboxPrimitive.Root
9
+ ref={ref}
10
+ {...props}
11
+ className={cn(
12
+ 'peer border-input dark:bg-input/30 data-[state=checked]:bg-ring data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-ring data-[state=checked]:border-ring focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50',
13
+ props?.className
14
+ )}
15
+ >
16
+ <CheckboxPrimitive.Indicator
17
+ {...indicatorProps}
18
+ className={cn('flex items-center justify-center text-current transition-none', indicatorProps?.className)}
19
+ >
20
+ {iconElement || <CheckIcon {...iconProps} className={cn('size-3.5', iconProps?.className)} />}
21
+ </CheckboxPrimitive.Indicator>
22
+ </CheckboxPrimitive.Root>
23
+ );
24
+ }
25
+
26
+ const Checkbox = forwardRef(CheckboxBase);
27
+
28
+ Checkbox.displayName = 'Checkbox';
29
+
30
+ export { Checkbox };
@@ -60,7 +60,7 @@ function DialogItemBase(
60
60
  onClick={onSelect}
61
61
  className={cn(
62
62
  'flex items-start gap-2 px-2 flex-1 cursor-pointer',
63
- 'transition-colors duration-200 ease-linear',
63
+ 'transition-colors duration-200 ease-out',
64
64
  `${selected ? 'border-l-[0.25em] pl-1 border-l-ring bg-ring/20' : 'hover:bg-ring/5'}`,
65
65
  props?.className
66
66
  )}
@@ -0,0 +1,73 @@
1
+ import { forwardRef, useImperativeHandle, useRef } from 'react';
2
+ import { VList } from 'virtua';
3
+ import { PlaceholderText } from './placeholder-text';
4
+
5
+ const PendingItem = () => (
6
+ <div className="flex flex-row gap-2 mx-2 border-b">
7
+ <div className="size-13 my-2 rounded-full bg-muted animate-pulse" />
8
+ <div className="flex-1 text-muted">
9
+ <div className="flex flex-row items-center justify-between h-6">
10
+ <div className="w-2/3 h-4 rounded-full bg-muted animate-pulse" />
11
+ <div className="w-1/6 h-3.5 rounded-full bg-muted animate-pulse" />
12
+ </div>
13
+ <div className="flex flex-col items-start justify-around h-10">
14
+ <div className="w-7/8 h-3.5 rounded-full bg-muted animate-pulse" />
15
+ <div className="w-5/6 h-3.5 rounded-full bg-muted animate-pulse" />
16
+ </div>
17
+ </div>
18
+ </div>
19
+ );
20
+
21
+ function DialogsListBase(
22
+ {
23
+ children,
24
+ pending,
25
+ pendingListLength = 5,
26
+ offsetToReach = 50,
27
+ placeholderVisible,
28
+ placeholderTitles = ['No dialogs yet.', 'Start a conversation!'],
29
+ onScrollStartReached,
30
+ onScrollEndReached,
31
+ ...props
32
+ },
33
+ ref
34
+ ) {
35
+ const vListRef = useRef(null);
36
+ const skeletonList = Array.from({
37
+ length: pendingListLength,
38
+ }).map((_, i) => <PendingItem key={`pending_dialog_item_${i}`} />);
39
+ const handleOnScroll = async (offset) => {
40
+ props.onScroll?.(offset);
41
+
42
+ if (!vListRef.current) return;
43
+
44
+ if (typeof onScrollStartReached === 'function' && offset < offsetToReach) {
45
+ onScrollStartReached();
46
+ }
47
+
48
+ if (
49
+ typeof onScrollEndReached === 'function' &&
50
+ vListRef.current.viewportSize + offset + offsetToReach > vListRef.current.scrollSize
51
+ ) {
52
+ onScrollEndReached();
53
+ }
54
+ };
55
+
56
+ useImperativeHandle(ref, () => vListRef.current || {}, []);
57
+
58
+ if (placeholderVisible) {
59
+ return <PlaceholderText titles={placeholderTitles} className="text-base text-muted" />;
60
+ }
61
+
62
+ return (
63
+ <VList ref={vListRef} onScroll={handleOnScroll} className="size-full overflow-y-scroll" {...props}>
64
+ {pending ? skeletonList : children}
65
+ </VList>
66
+ );
67
+ }
68
+
69
+ const DialogsList = forwardRef(DialogsListBase);
70
+
71
+ DialogsList.displayName = 'DialogsList';
72
+
73
+ export { DialogsList };
@@ -7,7 +7,7 @@ function DismissLayerBase(
7
7
  ) {
8
8
  const innerRef = useRef(null);
9
9
 
10
- useImperativeHandle(ref, () => innerRef.current, []);
10
+ useImperativeHandle(ref, () => innerRef.current || {}, []);
11
11
 
12
12
  const handleClickOrTouch = useCallback(
13
13
  (e) => {
@@ -73,7 +73,7 @@ function FilePickerInputBase(
73
73
  {...labelProps}
74
74
  htmlFor="file-uploader"
75
75
  className={cn(
76
- 'group p-2 rounded-full hover:bg-ring/10 transition-all duration-200 ease-in-out cursor-pointer',
76
+ 'group p-2 rounded-full hover:bg-ring/10 transition-all duration-200 ease-out cursor-pointer',
77
77
  labelProps?.className
78
78
  )}
79
79
  >
@@ -81,7 +81,7 @@ function FilePickerInputBase(
81
81
  <Paperclip
82
82
  {...iconProps}
83
83
  className={cn(
84
- 'text-foreground group-hover:scale-110 transition-all duration-200 ease-in-out',
84
+ 'text-foreground group-hover:scale-110 transition-all duration-200 ease-out',
85
85
  iconProps?.className
86
86
  )}
87
87
  />
@@ -12,7 +12,11 @@ function PlaceholderTextBase({ title, titles = [], rowProps, ...props }, ref) {
12
12
  className={cn('absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2', props?.className)}
13
13
  >
14
14
  {rows.map((row, index) => (
15
- <div key={`placeholder-text-${index}`} {...rowProps} className={cn('text-center', rowProps?.className)}>
15
+ <div
16
+ key={`placeholder-text-${index}`}
17
+ {...rowProps}
18
+ className={cn('text-center text-muted-foreground', rowProps?.className)}
19
+ >
16
20
  {row}
17
21
  </div>
18
22
  ))}
@@ -0,0 +1,62 @@
1
+ import { forwardRef } from 'react';
2
+ import { cn } from './utils';
3
+
4
+ function QuickActionsBase(
5
+ {
6
+ title,
7
+ description,
8
+ actions = [],
9
+ onAction = () => {},
10
+ containerProps,
11
+ titleProps,
12
+ descriptionProps,
13
+ actionProps,
14
+ ...props
15
+ },
16
+ ref
17
+ ) {
18
+ if (!actions.length) {
19
+ return null;
20
+ }
21
+
22
+ return (
23
+ <div
24
+ ref={ref}
25
+ {...containerProps}
26
+ className={cn('flex flex-col h-full overflow-y-auto py-2', containerProps?.className)}
27
+ >
28
+ <div className="grow" />
29
+ <div {...props} className={cn('grid gap-2 m-4', props?.className)}>
30
+ {title && (
31
+ <h2 {...titleProps} className={cn('font-medium text-foreground text-lg', titleProps?.className)}>
32
+ {title}
33
+ </h2>
34
+ )}
35
+ {description && (
36
+ <span {...descriptionProps} className={cn('text-sm text-muted-foreground mb-4', descriptionProps?.className)}>
37
+ {description}
38
+ </span>
39
+ )}
40
+ {actions.map((action, index) => (
41
+ <div
42
+ key={index}
43
+ {...actionProps}
44
+ className={cn(
45
+ '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',
46
+ actionProps?.className
47
+ )}
48
+ onClick={() => onAction(action)}
49
+ >
50
+ {action}
51
+ </div>
52
+ ))}
53
+ </div>
54
+ </div>
55
+ );
56
+ }
57
+
58
+ const QuickActions = forwardRef(QuickActionsBase);
59
+
60
+ QuickActions.displayName = 'QuickActions';
61
+
62
+ export { QuickActions };
@@ -58,7 +58,7 @@ function SearchBase(
58
58
  onClick={handleOnCancel}
59
59
  {...cancelIconProps}
60
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',
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-out',
62
62
  value.length > 0 ? 'opacity-100 scale-100' : 'opacity-0 scale-75 pointer-events-none',
63
63
  cancelIconProps?.className
64
64
  )}
@@ -0,0 +1,18 @@
1
+ import { Phone, PhoneIncoming, PhoneMissed, PhoneOutgoing } from 'lucide-react';
2
+ import { cn } from './utils';
3
+
4
+ const StatusCall = ({ fromMe, status, ...props }) => {
5
+ const CallIcon =
6
+ status === 'hungUp' ? Phone : fromMe ? PhoneOutgoing : status === 'reject' ? PhoneIncoming : PhoneMissed;
7
+
8
+ return (
9
+ <CallIcon
10
+ {...props}
11
+ className={cn('size-4', status === 'hungUp' ? 'text-green-500' : 'text-red-500', props?.className)}
12
+ />
13
+ );
14
+ };
15
+
16
+ StatusCall.displayName = 'StatusCall';
17
+
18
+ export { StatusCall };
@@ -9,7 +9,7 @@ function StreamViewBase({ id, stream, mirror, className, muted, ...props }, ref)
9
9
  const defaultClassName = 'size-full object-contain';
10
10
  const mirrorClassName = mirror ? 'scale-x-[-1]' : '';
11
11
 
12
- useImperativeHandle(ref, () => innerRef.current);
12
+ useImperativeHandle(ref, () => innerRef.current || {}, []);
13
13
 
14
14
  useEffect(() => {
15
15
  if (innerRef.current && stream) {
@@ -116,13 +116,13 @@ function FullscreenStreamViewBase(
116
116
 
117
117
  useImperativeHandle(
118
118
  ref,
119
- () =>
120
- Object.assign(innerRef.current, {
121
- isFullscreen,
122
- isPictureInPicture,
123
- toggleFullscreen,
124
- togglePictureInPicture,
125
- }),
119
+ () => ({
120
+ ...(innerRef.current || {}),
121
+ isFullscreen,
122
+ isPictureInPicture,
123
+ toggleFullscreen,
124
+ togglePictureInPicture,
125
+ }),
126
126
  [isFullscreen, isPictureInPicture, toggleFullscreen, togglePictureInPicture]
127
127
  );
128
128
 
package/gen/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ export { AlertDialog } from './components/alert-dialog';
2
+
1
3
  export {
2
4
  Attachment,
3
5
  AttachmentLink,
@@ -14,12 +16,18 @@ export { Badge } from './components/badge';
14
16
 
15
17
  export { Button } from './components/button';
16
18
 
17
- export { CallMessage } from './components/call-message';
19
+ export { ChatBubbleMessage, ChatBubbleInfo } from './components/chat-bubble';
20
+
21
+ export { ChatInput } from './components/chat-input';
22
+
23
+ export { ChatList } from './components/chat-list';
18
24
 
19
- export { ChatMessage } from './components/chat-message';
25
+ export { Checkbox } from './components/checkbox';
20
26
 
21
27
  export { DialogItem } from './components/dialog-item';
22
28
 
29
+ export { DialogsList } from './components/dialogs-list';
30
+
23
31
  export { DismissLayer } from './components/dismiss-layer';
24
32
 
25
33
  export { FilePickerInput, FilePickerDropzone } from './components/file-picker';
@@ -38,10 +46,14 @@ export { PlaceholderText } from './components/placeholder-text';
38
46
 
39
47
  export { Presence, PresenceBadge } from './components/presence';
40
48
 
49
+ export { QuickActions } from './components/quick-actions';
50
+
41
51
  export { Search } from './components/search';
42
52
 
43
53
  export { Spinner } from './components/spinner';
44
54
 
55
+ export { StatusCall } from './components/status-call';
56
+
45
57
  export { StatusIndicator } from './components/status-indicator';
46
58
 
47
59
  export { StatusSent } from './components/status-sent';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@connectycube/react-ui-kit",
3
- "version": "0.0.20",
3
+ "version": "0.0.22",
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": {
@@ -64,6 +64,7 @@
64
64
  "dependencies": {
65
65
  "@radix-ui/react-alert-dialog": "^1.1.15",
66
66
  "@radix-ui/react-avatar": "^1.1.11",
67
+ "@radix-ui/react-checkbox": "^1.3.3",
67
68
  "@radix-ui/react-label": "^2.1.8",
68
69
  "@radix-ui/react-slot": "^1.2.4",
69
70
  "@radix-ui/react-switch": "^1.2.6",
@@ -72,9 +73,11 @@
72
73
  "clsx": "^2.1.1",
73
74
  "date-fns": "^4.1.0",
74
75
  "linkify-react": "^4.3.2",
75
- "lucide-react": "^0.561.0",
76
+ "lucide-react": "^0.562.0",
76
77
  "react-intersection-observer": "^10.0.0",
77
- "tailwind-merge": "^3.4.0"
78
+ "react-textarea-autosize": "^8.5.9",
79
+ "tailwind-merge": "^3.4.0",
80
+ "virtua": "^0.48.2"
78
81
  },
79
82
  "peerDependencies": {
80
83
  "react": ">=18",
@@ -89,22 +92,22 @@
89
92
  "@rollup/plugin-terser": "^0.4.4",
90
93
  "@rollup/plugin-typescript": "^12.3.0",
91
94
  "@stylistic/eslint-plugin": "^5.6.1",
92
- "@types/node": "^25.0.1",
95
+ "@types/node": "^25.0.3",
93
96
  "@types/react": "^19.2.7",
94
97
  "eslint": "^9.39.2",
95
98
  "eslint-plugin-react-hooks": "^7.0.1",
96
- "eslint-plugin-react-refresh": "^0.4.24",
99
+ "eslint-plugin-react-refresh": "^0.4.26",
97
100
  "execa": "^9.6.1",
98
101
  "fast-glob": "^3.3.3",
99
- "fs-extra": "^11.3.2",
102
+ "fs-extra": "^11.3.3",
100
103
  "globals": "^16.5.0",
101
104
  "prettier": "^3.7.4",
102
105
  "prompts": "^2.4.2",
103
- "rollup": "^4.53.3",
106
+ "rollup": "^4.53.5",
104
107
  "rollup-plugin-peer-deps-external": "^2.2.4",
105
108
  "tslib": "^2.8.1",
106
109
  "typescript": "^5.9.3",
107
- "typescript-eslint": "^8.49.0"
110
+ "typescript-eslint": "^8.50.0"
108
111
  },
109
112
  "engines": {
110
113
  "node": ">=18"