@kushagradhawan/kookie-ui 0.1.49 → 0.1.51
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 +880 -243
- 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 +9 -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 -0
- 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/cjs/components/sidebar.d.ts +7 -1
- package/dist/cjs/components/sidebar.d.ts.map +1 -1
- package/dist/cjs/components/sidebar.js +1 -1
- package/dist/cjs/components/sidebar.js.map +3 -3
- 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 +9 -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 -0
- 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/dist/esm/components/sidebar.d.ts +7 -1
- package/dist/esm/components/sidebar.d.ts.map +1 -1
- package/dist/esm/components/sidebar.js +1 -1
- package/dist/esm/components/sidebar.js.map +3 -3
- 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 +17 -18
- package/src/components/_internal/base-sidebar-menu.css +23 -21
- package/src/components/_internal/base-sidebar.css +20 -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 +176 -69
- package/src/components/chatbar.css +240 -21
- package/src/components/chatbar.tsx +246 -290
- package/src/components/sheet.css +8 -16
- package/src/components/shell.context.tsx +79 -0
- package/src/components/shell.css +28 -2
- package/src/components/shell.hooks.ts +35 -0
- package/src/components/shell.tsx +574 -214
- package/src/components/shell.types.ts +2 -0
- package/src/components/sidebar.css +233 -33
- package/src/components/sidebar.tsx +247 -213
- package/styles.css +841 -204
|
@@ -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'];
|
|
@@ -199,9 +210,7 @@ type RootElement = React.ElementRef<'div'>;
|
|
|
199
210
|
* - `aria-expanded` on the root reflects the disclosure state of the input area.
|
|
200
211
|
* - Dropzone provides proper ARIA attributes for drag and drop operations.
|
|
201
212
|
*/
|
|
202
|
-
interface RootProps
|
|
203
|
-
extends ComponentPropsWithout<'div', RemovedProps | 'onSubmit'>,
|
|
204
|
-
ChatbarRootBaseProps {}
|
|
213
|
+
interface RootProps extends ComponentPropsWithout<'div', RemovedProps | 'onSubmit'>, ChatbarRootBaseProps {}
|
|
205
214
|
|
|
206
215
|
const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
207
216
|
const {
|
|
@@ -223,6 +232,10 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
223
232
|
onSubmit,
|
|
224
233
|
size = '2',
|
|
225
234
|
variant,
|
|
235
|
+
color,
|
|
236
|
+
radius,
|
|
237
|
+
panelBackground,
|
|
238
|
+
material,
|
|
226
239
|
width,
|
|
227
240
|
maxWidth,
|
|
228
241
|
asChild,
|
|
@@ -240,6 +253,7 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
240
253
|
dropzone = true,
|
|
241
254
|
...divProps
|
|
242
255
|
} = props;
|
|
256
|
+
const effectiveMaterial = material || panelBackground;
|
|
243
257
|
|
|
244
258
|
const isValueControlled = valueProp != null;
|
|
245
259
|
const [valueUncontrolled, setValueUncontrolled] = React.useState<string>(defaultValue);
|
|
@@ -251,23 +265,20 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
251
265
|
|
|
252
266
|
const rootRef = React.useRef<HTMLDivElement>(null);
|
|
253
267
|
const textareaRef = React.useRef<HTMLTextAreaElement>(null);
|
|
268
|
+
const fileDialogOpenRef = React.useRef<boolean>(false);
|
|
254
269
|
|
|
255
270
|
// Attachments state
|
|
256
271
|
// Treat `attachments` as controlled if the prop is provided, even if its value is `undefined`.
|
|
257
272
|
// This avoids switching between controlled and uncontrolled when a consumer sets
|
|
258
273
|
// `attachments={undefined}` to clear attachments. In that case we normalize to an empty array.
|
|
259
274
|
const isAttachmentsControlled = 'attachments' in props;
|
|
260
|
-
const [attachmentsUncontrolled, setAttachmentsUncontrolled] =
|
|
261
|
-
|
|
262
|
-
const attachments = isAttachmentsControlled
|
|
263
|
-
? ((attachmentsProp ?? []) as ChatbarAttachment[])
|
|
264
|
-
: attachmentsUncontrolled;
|
|
275
|
+
const [attachmentsUncontrolled, setAttachmentsUncontrolled] = React.useState<ChatbarAttachment[]>(defaultAttachments);
|
|
276
|
+
const attachments = isAttachmentsControlled ? ((attachmentsProp ?? []) as ChatbarAttachment[]) : attachmentsUncontrolled;
|
|
265
277
|
|
|
266
278
|
// Track generated object URLs for cleanup
|
|
267
279
|
const generatedUrlSetRef = React.useRef<Set<string>>(new Set());
|
|
268
280
|
|
|
269
|
-
const toArray = (val: string | string[] | undefined) =>
|
|
270
|
-
Array.isArray(val) ? val : typeof val === 'string' ? val.split(',').map((s) => s.trim()) : [];
|
|
281
|
+
const toArray = (val: string | string[] | undefined) => (Array.isArray(val) ? val : typeof val === 'string' ? val.split(',').map((s) => s.trim()) : []);
|
|
271
282
|
|
|
272
283
|
const accepts = toArray(accept);
|
|
273
284
|
const pasteAccepts = toArray(pasteAccept).length > 0 ? toArray(pasteAccept) : accepts;
|
|
@@ -282,8 +293,7 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
282
293
|
// MIME pattern
|
|
283
294
|
const [type, subtype] = pat.split('/');
|
|
284
295
|
const [fmType, fmSubtype] = mime.split('/');
|
|
285
|
-
if (type === '*' || (type === fmType && (subtype === '*' || subtype === fmSubtype)))
|
|
286
|
-
return true;
|
|
296
|
+
if (type === '*' || (type === fmType && (subtype === '*' || subtype === fmSubtype))) return true;
|
|
287
297
|
} else if (pat.startsWith('.')) {
|
|
288
298
|
if (name.endsWith(pat)) return true;
|
|
289
299
|
}
|
|
@@ -303,10 +313,7 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
303
313
|
const next: ChatbarAttachment[] = [];
|
|
304
314
|
const rejected: { file: File; reason: 'type' | 'size' | 'count' }[] = [];
|
|
305
315
|
|
|
306
|
-
const remainingSlots =
|
|
307
|
-
typeof maxAttachments === 'number'
|
|
308
|
-
? Math.max(maxAttachments - attachments.length, 0)
|
|
309
|
-
: Infinity;
|
|
316
|
+
const remainingSlots = typeof maxAttachments === 'number' ? Math.max(maxAttachments - attachments.length, 0) : Infinity;
|
|
310
317
|
|
|
311
318
|
for (const file of files) {
|
|
312
319
|
if (next.length >= remainingSlots) {
|
|
@@ -345,6 +352,9 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
345
352
|
const merged = attachments.concat(accepted);
|
|
346
353
|
if (!isAttachmentsControlled) setAttachmentsUncontrolled(merged);
|
|
347
354
|
onAttachmentsChange?.(merged);
|
|
355
|
+
// Ensure chatbar expands when attachments are added
|
|
356
|
+
if (!isOpenControlled) setOpenUncontrolled(true);
|
|
357
|
+
onOpenChangeProp?.(true);
|
|
348
358
|
}
|
|
349
359
|
if (rejected.length > 0) onAttachmentReject?.(rejected);
|
|
350
360
|
};
|
|
@@ -359,10 +369,7 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
359
369
|
const rejected: { file: File; reason: 'type' | 'size' | 'count' }[] = [];
|
|
360
370
|
|
|
361
371
|
// Enforce maxAttachments and maxFileSize
|
|
362
|
-
const remainingSlots =
|
|
363
|
-
typeof maxAttachments === 'number'
|
|
364
|
-
? Math.max(maxAttachments - attachments.length, 0)
|
|
365
|
-
: Infinity;
|
|
372
|
+
const remainingSlots = typeof maxAttachments === 'number' ? Math.max(maxAttachments - attachments.length, 0) : Infinity;
|
|
366
373
|
for (const file of files) {
|
|
367
374
|
if (acceptedFiles.length >= remainingSlots) {
|
|
368
375
|
rejected.push({ file, reason: 'count' });
|
|
@@ -412,13 +419,16 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
412
419
|
if (!rootEl) return;
|
|
413
420
|
// If focus remains within root, ignore
|
|
414
421
|
if (nextTarget && rootEl.contains(nextTarget)) return;
|
|
422
|
+
// If native file dialog is open, avoid collapsing on blur
|
|
423
|
+
if (fileDialogOpenRef.current) return;
|
|
415
424
|
// Collapse when leaving the root if the value is empty
|
|
416
|
-
|
|
425
|
+
// Only collapse when both message and attachments are empty
|
|
426
|
+
if ((value?.trim?.() ?? '').length === 0 && attachments.length === 0) {
|
|
417
427
|
if (!isOpenControlled) setOpenUncontrolled(false);
|
|
418
428
|
onOpenChangeProp?.(false);
|
|
419
429
|
}
|
|
420
430
|
},
|
|
421
|
-
[isOpenControlled, onOpenChangeProp, value],
|
|
431
|
+
[isOpenControlled, onOpenChangeProp, value, attachments],
|
|
422
432
|
);
|
|
423
433
|
|
|
424
434
|
// Dropzone functionality
|
|
@@ -429,12 +439,7 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
429
439
|
}
|
|
430
440
|
if (rejectedFiles.length > 0 && onAttachmentReject) {
|
|
431
441
|
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';
|
|
442
|
+
const reason = errors[0]?.code === 'file-too-large' ? 'size' : errors[0]?.code === 'file-invalid-type' ? 'type' : 'count';
|
|
438
443
|
return { file, reason: reason as 'type' | 'size' | 'count' };
|
|
439
444
|
});
|
|
440
445
|
onAttachmentReject(rejections);
|
|
@@ -463,6 +468,27 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
463
468
|
disabled: !dropzone || disabled,
|
|
464
469
|
});
|
|
465
470
|
|
|
471
|
+
// Click-to-focus: focus textarea when clicking non-interactive areas inside the container
|
|
472
|
+
const isInteractiveTarget = React.useCallback((target: EventTarget | null) => {
|
|
473
|
+
if (!(target instanceof Element)) return false;
|
|
474
|
+
const el = target as Element;
|
|
475
|
+
if (el.closest('.rt-ChatbarDropOverlay')) return true;
|
|
476
|
+
return !!el.closest('button, [role="button"], a[href], input, textarea, select, [contenteditable], [tabindex]:not([tabindex="-1"])');
|
|
477
|
+
}, []);
|
|
478
|
+
|
|
479
|
+
const handleContainerPointerDown = React.useCallback(
|
|
480
|
+
(event: React.PointerEvent) => {
|
|
481
|
+
if (disabled) return;
|
|
482
|
+
if (isInteractiveTarget(event.target)) return;
|
|
483
|
+
if (event.pointerType === 'mouse' && event.button !== 0) return;
|
|
484
|
+
event.preventDefault();
|
|
485
|
+
textareaRef.current?.focus({ preventScroll: true });
|
|
486
|
+
},
|
|
487
|
+
[disabled, isInteractiveTarget, textareaRef],
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
// Clicking the label-wrapped Card will naturally focus the nested textarea.
|
|
491
|
+
|
|
466
492
|
return (
|
|
467
493
|
<ChatbarContext.Provider
|
|
468
494
|
value={{
|
|
@@ -505,14 +531,14 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
505
531
|
dropzone,
|
|
506
532
|
appendFiles,
|
|
507
533
|
appendFilesFromPaste,
|
|
534
|
+
fileDialogOpenRef,
|
|
508
535
|
}}
|
|
509
536
|
>
|
|
510
537
|
<Comp
|
|
511
538
|
{...divProps}
|
|
512
539
|
ref={(node: HTMLDivElement) => {
|
|
513
540
|
if (typeof forwardedRef === 'function') forwardedRef(node);
|
|
514
|
-
else if (forwardedRef)
|
|
515
|
-
(forwardedRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
541
|
+
else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
516
542
|
(rootRef as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
517
543
|
}}
|
|
518
544
|
className={classNames('rt-ChatbarRoot', `rt-r-size-${size}`, className)}
|
|
@@ -521,16 +547,22 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
521
547
|
data-disabled={disabled ? '' : undefined}
|
|
522
548
|
data-readonly={readOnly ? '' : undefined}
|
|
523
549
|
data-drop-active={dropzone && isDragActive ? '' : undefined}
|
|
550
|
+
data-accent-color={color}
|
|
551
|
+
data-radius={radius as any}
|
|
552
|
+
data-panel-background={effectiveMaterial}
|
|
553
|
+
data-material={effectiveMaterial}
|
|
524
554
|
aria-expanded={open}
|
|
525
555
|
onBlurCapture={handleBlurCapture}
|
|
526
556
|
>
|
|
527
557
|
{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}
|
|
558
|
+
<div {...(dropzone ? getRootProps() : {})} style={{ width: '100%', height: '100%' }} onPointerDown={handleContainerPointerDown}>
|
|
559
|
+
<Box
|
|
560
|
+
className={classNames('rt-ChatbarBox', `rt-variant-${variant ?? 'surface'}`)}
|
|
533
561
|
style={{ position: 'relative' }}
|
|
562
|
+
data-accent-color={color}
|
|
563
|
+
data-radius={radius as any}
|
|
564
|
+
data-panel-background={effectiveMaterial}
|
|
565
|
+
data-material={effectiveMaterial}
|
|
534
566
|
>
|
|
535
567
|
<div className="rt-ChatbarGrid">{children}</div>
|
|
536
568
|
{dropzone && isDragActive && (
|
|
@@ -542,7 +574,7 @@ const Root = React.forwardRef<RootElement, RootProps>((props, forwardedRef) => {
|
|
|
542
574
|
</div>
|
|
543
575
|
</div>
|
|
544
576
|
)}
|
|
545
|
-
</
|
|
577
|
+
</Box>
|
|
546
578
|
</div>
|
|
547
579
|
</Comp>
|
|
548
580
|
</ChatbarContext.Provider>
|
|
@@ -588,37 +620,9 @@ interface TextareaProps extends Omit<React.ComponentPropsWithoutRef<'textarea'>,
|
|
|
588
620
|
* on this component, as no implicit label is rendered.
|
|
589
621
|
*/
|
|
590
622
|
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;
|
|
623
|
+
const { className, style, asChild, onFocus, onInput, onChange, onPaste, onKeyDown, submitOnEnter = false, rows, ...textareaProps } = props;
|
|
604
624
|
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;
|
|
625
|
+
const { open, minLines, maxLines, expandOn, disabled, readOnly, setOpen, setValue, textareaRef, value, isValueControlled, sendMode, paste, appendFilesFromPaste, size } = ctx;
|
|
622
626
|
|
|
623
627
|
// Cached metrics to avoid repeated getComputedStyle calls
|
|
624
628
|
const lineHeightRef = React.useRef<number>(0);
|
|
@@ -679,6 +683,26 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
679
683
|
updateHeight();
|
|
680
684
|
}, [updateHeight, value, open]);
|
|
681
685
|
|
|
686
|
+
// Auto-open on external value changes when content overflows one line
|
|
687
|
+
useIsomorphicLayoutEffect(() => {
|
|
688
|
+
if (!(expandOn === 'overflow' || expandOn === 'both')) return;
|
|
689
|
+
if (open) return;
|
|
690
|
+
const el = textareaRef.current;
|
|
691
|
+
if (!el) return;
|
|
692
|
+
// Measure overflow against compact (1-line) height
|
|
693
|
+
el.style.height = 'auto';
|
|
694
|
+
if (compactHeightRef.current === 0) {
|
|
695
|
+
recomputeMetrics();
|
|
696
|
+
}
|
|
697
|
+
const shouldExpand = el.scrollHeight > compactHeightRef.current + 1;
|
|
698
|
+
if (shouldExpand) {
|
|
699
|
+
setOpen(true);
|
|
700
|
+
// Immediately size for open state
|
|
701
|
+
updateHeight(true);
|
|
702
|
+
requestAnimationFrame(() => updateHeight(true));
|
|
703
|
+
}
|
|
704
|
+
}, [value, expandOn, open, setOpen, textareaRef, recomputeMetrics, updateHeight]);
|
|
705
|
+
|
|
682
706
|
// Recompute metrics on mount and when size changes may affect typography
|
|
683
707
|
useIsomorphicLayoutEffect(() => {
|
|
684
708
|
recomputeMetrics();
|
|
@@ -706,13 +730,10 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
706
730
|
// Dev-only warning if no accessible name is provided
|
|
707
731
|
React.useEffect(() => {
|
|
708
732
|
if (process.env.NODE_ENV === 'production') return;
|
|
709
|
-
const hasLabel =
|
|
710
|
-
textareaProps['aria-label'] != null || textareaProps['aria-labelledby'] != null;
|
|
733
|
+
const hasLabel = textareaProps['aria-label'] != null || textareaProps['aria-labelledby'] != null;
|
|
711
734
|
if (!hasLabel) {
|
|
712
735
|
// 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
|
-
);
|
|
736
|
+
console.warn('[Chatbar.Textarea] Provide aria-label or aria-labelledby to ensure the control has an accessible name.');
|
|
716
737
|
}
|
|
717
738
|
// warn only on mount
|
|
718
739
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -768,6 +789,8 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
768
789
|
.map((i) => i.getAsFile())
|
|
769
790
|
.filter((f): f is File => !!f);
|
|
770
791
|
if (files.length > 0) {
|
|
792
|
+
// Prevent pasting the file name or any text representation when files are present
|
|
793
|
+
event.preventDefault();
|
|
771
794
|
appendFilesFromPaste(files);
|
|
772
795
|
}
|
|
773
796
|
}
|
|
@@ -790,14 +813,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
790
813
|
onKeyDown?.(event);
|
|
791
814
|
return;
|
|
792
815
|
}
|
|
793
|
-
if (
|
|
794
|
-
event.key === 'Enter' &&
|
|
795
|
-
!event.shiftKey &&
|
|
796
|
-
!event.altKey &&
|
|
797
|
-
!event.ctrlKey &&
|
|
798
|
-
!event.metaKey &&
|
|
799
|
-
!event.nativeEvent.isComposing
|
|
800
|
-
) {
|
|
816
|
+
if (event.key === 'Enter' && !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey && !event.nativeEvent.isComposing) {
|
|
801
817
|
if (disabled || readOnly) {
|
|
802
818
|
onKeyDown?.(event);
|
|
803
819
|
return;
|
|
@@ -821,17 +837,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
821
837
|
}
|
|
822
838
|
onKeyDown?.(event);
|
|
823
839
|
},
|
|
824
|
-
[
|
|
825
|
-
submitOnEnter,
|
|
826
|
-
disabled,
|
|
827
|
-
readOnly,
|
|
828
|
-
sendMode,
|
|
829
|
-
value,
|
|
830
|
-
isValueControlled,
|
|
831
|
-
setValue,
|
|
832
|
-
ctx,
|
|
833
|
-
onKeyDown,
|
|
834
|
-
],
|
|
840
|
+
[submitOnEnter, disabled, readOnly, sendMode, value, isValueControlled, setValue, ctx, onKeyDown],
|
|
835
841
|
);
|
|
836
842
|
|
|
837
843
|
const Comp = asChild ? Slot : ('textarea' as any);
|
|
@@ -841,8 +847,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>((props, fo
|
|
|
841
847
|
{...textareaProps}
|
|
842
848
|
ref={(node: HTMLTextAreaElement) => {
|
|
843
849
|
if (typeof forwardedRef === 'function') forwardedRef(node);
|
|
844
|
-
else if (forwardedRef)
|
|
845
|
-
(forwardedRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
|
|
850
|
+
else if (forwardedRef) (forwardedRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
|
|
846
851
|
(textareaRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;
|
|
847
852
|
}}
|
|
848
853
|
className="rt-ChatbarInput"
|
|
@@ -875,12 +880,7 @@ const InlineStart = React.forwardRef<HTMLDivElement, InlineSlotProps>((props, fo
|
|
|
875
880
|
if (ctx.open) return null;
|
|
876
881
|
const Comp = asChild ? Slot : ('div' as any);
|
|
877
882
|
return (
|
|
878
|
-
<Comp
|
|
879
|
-
{...divProps}
|
|
880
|
-
ref={forwardedRef}
|
|
881
|
-
className={classNames('rt-ChatbarInlineStart', className)}
|
|
882
|
-
style={style}
|
|
883
|
-
>
|
|
883
|
+
<Comp {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarInlineStart', className)} style={style}>
|
|
884
884
|
{children}
|
|
885
885
|
</Comp>
|
|
886
886
|
);
|
|
@@ -893,12 +893,7 @@ const InlineEnd = React.forwardRef<HTMLDivElement, InlineSlotProps>((props, forw
|
|
|
893
893
|
if (ctx.open) return null;
|
|
894
894
|
const Comp = asChild ? Slot : ('div' as any);
|
|
895
895
|
return (
|
|
896
|
-
<Comp
|
|
897
|
-
{...divProps}
|
|
898
|
-
ref={forwardedRef}
|
|
899
|
-
className={classNames('rt-ChatbarInlineEnd', className)}
|
|
900
|
-
style={style}
|
|
901
|
-
>
|
|
896
|
+
<Comp {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarInlineEnd', className)} style={style}>
|
|
902
897
|
{children}
|
|
903
898
|
</Comp>
|
|
904
899
|
);
|
|
@@ -916,35 +911,26 @@ interface AttachmentsRowProps extends Omit<React.ComponentPropsWithoutRef<'div'>
|
|
|
916
911
|
renderAttachment?: (attachment: ChatbarAttachment) => React.ReactNode;
|
|
917
912
|
}
|
|
918
913
|
|
|
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
|
-
);
|
|
914
|
+
const AttachmentsRow = React.forwardRef<HTMLDivElement, AttachmentsRowProps>((props, forwardedRef) => {
|
|
915
|
+
const { asChild, forceMount, renderAttachment, className, style, ...divProps } = props;
|
|
916
|
+
const ctx = useChatbarContext();
|
|
917
|
+
const hasItems = ctx.attachments.length > 0;
|
|
918
|
+
if (!hasItems && !forceMount) return null;
|
|
919
|
+
const Comp = asChild ? Slot : ('div' as any);
|
|
920
|
+
return (
|
|
921
|
+
<Comp {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarAttachmentsRow', className)} style={style} role="list" aria-label={divProps['aria-label'] ?? 'Attachments'}>
|
|
922
|
+
<ScrollArea className="rt-ChatbarScrollArea" scrollbars="horizontal" size="1">
|
|
923
|
+
<Flex align="center" gap="2" style={{ minWidth: 'fit-content' }}>
|
|
924
|
+
{ctx.attachments.map((att) => (
|
|
925
|
+
<Attachment key={att.id} attachment={att} asChild={!!renderAttachment}>
|
|
926
|
+
{renderAttachment?.(att)}
|
|
927
|
+
</Attachment>
|
|
928
|
+
))}
|
|
929
|
+
</Flex>
|
|
930
|
+
</ScrollArea>
|
|
931
|
+
</Comp>
|
|
932
|
+
);
|
|
933
|
+
});
|
|
948
934
|
AttachmentsRow.displayName = 'Chatbar.AttachmentsRow';
|
|
949
935
|
|
|
950
936
|
/** Default tile renderer for a single attachment. */
|
|
@@ -959,55 +945,37 @@ const Attachment = React.forwardRef<HTMLDivElement, AttachmentProps>((props, for
|
|
|
959
945
|
const Comp = asChild ? Slot : ('div' as any);
|
|
960
946
|
const isImage = !!attachment.url && attachment.type.startsWith('image/');
|
|
961
947
|
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
|
-
>
|
|
948
|
+
<Comp {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarAttachment', className)} style={style} role="listitem" data-kind={isImage ? 'image' : 'file'} title={attachment.name}>
|
|
971
949
|
{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>
|
|
950
|
+
// <Card size={ctx.size} variant="surface">
|
|
951
|
+
<Flex align="center" gap="2" pr={!isImage ? '6' : undefined}>
|
|
952
|
+
<div className="rt-ChatbarAttachmentPreview" aria-hidden>
|
|
953
|
+
{isImage ? <img className="rt-ChatbarAttachmentImage" src={attachment.url} alt="" /> : <FileTextIcon />}
|
|
954
|
+
</div>
|
|
955
|
+
{!isImage && (
|
|
956
|
+
<Flex direction="column" gap="0" style={{ minWidth: 0 }}>
|
|
957
|
+
<Text size={ctx.size} weight="medium" style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
|
958
|
+
{attachment.name}
|
|
959
|
+
</Text>
|
|
960
|
+
<Text size="1" color="gray">
|
|
961
|
+
{Math.ceil(attachment.size / 1024)} KB
|
|
962
|
+
</Text>
|
|
963
|
+
</Flex>
|
|
964
|
+
)}
|
|
965
|
+
<IconButton
|
|
966
|
+
className="rt-ChatbarAttachmentRemove"
|
|
967
|
+
aria-label={`Remove ${attachment.name}`}
|
|
968
|
+
size="1"
|
|
969
|
+
// size={ctx.size}
|
|
970
|
+
variant="classic"
|
|
971
|
+
highContrast
|
|
972
|
+
color="gray"
|
|
973
|
+
onClick={() => ctx.setAttachments(ctx.attachments.filter((a) => a.id !== attachment.id))}
|
|
974
|
+
>
|
|
975
|
+
<CloseIcon />
|
|
976
|
+
</IconButton>
|
|
977
|
+
</Flex>
|
|
978
|
+
// </Card>
|
|
1011
979
|
)}
|
|
1012
980
|
</Comp>
|
|
1013
981
|
);
|
|
@@ -1020,51 +988,72 @@ interface AttachTriggerProps extends React.ComponentPropsWithoutRef<'button'> {
|
|
|
1020
988
|
multiple?: boolean;
|
|
1021
989
|
}
|
|
1022
990
|
|
|
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
|
-
|
|
991
|
+
const AttachTrigger = React.forwardRef<HTMLButtonElement, AttachTriggerProps>((props, forwardedRef) => {
|
|
992
|
+
const { asChild, accept, multiple, className, style, ...buttonProps } = props;
|
|
993
|
+
const ctx = useChatbarContext();
|
|
994
|
+
const inputRef = React.useRef<HTMLInputElement | null>(null);
|
|
995
|
+
const Comp = asChild ? Slot : ('button' as any);
|
|
996
|
+
// Prefer Chatbar.Root's accept when a local accept is not provided
|
|
997
|
+
const mergedAccept = accept ?? ctx.accept;
|
|
998
|
+
const actualAccept = (Array.isArray(mergedAccept) ? mergedAccept : (mergedAccept?.split(',') ?? [])).join(',');
|
|
999
|
+
React.useEffect(() => {
|
|
1000
|
+
const handleWindowFocus = () => {
|
|
1001
|
+
// Reset guard when window regains focus after file dialog closes
|
|
1002
|
+
ctx.fileDialogOpenRef.current = false;
|
|
1003
|
+
};
|
|
1004
|
+
window.addEventListener('focus', handleWindowFocus);
|
|
1005
|
+
return () => window.removeEventListener('focus', handleWindowFocus);
|
|
1006
|
+
}, [ctx.fileDialogOpenRef]);
|
|
1007
|
+
return (
|
|
1008
|
+
<>
|
|
1009
|
+
<Comp
|
|
1010
|
+
{...(buttonProps as any)}
|
|
1011
|
+
ref={forwardedRef as any}
|
|
1012
|
+
className={classNames('rt-ChatbarAttachTrigger', className)}
|
|
1013
|
+
style={style}
|
|
1014
|
+
type={buttonProps.type ?? 'button'}
|
|
1015
|
+
aria-label={buttonProps['aria-label'] ?? 'Add attachments'}
|
|
1016
|
+
onPointerDown={(e: any) => {
|
|
1017
|
+
// Set guard before blur occurs (Safari fires blur before click)
|
|
1018
|
+
ctx.fileDialogOpenRef.current = true;
|
|
1019
|
+
buttonProps.onPointerDown?.(e);
|
|
1020
|
+
}}
|
|
1021
|
+
onMouseDown={(e: any) => {
|
|
1022
|
+
// Fallback for environments without Pointer Events
|
|
1023
|
+
ctx.fileDialogOpenRef.current = true;
|
|
1024
|
+
buttonProps.onMouseDown?.(e);
|
|
1025
|
+
}}
|
|
1026
|
+
onClick={(e: any) => {
|
|
1027
|
+
// Ensure file input opens reliably by clicking it first
|
|
1028
|
+
ctx.fileDialogOpenRef.current = true;
|
|
1029
|
+
if (inputRef.current) {
|
|
1030
|
+
inputRef.current.click();
|
|
1031
|
+
}
|
|
1032
|
+
// Then call user's onClick if provided
|
|
1033
|
+
buttonProps.onClick?.(e);
|
|
1034
|
+
}}
|
|
1035
|
+
/>
|
|
1036
|
+
<input
|
|
1037
|
+
ref={inputRef}
|
|
1038
|
+
type="file"
|
|
1039
|
+
accept={actualAccept}
|
|
1040
|
+
multiple={multiple ?? ctx.multiple}
|
|
1041
|
+
tabIndex={-1}
|
|
1042
|
+
style={{ display: 'none' }}
|
|
1043
|
+
onChange={(e) => {
|
|
1044
|
+
const files = Array.from(e.currentTarget.files ?? []);
|
|
1045
|
+
if (files.length > 0) {
|
|
1046
|
+
ctx.appendFiles(files);
|
|
1047
|
+
}
|
|
1048
|
+
// File dialog closed; allow normal blur handling
|
|
1049
|
+
ctx.fileDialogOpenRef.current = false;
|
|
1050
|
+
// Reset input value to allow selecting the same file again
|
|
1051
|
+
e.currentTarget.value = '';
|
|
1052
|
+
}}
|
|
1053
|
+
/>
|
|
1054
|
+
</>
|
|
1055
|
+
);
|
|
1056
|
+
});
|
|
1068
1057
|
AttachTrigger.displayName = 'Chatbar.AttachTrigger';
|
|
1069
1058
|
interface RowProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
1070
1059
|
asChild?: boolean;
|
|
@@ -1077,12 +1066,7 @@ const Row = React.forwardRef<HTMLDivElement, RowProps>((props, forwardedRef) =>
|
|
|
1077
1066
|
if (!ctx.open) return null;
|
|
1078
1067
|
const Comp = asChild ? Slot : ('div' as any);
|
|
1079
1068
|
return (
|
|
1080
|
-
<Comp
|
|
1081
|
-
{...divProps}
|
|
1082
|
-
ref={forwardedRef}
|
|
1083
|
-
className={classNames('rt-ChatbarRow', className)}
|
|
1084
|
-
style={style}
|
|
1085
|
-
>
|
|
1069
|
+
<Comp {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarRow', className)} style={style}>
|
|
1086
1070
|
<Flex align="center" justify="between" width="100%">
|
|
1087
1071
|
{children}
|
|
1088
1072
|
</Flex>
|
|
@@ -1091,34 +1075,16 @@ const Row = React.forwardRef<HTMLDivElement, RowProps>((props, forwardedRef) =>
|
|
|
1091
1075
|
});
|
|
1092
1076
|
Row.displayName = 'Chatbar.Row';
|
|
1093
1077
|
|
|
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
|
-
);
|
|
1078
|
+
const RowStart = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>((props, forwardedRef) => {
|
|
1079
|
+
const { className, style, ...divProps } = props;
|
|
1080
|
+
return <div {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarRowStart', className)} style={style} />;
|
|
1081
|
+
});
|
|
1107
1082
|
RowStart.displayName = 'Chatbar.RowStart';
|
|
1108
1083
|
|
|
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
|
-
);
|
|
1084
|
+
const RowEnd = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<'div'>>((props, forwardedRef) => {
|
|
1085
|
+
const { className, style, ...divProps } = props;
|
|
1086
|
+
return <div {...divProps} ref={forwardedRef} className={classNames('rt-ChatbarRowEnd', className)} style={style} />;
|
|
1087
|
+
});
|
|
1122
1088
|
RowEnd.displayName = 'Chatbar.RowEnd';
|
|
1123
1089
|
|
|
1124
1090
|
interface SendProps extends Omit<IconButtonProps, 'size'> {
|
|
@@ -1127,15 +1093,7 @@ interface SendProps extends Omit<IconButtonProps, 'size'> {
|
|
|
1127
1093
|
}
|
|
1128
1094
|
|
|
1129
1095
|
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;
|
|
1096
|
+
const { asChild, clearOnSend = true, disabled, children, className, style, ...buttonProps } = props;
|
|
1139
1097
|
const ctx = useChatbarContext();
|
|
1140
1098
|
|
|
1141
1099
|
const trimmed = ctx.value.trim();
|
|
@@ -1170,29 +1128,27 @@ const Send = React.forwardRef<HTMLButtonElement, SendProps>((props, forwardedRef
|
|
|
1170
1128
|
onClick={handleClick}
|
|
1171
1129
|
aria-label={buttonProps['aria-label'] ?? 'Send'}
|
|
1172
1130
|
>
|
|
1173
|
-
{children ??
|
|
1131
|
+
{children ?? (
|
|
1132
|
+
<svg
|
|
1133
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1134
|
+
width="24"
|
|
1135
|
+
height="24"
|
|
1136
|
+
viewBox="0 0 24 24"
|
|
1137
|
+
fill="none"
|
|
1138
|
+
stroke="currentColor"
|
|
1139
|
+
strokeWidth="2"
|
|
1140
|
+
strokeLinecap="round"
|
|
1141
|
+
strokeLinejoin="round"
|
|
1142
|
+
className="lucide lucide-arrow-right-icon lucide-arrow-right"
|
|
1143
|
+
>
|
|
1144
|
+
<path d="M5 12h14" />
|
|
1145
|
+
<path d="m12 5 7 7-7 7" />
|
|
1146
|
+
</svg>
|
|
1147
|
+
)}
|
|
1174
1148
|
</IconButton>
|
|
1175
1149
|
);
|
|
1176
1150
|
});
|
|
1177
1151
|
Send.displayName = 'Chatbar.Send';
|
|
1178
1152
|
|
|
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
|
-
};
|
|
1153
|
+
export { Root, Textarea, InlineStart, InlineEnd, AttachmentsRow, Attachment, AttachTrigger, Row, RowStart, RowEnd, Send };
|
|
1154
|
+
export type { RootProps as ChatbarRootProps, TextareaProps as ChatbarTextareaProps, RowProps as ChatbarRowProps, SendProps as ChatbarSendProps, ChatbarAttachment };
|