@kushagradhawan/kookie-ui 0.1.50 → 0.1.52
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/components.css +582 -116
- package/dist/cjs/components/_internal/shell-bottom.d.ts +31 -5
- package/dist/cjs/components/_internal/shell-bottom.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-bottom.js +1 -1
- package/dist/cjs/components/_internal/shell-bottom.js.map +3 -3
- package/dist/cjs/components/_internal/shell-handles.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-handles.js +1 -1
- package/dist/cjs/components/_internal/shell-handles.js.map +3 -3
- package/dist/cjs/components/_internal/shell-inspector.d.ts +23 -5
- package/dist/cjs/components/_internal/shell-inspector.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-inspector.js +1 -1
- package/dist/cjs/components/_internal/shell-inspector.js.map +3 -3
- package/dist/cjs/components/_internal/shell-sidebar.d.ts +24 -6
- package/dist/cjs/components/_internal/shell-sidebar.d.ts.map +1 -1
- package/dist/cjs/components/_internal/shell-sidebar.js +1 -1
- package/dist/cjs/components/_internal/shell-sidebar.js.map +3 -3
- package/dist/cjs/components/chatbar.d.ts +21 -2
- package/dist/cjs/components/chatbar.d.ts.map +1 -1
- package/dist/cjs/components/chatbar.js +1 -1
- package/dist/cjs/components/chatbar.js.map +3 -3
- package/dist/cjs/components/shell.context.d.ts +88 -1
- package/dist/cjs/components/shell.context.d.ts.map +1 -1
- package/dist/cjs/components/shell.context.js +1 -1
- package/dist/cjs/components/shell.context.js.map +3 -3
- package/dist/cjs/components/shell.d.ts +51 -13
- package/dist/cjs/components/shell.d.ts.map +1 -1
- package/dist/cjs/components/shell.hooks.d.ts +7 -1
- package/dist/cjs/components/shell.hooks.d.ts.map +1 -1
- package/dist/cjs/components/shell.hooks.js +1 -1
- package/dist/cjs/components/shell.hooks.js.map +3 -3
- package/dist/cjs/components/shell.js +1 -1
- package/dist/cjs/components/shell.js.map +3 -3
- package/dist/cjs/components/shell.types.d.ts +1 -0
- package/dist/cjs/components/shell.types.d.ts.map +1 -1
- package/dist/cjs/components/shell.types.js +1 -1
- package/dist/cjs/components/shell.types.js.map +2 -2
- package/dist/esm/components/_internal/shell-bottom.d.ts +31 -5
- package/dist/esm/components/_internal/shell-bottom.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-bottom.js +1 -1
- package/dist/esm/components/_internal/shell-bottom.js.map +3 -3
- package/dist/esm/components/_internal/shell-handles.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-handles.js +1 -1
- package/dist/esm/components/_internal/shell-handles.js.map +3 -3
- package/dist/esm/components/_internal/shell-inspector.d.ts +23 -5
- package/dist/esm/components/_internal/shell-inspector.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-inspector.js +1 -1
- package/dist/esm/components/_internal/shell-inspector.js.map +3 -3
- package/dist/esm/components/_internal/shell-sidebar.d.ts +24 -6
- package/dist/esm/components/_internal/shell-sidebar.d.ts.map +1 -1
- package/dist/esm/components/_internal/shell-sidebar.js +1 -1
- package/dist/esm/components/_internal/shell-sidebar.js.map +3 -3
- package/dist/esm/components/chatbar.d.ts +21 -2
- package/dist/esm/components/chatbar.d.ts.map +1 -1
- package/dist/esm/components/chatbar.js +1 -1
- package/dist/esm/components/chatbar.js.map +3 -3
- package/dist/esm/components/shell.context.d.ts +88 -1
- package/dist/esm/components/shell.context.d.ts.map +1 -1
- package/dist/esm/components/shell.context.js +1 -1
- package/dist/esm/components/shell.context.js.map +3 -3
- package/dist/esm/components/shell.d.ts +51 -13
- package/dist/esm/components/shell.d.ts.map +1 -1
- package/dist/esm/components/shell.hooks.d.ts +7 -1
- package/dist/esm/components/shell.hooks.d.ts.map +1 -1
- package/dist/esm/components/shell.hooks.js +1 -1
- package/dist/esm/components/shell.hooks.js.map +3 -3
- package/dist/esm/components/shell.js +1 -1
- package/dist/esm/components/shell.js.map +3 -3
- package/dist/esm/components/shell.types.d.ts +1 -0
- package/dist/esm/components/shell.types.d.ts.map +1 -1
- package/dist/esm/components/shell.types.js.map +2 -2
- package/package.json +14 -3
- package/schemas/base-button.json +1 -1
- package/schemas/button.json +1 -1
- package/schemas/icon-button.json +1 -1
- package/schemas/index.json +6 -6
- package/schemas/toggle-button.json +1 -1
- package/schemas/toggle-icon-button.json +1 -1
- package/src/components/_internal/base-menu.css +16 -16
- package/src/components/_internal/base-sidebar-menu.css +23 -20
- package/src/components/_internal/base-sidebar.css +13 -0
- package/src/components/_internal/shell-bottom.tsx +176 -49
- package/src/components/_internal/shell-handles.tsx +29 -4
- package/src/components/_internal/shell-inspector.tsx +175 -43
- package/src/components/_internal/shell-sidebar.tsx +177 -93
- package/src/components/chatbar.css +240 -21
- package/src/components/chatbar.tsx +280 -291
- package/src/components/sheet.css +8 -16
- package/src/components/shell.context.tsx +79 -3
- package/src/components/shell.css +0 -1
- package/src/components/shell.hooks.ts +35 -0
- package/src/components/shell.tsx +574 -235
- package/src/components/shell.types.ts +2 -0
- package/src/components/sidebar.css +2 -2
- package/styles.css +582 -116
|
@@ -7,14 +7,15 @@ import { Flex } from './flex.js';
|
|
|
7
7
|
|
|
8
8
|
import { ScrollArea } from './scroll-area.js';
|
|
9
9
|
import { Slot } from './slot.js';
|
|
10
|
+
import { Box } from './box.js';
|
|
10
11
|
import { Card } from './card.js';
|
|
11
12
|
import { Text } from './text.js';
|
|
13
|
+
import { Inset } from './inset.js';
|
|
12
14
|
import { useDropzone } from 'react-dropzone';
|
|
13
15
|
import type { ComponentPropsWithout, RemovedProps } from '../helpers/component-props.js';
|
|
14
16
|
|
|
15
17
|
// Avoid SSR warnings by using an isomorphic layout effect
|
|
16
|
-
const useIsomorphicLayoutEffect =
|
|
17
|
-
typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
|
|
18
|
+
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
|
|
18
19
|
|
|
19
20
|
type ExpandOn = 'none' | 'focus' | 'overflow' | 'both';
|
|
20
21
|
type SendMode = 'always' | 'whenDirty' | 'never';
|
|
@@ -86,6 +87,9 @@ interface ChatbarContextValue {
|
|
|
86
87
|
// Helpers
|
|
87
88
|
appendFiles(files: File[]): void;
|
|
88
89
|
appendFilesFromPaste(files: File[]): void;
|
|
90
|
+
|
|
91
|
+
// Guards
|
|
92
|
+
fileDialogOpenRef: React.MutableRefObject<boolean>;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
const ChatbarContext = React.createContext<ChatbarContextValue | null>(null);
|
|
@@ -136,6 +140,13 @@ interface ChatbarRootBaseProps {
|
|
|
136
140
|
|
|
137
141
|
size?: '1' | '2' | '3';
|
|
138
142
|
variant?: 'surface' | 'outline' | 'classic' | 'ghost' | 'soft';
|
|
143
|
+
/** Accent color for the control (matches TextArea) */
|
|
144
|
+
color?: string;
|
|
145
|
+
/** Optional radius override (matches TextArea) */
|
|
146
|
+
radius?: string | number;
|
|
147
|
+
/** Panel/material translucency flags (matches TextArea) */
|
|
148
|
+
panelBackground?: 'solid' | 'translucent';
|
|
149
|
+
material?: 'solid' | 'translucent';
|
|
139
150
|
|
|
140
151
|
width?: React.CSSProperties['width'];
|
|
141
152
|
maxWidth?: React.CSSProperties['maxWidth'];
|
|
@@ -166,9 +177,22 @@ interface ChatbarRootBaseProps {
|
|
|
166
177
|
* @default true
|
|
167
178
|
*/
|
|
168
179
|
dropzone?: boolean;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Optional API ref to control Chatbar imperatively without relying on DOM refs.
|
|
183
|
+
* Provides methods to focus the textarea and open the file picker.
|
|
184
|
+
*/
|
|
185
|
+
apiRef?: React.Ref<ChatbarApi>;
|
|
169
186
|
}
|
|
170
187
|
|
|
171
188
|
type RootElement = React.ElementRef<'div'>;
|
|
189
|
+
/** Imperative API for Chatbar.Root */
|
|
190
|
+
export interface ChatbarApi {
|
|
191
|
+
/** Focus the textarea input */
|
|
192
|
+
focusTextarea: () => void;
|
|
193
|
+
/** Open the file picker dialog (respects accept/multiple) */
|
|
194
|
+
openFilePicker: () => void;
|
|
195
|
+
}
|
|
172
196
|
/**
|
|
173
197
|
* Chatbar container and state provider.
|
|
174
198
|
*
|
|
@@ -199,9 +223,7 @@ type RootElement = React.ElementRef<'div'>;
|
|
|
199
223
|
* - `aria-expanded` on the root reflects the disclosure state of the input area.
|
|
200
224
|
* - Dropzone provides proper ARIA attributes for drag and drop operations.
|
|
201
225
|
*/
|
|
202
|
-
interface RootProps
|
|
203
|
-
extends ComponentPropsWithout<'div', RemovedProps | 'onSubmit'>,
|
|
204
|
-
ChatbarRootBaseProps {}
|
|
226
|
+
interface RootProps extends ComponentPropsWithout<'div', RemovedProps | 'onSubmit'>, ChatbarRootBaseProps {}
|
|
205
227
|
|
|
206
228
|
const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
207
229
|
const {
|
|
@@ -223,6 +245,10 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
223
245
|
onSubmit,
|
|
224
246
|
size = '2',
|
|
225
247
|
variant,
|
|
248
|
+
color,
|
|
249
|
+
radius,
|
|
250
|
+
panelBackground,
|
|
251
|
+
material,
|
|
226
252
|
width,
|
|
227
253
|
maxWidth,
|
|
228
254
|
asChild,
|
|
@@ -238,8 +264,10 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
238
264
|
clearOnSubmit = true,
|
|
239
265
|
onAttachmentReject,
|
|
240
266
|
dropzone = true,
|
|
267
|
+
apiRef,
|
|
241
268
|
...divProps
|
|
242
269
|
} = props;
|
|
270
|
+
const effectiveMaterial = material || panelBackground;
|
|
243
271
|
|
|
244
272
|
const isValueControlled = valueProp != null;
|
|
245
273
|
const [valueUncontrolled, setValueUncontrolled] = React.useState<string>(defaultValue);
|
|
@@ -251,23 +279,20 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
251
279
|
|
|
252
280
|
const rootRef = React.useRef<HTMLDivElement>(null);
|
|
253
281
|
const textareaRef = React.useRef<HTMLTextAreaElement>(null);
|
|
282
|
+
const fileDialogOpenRef = React.useRef<boolean>(false);
|
|
254
283
|
|
|
255
284
|
// Attachments state
|
|
256
285
|
// Treat `attachments` as controlled if the prop is provided, even if its value is `undefined`.
|
|
257
286
|
// This avoids switching between controlled and uncontrolled when a consumer sets
|
|
258
287
|
// `attachments={undefined}` to clear attachments. In that case we normalize to an empty array.
|
|
259
288
|
const isAttachmentsControlled = 'attachments' in props;
|
|
260
|
-
const [attachmentsUncontrolled, setAttachmentsUncontrolled] =
|
|
261
|
-
|
|
262
|
-
const attachments = isAttachmentsControlled
|
|
263
|
-
? ((attachmentsProp ?? []) as ChatbarAttachment[])
|
|
264
|
-
: attachmentsUncontrolled;
|
|
289
|
+
const [attachmentsUncontrolled, setAttachmentsUncontrolled] = React.useState<ChatbarAttachment[]>(defaultAttachments);
|
|
290
|
+
const attachments = isAttachmentsControlled ? ((attachmentsProp ?? []) as ChatbarAttachment[]) : attachmentsUncontrolled;
|
|
265
291
|
|
|
266
292
|
// Track generated object URLs for cleanup
|
|
267
293
|
const generatedUrlSetRef = React.useRef<Set<string>>(new Set());
|
|
268
294
|
|
|
269
|
-
const toArray = (val: string | string[] | undefined) =>
|
|
270
|
-
Array.isArray(val) ? val : typeof val === 'string' ? val.split(',').map((s) => s.trim()) : [];
|
|
295
|
+
const toArray = (val: string | string[] | undefined) => (Array.isArray(val) ? val : typeof val === 'string' ? val.split(',').map((s) => s.trim()) : []);
|
|
271
296
|
|
|
272
297
|
const accepts = toArray(accept);
|
|
273
298
|
const pasteAccepts = toArray(pasteAccept).length > 0 ? toArray(pasteAccept) : accepts;
|
|
@@ -282,8 +307,7 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
282
307
|
// MIME pattern
|
|
283
308
|
const [type, subtype] = pat.split('/');
|
|
284
309
|
const [fmType, fmSubtype] = mime.split('/');
|
|
285
|
-
if (type === '*' || (type === fmType && (subtype === '*' || subtype === fmSubtype)))
|
|
286
|
-
return true;
|
|
310
|
+
if (type === '*' || (type === fmType && (subtype === '*' || subtype === fmSubtype))) return true;
|
|
287
311
|
} else if (pat.startsWith('.')) {
|
|
288
312
|
if (name.endsWith(pat)) return true;
|
|
289
313
|
}
|
|
@@ -303,10 +327,7 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
303
327
|
const next: ChatbarAttachment[] = [];
|
|
304
328
|
const rejected: { file: File; reason: 'type' | 'size' | 'count' }[] = [];
|
|
305
329
|
|
|
306
|
-
const remainingSlots =
|
|
307
|
-
typeof maxAttachments === 'number'
|
|
308
|
-
? Math.max(maxAttachments - attachments.length, 0)
|
|
309
|
-
: Infinity;
|
|
330
|
+
const remainingSlots = typeof maxAttachments === 'number' ? Math.max(maxAttachments - attachments.length, 0) : Infinity;
|
|
310
331
|
|
|
311
332
|
for (const file of files) {
|
|
312
333
|
if (next.length >= remainingSlots) {
|
|
@@ -345,6 +366,9 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
345
366
|
const merged = attachments.concat(accepted);
|
|
346
367
|
if (!isAttachmentsControlled) setAttachmentsUncontrolled(merged);
|
|
347
368
|
onAttachmentsChange?.(merged);
|
|
369
|
+
// Ensure chatbar expands when attachments are added
|
|
370
|
+
if (!isOpenControlled) setOpenUncontrolled(true);
|
|
371
|
+
onOpenChangeProp?.(true);
|
|
348
372
|
}
|
|
349
373
|
if (rejected.length > 0) onAttachmentReject?.(rejected);
|
|
350
374
|
};
|
|
@@ -359,10 +383,7 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
359
383
|
const rejected: { file: File; reason: 'type' | 'size' | 'count' }[] = [];
|
|
360
384
|
|
|
361
385
|
// Enforce maxAttachments and maxFileSize
|
|
362
|
-
const remainingSlots =
|
|
363
|
-
typeof maxAttachments === 'number'
|
|
364
|
-
? Math.max(maxAttachments - attachments.length, 0)
|
|
365
|
-
: Infinity;
|
|
386
|
+
const remainingSlots = typeof maxAttachments === 'number' ? Math.max(maxAttachments - attachments.length, 0) : Infinity;
|
|
366
387
|
for (const file of files) {
|
|
367
388
|
if (acceptedFiles.length >= remainingSlots) {
|
|
368
389
|
rejected.push({ file, reason: 'count' });
|
|
@@ -412,29 +433,32 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
412
433
|
if (!rootEl) return;
|
|
413
434
|
// If focus remains within root, ignore
|
|
414
435
|
if (nextTarget && rootEl.contains(nextTarget)) return;
|
|
436
|
+
// If native file dialog is open, avoid collapsing on blur
|
|
437
|
+
if (fileDialogOpenRef.current) return;
|
|
415
438
|
// Collapse when leaving the root if the value is empty
|
|
416
|
-
|
|
439
|
+
// Only collapse when both message and attachments are empty
|
|
440
|
+
if ((value?.trim?.() ?? '').length === 0 && attachments.length === 0) {
|
|
417
441
|
if (!isOpenControlled) setOpenUncontrolled(false);
|
|
418
442
|
onOpenChangeProp?.(false);
|
|
419
443
|
}
|
|
420
444
|
},
|
|
421
|
-
[isOpenControlled, onOpenChangeProp, value],
|
|
445
|
+
[isOpenControlled, onOpenChangeProp, value, attachments],
|
|
422
446
|
);
|
|
423
447
|
|
|
424
448
|
// Dropzone functionality
|
|
425
|
-
const {
|
|
449
|
+
const {
|
|
450
|
+
getRootProps,
|
|
451
|
+
getInputProps,
|
|
452
|
+
isDragActive,
|
|
453
|
+
open: openFileDialog,
|
|
454
|
+
} = useDropzone({
|
|
426
455
|
onDrop: (acceptedFiles, rejectedFiles) => {
|
|
427
456
|
if (acceptedFiles.length > 0) {
|
|
428
457
|
appendFiles(acceptedFiles);
|
|
429
458
|
}
|
|
430
459
|
if (rejectedFiles.length > 0 && onAttachmentReject) {
|
|
431
460
|
const rejections = rejectedFiles.map(({ file, errors }) => {
|
|
432
|
-
const reason =
|
|
433
|
-
errors[0]?.code === 'file-too-large'
|
|
434
|
-
? 'size'
|
|
435
|
-
: errors[0]?.code === 'file-invalid-type'
|
|
436
|
-
? 'type'
|
|
437
|
-
: 'count';
|
|
461
|
+
const reason = errors[0]?.code === 'file-too-large' ? 'size' : errors[0]?.code === 'file-invalid-type' ? 'type' : 'count';
|
|
438
462
|
return { file, reason: reason as 'type' | 'size' | 'count' };
|
|
439
463
|
});
|
|
440
464
|
onAttachmentReject(rejections);
|
|
@@ -463,6 +487,41 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
463
487
|
disabled: !dropzone || disabled,
|
|
464
488
|
});
|
|
465
489
|
|
|
490
|
+
// Expose imperative API via apiRef (non-breaking; forwardedRef still receives the DOM node)
|
|
491
|
+
React.useImperativeHandle(
|
|
492
|
+
apiRef,
|
|
493
|
+
() => ({
|
|
494
|
+
focusTextarea: () => textareaRef.current?.focus({ preventScroll: true }),
|
|
495
|
+
openFilePicker: () => {
|
|
496
|
+
// Guard against blur-collapse while native dialog is open
|
|
497
|
+
fileDialogOpenRef.current = true;
|
|
498
|
+
openFileDialog();
|
|
499
|
+
},
|
|
500
|
+
}),
|
|
501
|
+
[openFileDialog],
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
// Click-to-focus: focus textarea when clicking non-interactive areas inside the container
|
|
505
|
+
const isInteractiveTarget = React.useCallback((target: EventTarget | null) => {
|
|
506
|
+
if (!(target instanceof Element)) return false;
|
|
507
|
+
const el = target as Element;
|
|
508
|
+
if (el.closest('.rt-ChatbarDropOverlay')) return true;
|
|
509
|
+
return !!el.closest('button, [role="button"], a[href], input, textarea, select, [contenteditable], [tabindex]:not([tabindex="-1"])');
|
|
510
|
+
}, []);
|
|
511
|
+
|
|
512
|
+
const handleContainerPointerDown = React.useCallback(
|
|
513
|
+
(event: React.PointerEvent) => {
|
|
514
|
+
if (disabled) return;
|
|
515
|
+
if (isInteractiveTarget(event.target)) return;
|
|
516
|
+
if (event.pointerType === 'mouse' && event.button !== 0) return;
|
|
517
|
+
event.preventDefault();
|
|
518
|
+
textareaRef.current?.focus({ preventScroll: true });
|
|
519
|
+
},
|
|
520
|
+
[disabled, isInteractiveTarget, textareaRef],
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
// Clicking the label-wrapped Card will naturally focus the nested textarea.
|
|
524
|
+
|
|
466
525
|
return (
|
|
467
526
|
<ChatbarContext.Provider
|
|
468
527
|
value={{
|
|
@@ -505,14 +564,14 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
505
564
|
dropzone,
|
|
506
565
|
appendFiles,
|
|
507
566
|
appendFilesFromPaste,
|
|
567
|
+
fileDialogOpenRef,
|
|
508
568
|
}}
|
|
509
569
|
>
|
|
510
570
|
<Comp
|
|
511
571
|
{...divProps}
|
|
512
572
|
ref={(node: HTMLDivElement) => {
|
|
513
573
|
if (typeof forwardedRef === 'function') forwardedRef(node);
|
|
514
|
-
else if (forwardedRef)
|
|
515
|
-
(forwardedRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
574
|
+
else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
516
575
|
(rootRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
517
576
|
}}
|
|
518
577
|
className={classNames('rt-ChatbarRoot', `rt-r-size-${size}`, className)}
|
|
@@ -521,16 +580,22 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
521
580
|
data-disabled={disabled ? '' : undefined}
|
|
522
581
|
data-readonly={readOnly ? '' : undefined}
|
|
523
582
|
data-drop-active={dropzone && isDragActive ? '' : undefined}
|
|
583
|
+
data-accent-color={color}
|
|
584
|
+
data-radius={radius as any}
|
|
585
|
+
data-panel-background={effectiveMaterial}
|
|
586
|
+
data-material={effectiveMaterial}
|
|
524
587
|
aria-expanded={open}
|
|
525
588
|
onBlurCapture={handleBlurCapture}
|
|
526
589
|
>
|
|
527
590
|
{dropzone && <input {...getInputProps()} />}
|
|
528
|
-
<div {...(dropzone ? getRootProps() : {})} style={{ width: '100%', height: '100%' }}>
|
|
529
|
-
<
|
|
530
|
-
className=
|
|
531
|
-
size={Math.min(3, Number(size) + 1).toString() as '1' | '2' | '3'}
|
|
532
|
-
variant={variant as any}
|
|
591
|
+
<div {...(dropzone ? getRootProps() : {})} style={{ width: '100%', height: '100%' }} onPointerDown={handleContainerPointerDown}>
|
|
592
|
+
<Box
|
|
593
|
+
className={classNames('rt-ChatbarBox', `rt-variant-${variant ?? 'surface'}`)}
|
|
533
594
|
style={{ position: 'relative' }}
|
|
595
|
+
data-accent-color={color}
|
|
596
|
+
data-radius={radius as any}
|
|
597
|
+
data-panel-background={effectiveMaterial}
|
|
598
|
+
data-material={effectiveMaterial}
|
|
534
599
|
>
|
|
535
600
|
<div className="rt-ChatbarGrid">{children}</div>
|
|
536
601
|
{dropzone && isDragActive && (
|
|
@@ -542,7 +607,7 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
542
607
|
</div>
|
|
543
608
|
</div>
|
|
544
609
|
)}
|
|
545
|
-
</
|
|
610
|
+
</Box>
|
|
546
611
|
</div>
|
|
547
612
|
</Comp>
|
|
548
613
|
</ChatbarContext.Provider>
|
|
@@ -588,37 +653,9 @@ interface TextareaProps extends Omit<React.ComponentPropsWithoutRef<'textarea'>,
|
|
|
588
653
|
* on this component, as no implicit label is rendered.
|
|
589
654
|
*/
|
|
590
655
|
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, forwardedRef) => {
|
|
591
|
-
const {
|
|
592
|
-
className,
|
|
593
|
-
style,
|
|
594
|
-
asChild,
|
|
595
|
-
onFocus,
|
|
596
|
-
onInput,
|
|
597
|
-
onChange,
|
|
598
|
-
onPaste,
|
|
599
|
-
onKeyDown,
|
|
600
|
-
submitOnEnter = false,
|
|
601
|
-
rows,
|
|
602
|
-
...textareaProps
|
|
603
|
-
} = props;
|
|
656
|
+
const { className, style, asChild, onFocus, onInput, onChange, onPaste, onKeyDown, submitOnEnter = false, rows, ...textareaProps } = props;
|
|
604
657
|
const ctx = useChatbarContext();
|
|
605
|
-
const {
|
|
606
|
-
open,
|
|
607
|
-
minLines,
|
|
608
|
-
maxLines,
|
|
609
|
-
expandOn,
|
|
610
|
-
disabled,
|
|
611
|
-
readOnly,
|
|
612
|
-
setOpen,
|
|
613
|
-
setValue,
|
|
614
|
-
textareaRef,
|
|
615
|
-
value,
|
|
616
|
-
isValueControlled,
|
|
617
|
-
sendMode,
|
|
618
|
-
paste,
|
|
619
|
-
appendFilesFromPaste,
|
|
620
|
-
size,
|
|
621
|
-
} = ctx;
|
|
658
|
+
const { open, minLines, maxLines, expandOn, disabled, readOnly, setOpen, setValue, textareaRef, value, isValueControlled, sendMode, paste, appendFilesFromPaste, size } = ctx;
|
|
622
659
|
|
|
623
660
|
// Cached metrics to avoid repeated getComputedStyle calls
|
|
624
661
|
const lineHeightRef = React.useRef<number>(0);
|
|
@@ -679,6 +716,26 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
679
716
|
updateHeight();
|
|
680
717
|
}, [updateHeight, value, open]);
|
|
681
718
|
|
|
719
|
+
// Auto-open on external value changes when content overflows one line
|
|
720
|
+
useIsomorphicLayoutEffect(() => {
|
|
721
|
+
if (!(expandOn === 'overflow' || expandOn === 'both')) return;
|
|
722
|
+
if (open) return;
|
|
723
|
+
const el = textareaRef.current;
|
|
724
|
+
if (!el) return;
|
|
725
|
+
// Measure overflow against compact (1-line) height
|
|
726
|
+
el.style.height = 'auto';
|
|
727
|
+
if (compactHeightRef.current === 0) {
|
|
728
|
+
recomputeMetrics();
|
|
729
|
+
}
|
|
730
|
+
const shouldExpand = el.scrollHeight > compactHeightRef.current + 1;
|
|
731
|
+
if (shouldExpand) {
|
|
732
|
+
setOpen(true);
|
|
733
|
+
// Immediately size for open state
|
|
734
|
+
updateHeight(true);
|
|
735
|
+
requestAnimationFrame(() => updateHeight(true));
|
|
736
|
+
}
|
|
737
|
+
}, [value, expandOn, open, setOpen, textareaRef, recomputeMetrics, updateHeight]);
|
|
738
|
+
|
|
682
739
|
// Recompute metrics on mount and when size changes may affect typography
|
|
683
740
|
useIsomorphicLayoutEffect(() => {
|
|
684
741
|
recomputeMetrics();
|
|
@@ -706,13 +763,10 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
706
763
|
// Dev-only warning if no accessible name is provided
|
|
707
764
|
React.useEffect(() => {
|
|
708
765
|
if (process.env.NODE_ENV === 'production') return;
|
|
709
|
-
const hasLabel =
|
|
710
|
-
textareaProps['aria-label'] != null || textareaProps['aria-labelledby'] != null;
|
|
766
|
+
const hasLabel = textareaProps['aria-label'] != null || textareaProps['aria-labelledby'] != null;
|
|
711
767
|
if (!hasLabel) {
|
|
712
768
|
// eslint-disable-next-line no-console
|
|
713
|
-
console.warn(
|
|
714
|
-
'[Chatbar.Textarea] Provide aria-label or aria-labelledby to ensure the control has an accessible name.',
|
|
715
|
-
);
|
|
769
|
+
console.warn('[Chatbar.Textarea] Provide aria-label or aria-labelledby to ensure the control has an accessible name.');
|
|
716
770
|
}
|
|
717
771
|
// warn only on mount
|
|
718
772
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -768,6 +822,8 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
768
822
|
.map((i) => i.getAsFile())
|
|
769
823
|
.filter((f): f is File => !!f);
|
|
770
824
|
if (files.length > 0) {
|
|
825
|
+
// Prevent pasting the file name or any text representation when files are present
|
|
826
|
+
event.preventDefault();
|
|
771
827
|
appendFilesFromPaste(files);
|
|
772
828
|
}
|
|
773
829
|
}
|
|
@@ -790,14 +846,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
790
846
|
onKeyDown?.(event);
|
|
791
847
|
return;
|
|
792
848
|
}
|
|
793
|
-
if (
|
|
794
|
-
event.key === 'Enter' &&
|
|
795
|
-
!event.shiftKey &&
|
|
796
|
-
!event.altKey &&
|
|
797
|
-
!event.ctrlKey &&
|
|
798
|
-
!event.metaKey &&
|
|
799
|
-
!event.nativeEvent.isComposing
|
|
800
|
-
) {
|
|
849
|
+
if (event.key === 'Enter' && !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey && !event.nativeEvent.isComposing) {
|
|
801
850
|
if (disabled || readOnly) {
|
|
802
851
|
onKeyDown?.(event);
|
|
803
852
|
return;
|
|
@@ -821,17 +870,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
821
870
|
}
|
|
822
871
|
onKeyDown?.(event);
|
|
823
872
|
},
|
|
824
|
-
[
|
|
825
|
-
submitOnEnter,
|
|
826
|
-
disabled,
|
|
827
|
-
readOnly,
|
|
828
|
-
sendMode,
|
|
829
|
-
value,
|
|
830
|
-
isValueControlled,
|
|
831
|
-
setValue,
|
|
832
|
-
ctx,
|
|
833
|
-
onKeyDown,
|
|
834
|
-
],
|
|
873
|
+
[submitOnEnter, disabled, readOnly, sendMode, value, isValueControlled, setValue, ctx, onKeyDown],
|
|
835
874
|
);
|
|
836
875
|
|
|
837
876
|
const Comp = asChild ? Slot : ('textarea' as any);
|
|
@@ -841,8 +880,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
841
880
|
{...textareaProps}
|
|
842
881
|
ref={(node: HTMLTextAreaElement) => {
|
|
843
882
|
if (typeof forwardedRef === 'function') forwardedRef(node);
|
|
844
|
-
else if (forwardedRef)
|
|
845
|
-
(forwardedRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
|
|
883
|
+
else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
|
|
846
884
|
(textareaRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
|
|
847
885
|
}}
|
|
848
886
|
className="rt-ChatbarInput"
|
|
@@ -875,12 +913,7 @@ const InlineStart = React.forwardRef<HTMLDivElement, InlineSlotProps>((props, fo
|
|
|
875
913
|
if (ctx.open) return null;
|
|
876
914
|
const Comp = asChild ? Slot : ('div' as any);
|
|
877
915
|
return (
|
|
878
|
-
<Comp
|
|
879
|
-
{...divProps}
|
|
880
|
-
ref={forwardedRef}
|
|
881
|
-
className={classNames('rt-ChatbarInlineStart', className)}
|
|
882
|
-
style={style}
|
|
883
|
-
>
|
|
916
|
+
<Comp {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarInlineStart', className)} style={style}>
|
|
884
917
|
{children}
|
|
885
918
|
</Comp>
|
|
886
919
|
);
|
|
@@ -893,12 +926,7 @@ const InlineEnd = React.forwardRef<HTMLDivElement, InlineSlotProps>((props, forw
|
|
|
893
926
|
if (ctx.open) return null;
|
|
894
927
|
const Comp = asChild ? Slot : ('div' as any);
|
|
895
928
|
return (
|
|
896
|
-
<Comp
|
|
897
|
-
{...divProps}
|
|
898
|
-
ref={forwardedRef}
|
|
899
|
-
className={classNames('rt-ChatbarInlineEnd', className)}
|
|
900
|
-
style={style}
|
|
901
|
-
>
|
|
929
|
+
<Comp {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarInlineEnd', className)} style={style}>
|
|
902
930
|
{children}
|
|
903
931
|
</Comp>
|
|
904
932
|
);
|
|
@@ -916,35 +944,26 @@ interface AttachmentsRowProps extends Omit<React.ComponentPropsWithoutRef<'div'>
|
|
|
916
944
|
renderAttachment?: (attachment: ChatbarAttachment) => React.ReactNode;
|
|
917
945
|
}
|
|
918
946
|
|
|
919
|
-
const AttachmentsRow = React.forwardRef<HTMLDivElement, AttachmentsRowProps>(
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
<
|
|
928
|
-
{
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
{renderAttachment?.(att)}
|
|
940
|
-
</Attachment>
|
|
941
|
-
))}
|
|
942
|
-
</Flex>
|
|
943
|
-
</ScrollArea>
|
|
944
|
-
</Comp>
|
|
945
|
-
);
|
|
946
|
-
},
|
|
947
|
-
);
|
|
947
|
+
const AttachmentsRow = React.forwardRef<HTMLDivElement, AttachmentsRowProps>((props, forwardedRef) => {
|
|
948
|
+
const { asChild, forceMount, renderAttachment, className, style, ...divProps } = props;
|
|
949
|
+
const ctx = useChatbarContext();
|
|
950
|
+
const hasItems = ctx.attachments.length > 0;
|
|
951
|
+
if (!hasItems && !forceMount) return null;
|
|
952
|
+
const Comp = asChild ? Slot : ('div' as any);
|
|
953
|
+
return (
|
|
954
|
+
<Comp {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarAttachmentsRow', className)} style={style} role="list" aria-label={divProps['aria-label'] ?? 'Attachments'}>
|
|
955
|
+
<ScrollArea className="rt-ChatbarScrollArea" scrollbars="horizontal" size="1">
|
|
956
|
+
<Flex align="center" gap="2" style={{ minWidth: 'fit-content' }}>
|
|
957
|
+
{ctx.attachments.map((att) => (
|
|
958
|
+
<Attachment key={att.id} attachment={att} asChild={!!renderAttachment}>
|
|
959
|
+
{renderAttachment?.(att)}
|
|
960
|
+
</Attachment>
|
|
961
|
+
))}
|
|
962
|
+
</Flex>
|
|
963
|
+
</ScrollArea>
|
|
964
|
+
</Comp>
|
|
965
|
+
);
|
|
966
|
+
});
|
|
948
967
|
AttachmentsRow.displayName = 'Chatbar.AttachmentsRow';
|
|
949
968
|
|
|
950
969
|
/** Default tile renderer for a single attachment. */
|
|
@@ -959,55 +978,37 @@ const Attachment = React.forwardRef<HTMLDivElement, AttachmentProps>((props, for
|
|
|
959
978
|
const Comp = asChild ? Slot : ('div' as any);
|
|
960
979
|
const isImage = !!attachment.url && attachment.type.startsWith('image/');
|
|
961
980
|
return (
|
|
962
|
-
<Comp
|
|
963
|
-
{...divProps}
|
|
964
|
-
ref={forwardedRef}
|
|
965
|
-
className={classNames('rt-ChatbarAttachment', className)}
|
|
966
|
-
style={style}
|
|
967
|
-
role="listitem"
|
|
968
|
-
data-kind={isImage ? 'image' : 'file'}
|
|
969
|
-
title={attachment.name}
|
|
970
|
-
>
|
|
981
|
+
<Comp {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarAttachment', className)} style={style} role="listitem" data-kind={isImage ? 'image' : 'file'} title={attachment.name}>
|
|
971
982
|
{children ?? (
|
|
972
|
-
<Card size={ctx.size} variant="
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
<
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
highContrast
|
|
1002
|
-
color="gray"
|
|
1003
|
-
onClick={() =>
|
|
1004
|
-
ctx.setAttachments(ctx.attachments.filter((a) => a.id !== attachment.id))
|
|
1005
|
-
}
|
|
1006
|
-
>
|
|
1007
|
-
<CloseIcon />
|
|
1008
|
-
</IconButton>
|
|
1009
|
-
</Flex>
|
|
1010
|
-
</Card>
|
|
983
|
+
// <Card size={ctx.size} variant="surface">
|
|
984
|
+
<Flex align="center" gap="2" pr={!isImage ? '6' : undefined}>
|
|
985
|
+
<div className="rt-ChatbarAttachmentPreview" aria-hidden>
|
|
986
|
+
{isImage ? <img className="rt-ChatbarAttachmentImage" src={attachment.url} alt="" /> : <FileTextIcon />}
|
|
987
|
+
</div>
|
|
988
|
+
{!isImage && (
|
|
989
|
+
<Flex direction="column" gap="0" style={{ minWidth: 0 }}>
|
|
990
|
+
<Text size={ctx.size} weight="medium" style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
|
991
|
+
{attachment.name}
|
|
992
|
+
</Text>
|
|
993
|
+
<Text size="1" color="gray">
|
|
994
|
+
{Math.ceil(attachment.size / 1024)} KB
|
|
995
|
+
</Text>
|
|
996
|
+
</Flex>
|
|
997
|
+
)}
|
|
998
|
+
<IconButton
|
|
999
|
+
className="rt-ChatbarAttachmentRemove"
|
|
1000
|
+
aria-label={`Remove ${attachment.name}`}
|
|
1001
|
+
size="1"
|
|
1002
|
+
// size={ctx.size}
|
|
1003
|
+
variant="classic"
|
|
1004
|
+
highContrast
|
|
1005
|
+
color="gray"
|
|
1006
|
+
onClick={() => ctx.setAttachments(ctx.attachments.filter((a) => a.id !== attachment.id))}
|
|
1007
|
+
>
|
|
1008
|
+
<CloseIcon />
|
|
1009
|
+
</IconButton>
|
|
1010
|
+
</Flex>
|
|
1011
|
+
// </Card>
|
|
1011
1012
|
)}
|
|
1012
1013
|
</Comp>
|
|
1013
1014
|
);
|
|
@@ -1020,51 +1021,72 @@ interface AttachTriggerProps extends React.ComponentPropsWithoutRef<'button'> {
|
|
|
1020
1021
|
multiple?: boolean;
|
|
1021
1022
|
}
|
|
1022
1023
|
|
|
1023
|
-
const AttachTrigger = React.forwardRef<HTMLButtonElement, AttachTriggerProps>(
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1024
|
+
const AttachTrigger = React.forwardRef<HTMLButtonElement, AttachTriggerProps>((props, forwardedRef) => {
|
|
1025
|
+
const { asChild, accept, multiple, className, style, ...buttonProps } = props;
|
|
1026
|
+
const ctx = useChatbarContext();
|
|
1027
|
+
const inputRef = React.useRef<HTMLInputElement | null>(null);
|
|
1028
|
+
const Comp = asChild ? Slot : ('button' as any);
|
|
1029
|
+
// Prefer Chatbar.Root's accept when a local accept is not provided
|
|
1030
|
+
const mergedAccept = accept ?? ctx.accept;
|
|
1031
|
+
const actualAccept = (Array.isArray(mergedAccept) ? mergedAccept : (mergedAccept?.split(',') ?? [])).join(',');
|
|
1032
|
+
React.useEffect(() => {
|
|
1033
|
+
const handleWindowFocus = () => {
|
|
1034
|
+
// Reset guard when window regains focus after file dialog closes
|
|
1035
|
+
ctx.fileDialogOpenRef.current = false;
|
|
1036
|
+
};
|
|
1037
|
+
window.addEventListener('focus', handleWindowFocus);
|
|
1038
|
+
return () => window.removeEventListener('focus', handleWindowFocus);
|
|
1039
|
+
}, [ctx.fileDialogOpenRef]);
|
|
1040
|
+
return (
|
|
1041
|
+
<>
|
|
1042
|
+
<Comp
|
|
1043
|
+
{...(buttonProps as any)}
|
|
1044
|
+
ref={forwardedRef as any}
|
|
1045
|
+
className={classNames('rt-ChatbarAttachTrigger', className)}
|
|
1046
|
+
style={style}
|
|
1047
|
+
type={buttonProps.type ?? 'button'}
|
|
1048
|
+
aria-label={buttonProps['aria-label'] ?? 'Add attachments'}
|
|
1049
|
+
onPointerDown={(e: any) => {
|
|
1050
|
+
// Set guard before blur occurs (Safari fires blur before click)
|
|
1051
|
+
ctx.fileDialogOpenRef.current = true;
|
|
1052
|
+
buttonProps.onPointerDown?.(e);
|
|
1053
|
+
}}
|
|
1054
|
+
onMouseDown={(e: any) => {
|
|
1055
|
+
// Fallback for environments without Pointer Events
|
|
1056
|
+
ctx.fileDialogOpenRef.current = true;
|
|
1057
|
+
buttonProps.onMouseDown?.(e);
|
|
1058
|
+
}}
|
|
1059
|
+
onClick={(e: any) => {
|
|
1060
|
+
// Ensure file input opens reliably by clicking it first
|
|
1061
|
+
ctx.fileDialogOpenRef.current = true;
|
|
1062
|
+
if (inputRef.current) {
|
|
1063
|
+
inputRef.current.click();
|
|
1064
|
+
}
|
|
1065
|
+
// Then call user's onClick if provided
|
|
1066
|
+
buttonProps.onClick?.(e);
|
|
1067
|
+
}}
|
|
1068
|
+
/>
|
|
1069
|
+
<input
|
|
1070
|
+
ref={inputRef}
|
|
1071
|
+
type="file"
|
|
1072
|
+
accept={actualAccept}
|
|
1073
|
+
multiple={multiple ?? ctx.multiple}
|
|
1074
|
+
tabIndex={-1}
|
|
1075
|
+
style={{ display: 'none' }}
|
|
1076
|
+
onChange={(e) => {
|
|
1077
|
+
const files = Array.from(e.currentTarget.files ?? []);
|
|
1078
|
+
if (files.length > 0) {
|
|
1079
|
+
ctx.appendFiles(files);
|
|
1080
|
+
}
|
|
1081
|
+
// File dialog closed; allow normal blur handling
|
|
1082
|
+
ctx.fileDialogOpenRef.current = false;
|
|
1083
|
+
// Reset input value to allow selecting the same file again
|
|
1084
|
+
e.currentTarget.value = '';
|
|
1085
|
+
}}
|
|
1086
|
+
/>
|
|
1087
|
+
</>
|
|
1088
|
+
);
|
|
1089
|
+
});
|
|
1068
1090
|
AttachTrigger.displayName = 'Chatbar.AttachTrigger';
|
|
1069
1091
|
interface RowProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
1070
1092
|
asChild?: boolean;
|
|
@@ -1077,12 +1099,7 @@ const Row = React.forwardRef<HTMLDivElement, RowProps>((props, forwardedRef) =>
|
|
|
1077
1099
|
if (!ctx.open) return null;
|
|
1078
1100
|
const Comp = asChild ? Slot : ('div' as any);
|
|
1079
1101
|
return (
|
|
1080
|
-
<Comp
|
|
1081
|
-
{...divProps}
|
|
1082
|
-
ref={forwardedRef}
|
|
1083
|
-
className={classNames('rt-ChatbarRow', className)}
|
|
1084
|
-
style={style}
|
|
1085
|
-
>
|
|
1102
|
+
<Comp {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarRow', className)} style={style}>
|
|
1086
1103
|
<Flex align="center" justify="between" width="100%">
|
|
1087
1104
|
{children}
|
|
1088
1105
|
</Flex>
|
|
@@ -1091,34 +1108,16 @@ const Row = React.forwardRef<HTMLDivElement, RowProps>((props, forwardedRef) =>
|
|
|
1091
1108
|
});
|
|
1092
1109
|
Row.displayName = 'Chatbar.Row';
|
|
1093
1110
|
|
|
1094
|
-
const RowStart = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>(
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
<div
|
|
1099
|
-
{...divProps}
|
|
1100
|
-
ref={forwardedRef}
|
|
1101
|
-
className={classNames('rt-ChatbarRowStart', className)}
|
|
1102
|
-
style={style}
|
|
1103
|
-
/>
|
|
1104
|
-
);
|
|
1105
|
-
},
|
|
1106
|
-
);
|
|
1111
|
+
const RowStart = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>((props, forwardedRef) => {
|
|
1112
|
+
const { className, style, ...divProps } = props;
|
|
1113
|
+
return <div {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarRowStart', className)} style={style} />;
|
|
1114
|
+
});
|
|
1107
1115
|
RowStart.displayName = 'Chatbar.RowStart';
|
|
1108
1116
|
|
|
1109
|
-
const RowEnd = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>(
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
<div
|
|
1114
|
-
{...divProps}
|
|
1115
|
-
ref={forwardedRef}
|
|
1116
|
-
className={classNames('rt-ChatbarRowEnd', className)}
|
|
1117
|
-
style={style}
|
|
1118
|
-
/>
|
|
1119
|
-
);
|
|
1120
|
-
},
|
|
1121
|
-
);
|
|
1117
|
+
const RowEnd = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>((props, forwardedRef) => {
|
|
1118
|
+
const { className, style, ...divProps } = props;
|
|
1119
|
+
return <div {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarRowEnd', className)} style={style} />;
|
|
1120
|
+
});
|
|
1122
1121
|
RowEnd.displayName = 'Chatbar.RowEnd';
|
|
1123
1122
|
|
|
1124
1123
|
interface SendProps extends Omit<IconButtonProps, 'size'> {
|
|
@@ -1127,15 +1126,7 @@ interface SendProps extends Omit<IconButtonProps, 'size'> {
|
|
|
1127
1126
|
}
|
|
1128
1127
|
|
|
1129
1128
|
const Send = React.forwardRef<HTMLButtonElement, SendProps>((props, forwardedRef) => {
|
|
1130
|
-
const {
|
|
1131
|
-
asChild,
|
|
1132
|
-
clearOnSend = true,
|
|
1133
|
-
disabled,
|
|
1134
|
-
children,
|
|
1135
|
-
className,
|
|
1136
|
-
style,
|
|
1137
|
-
...buttonProps
|
|
1138
|
-
} = props;
|
|
1129
|
+
const { asChild, clearOnSend = true, disabled, children, className, style, ...buttonProps } = props;
|
|
1139
1130
|
const ctx = useChatbarContext();
|
|
1140
1131
|
|
|
1141
1132
|
const trimmed = ctx.value.trim();
|
|
@@ -1170,29 +1161,27 @@ const Send = React.forwardRef<HTMLButtonElement, SendProps>((props, forwardedRef
|
|
|
1170
1161
|
onClick={handleClick}
|
|
1171
1162
|
aria-label={buttonProps['aria-label'] ?? 'Send'}
|
|
1172
1163
|
>
|
|
1173
|
-
{children ??
|
|
1164
|
+
{children ?? (
|
|
1165
|
+
<svg
|
|
1166
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1167
|
+
width="24"
|
|
1168
|
+
height="24"
|
|
1169
|
+
viewBox="0 0 24 24"
|
|
1170
|
+
fill="none"
|
|
1171
|
+
stroke="currentColor"
|
|
1172
|
+
strokeWidth="2"
|
|
1173
|
+
strokeLinecap="round"
|
|
1174
|
+
strokeLinejoin="round"
|
|
1175
|
+
className="lucide lucide-arrow-right-icon lucide-arrow-right"
|
|
1176
|
+
>
|
|
1177
|
+
<path d="M5 12h14" />
|
|
1178
|
+
<path d="m12 5 7 7-7 7" />
|
|
1179
|
+
</svg>
|
|
1180
|
+
)}
|
|
1174
1181
|
</IconButton>
|
|
1175
1182
|
);
|
|
1176
1183
|
});
|
|
1177
1184
|
Send.displayName = 'Chatbar.Send';
|
|
1178
1185
|
|
|
1179
|
-
export {
|
|
1180
|
-
|
|
1181
|
-
Textarea,
|
|
1182
|
-
InlineStart,
|
|
1183
|
-
InlineEnd,
|
|
1184
|
-
AttachmentsRow,
|
|
1185
|
-
Attachment,
|
|
1186
|
-
AttachTrigger,
|
|
1187
|
-
Row,
|
|
1188
|
-
RowStart,
|
|
1189
|
-
RowEnd,
|
|
1190
|
-
Send,
|
|
1191
|
-
};
|
|
1192
|
-
export type {
|
|
1193
|
-
RootProps as ChatbarRootProps,
|
|
1194
|
-
TextareaProps as ChatbarTextareaProps,
|
|
1195
|
-
RowProps as ChatbarRowProps,
|
|
1196
|
-
SendProps as ChatbarSendProps,
|
|
1197
|
-
ChatbarAttachment,
|
|
1198
|
-
};
|
|
1186
|
+
export { Root, Textarea, InlineStart, InlineEnd, AttachmentsRow, Attachment, AttachTrigger, Row, RowStart, RowEnd, Send };
|
|
1187
|
+
export type { RootProps as ChatbarRootProps, TextareaProps as ChatbarTextareaProps, RowProps as ChatbarRowProps, SendProps as ChatbarSendProps, ChatbarAttachment };
|