@juspay/svelte-ui-components 4.35.1 → 4.36.0

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.
@@ -60,6 +60,7 @@
60
60
  actionLabel = 'Voice conversation',
61
61
  actionText,
62
62
  leading,
63
+ trailing,
63
64
  statusText,
64
65
  statusTestId,
65
66
  slashCommands = null,
@@ -71,6 +72,7 @@
71
72
  slashItem,
72
73
  onsubmit,
73
74
  oninput,
75
+ oninputfocus,
74
76
  onkeydown,
75
77
  onpaste,
76
78
  onstop,
@@ -92,6 +94,11 @@
92
94
  }: ChatComposerProperties = $props();
93
95
 
94
96
  let fileInput: HTMLInputElement | null = $state(null);
97
+ let inputElement: HTMLTextAreaElement | null = $state(null);
98
+
99
+ export const focus = (options?: FocusOptions): void => {
100
+ inputElement?.focus(options);
101
+ };
95
102
 
96
103
  let showVoice = $derived(typeof onvoice === 'function');
97
104
  let showAttach = $derived(typeof onattach === 'function' || typeof onattachclick === 'function');
@@ -391,6 +398,7 @@
391
398
  {#snippet textInput()}
392
399
  <textarea
393
400
  class="input"
401
+ bind:this={inputElement}
394
402
  data-pw={inputTestId ?? null}
395
403
  bind:value
396
404
  {placeholder}
@@ -404,6 +412,7 @@
404
412
  aria-activedescendant={slashActiveId}
405
413
  maxlength={maxLength > 0 ? maxLength : null}
406
414
  oninput={handleInput}
415
+ onfocus={(event) => oninputfocus?.(event)}
407
416
  onkeydown={handleKeydown}
408
417
  onpaste={(event) => onpaste?.(event)}
409
418
  use:autoGrow={value}
@@ -480,6 +489,9 @@
480
489
  </Button>
481
490
  </div>
482
491
  {/if}
492
+ {#if typeof trailing === 'function'}
493
+ {@render trailing()}
494
+ {/if}
483
495
  {/snippet}
484
496
 
485
497
  <div
@@ -1,4 +1,6 @@
1
1
  import type { ChatComposerProperties } from './properties';
2
- declare const ChatComposer: import("svelte").Component<ChatComposerProperties, {}, "value" | "attachments">;
2
+ declare const ChatComposer: import("svelte").Component<ChatComposerProperties, {
3
+ focus: (options?: FocusOptions) => void;
4
+ }, "value" | "attachments">;
3
5
  type ChatComposer = ReturnType<typeof ChatComposer>;
4
6
  export default ChatComposer;
@@ -85,6 +85,8 @@ export type OptionalChatComposerProperties = {
85
85
  /** Optional visible text beside the idle action icon; omitted keeps icon-only markup. */
86
86
  actionText?: string;
87
87
  leading?: Snippet;
88
+ /** Additional controls after the send/stop control. */
89
+ trailing?: Snippet;
88
90
  /**
89
91
  * Opt-in sr-only status region (`role="status" aria-live="polite"`) for
90
92
  * narrating composer state changes -- e.g. "Recording. Press Escape to stop
@@ -145,6 +147,8 @@ export type ChatComposerEventProperties = {
145
147
  */
146
148
  onsubmit?: (value: string, attachments: File[]) => boolean | void | Promise<boolean | void>;
147
149
  oninput?: (value: string, event: Event) => void;
150
+ /** Textarea focus, named separately from the native HTMLElement onfocus accessor. */
151
+ oninputfocus?: (event: FocusEvent) => void;
148
152
  onkeydown?: (event: KeyboardEvent) => void;
149
153
  onpaste?: (event: ClipboardEvent) => void;
150
154
  onstop?: () => void;
@@ -2,6 +2,8 @@
2
2
  import Button from '../Button/Button.svelte';
3
3
  import Img from '../Img/Img.svelte';
4
4
  import closeSvg from '../assets/close.svg?raw';
5
+ import fullscreenSvg from '../assets/fullscreen.svg?raw';
6
+ import exitFullscreenSvg from '../assets/exit-fullscreen.svg?raw';
5
7
  import type { ChatHeaderProperties } from './properties';
6
8
 
7
9
  let {
@@ -11,6 +13,11 @@
11
13
  imageAlt = '',
12
14
  avatar,
13
15
  actions,
16
+ expanded = $bindable(false),
17
+ showExpand,
18
+ expandLabel = 'Expand',
19
+ collapseLabel = 'Collapse',
20
+ onexpandchange,
14
21
  closeIcon,
15
22
  closeLabel = 'Close',
16
23
  showClose,
@@ -21,6 +28,12 @@
21
28
  }: ChatHeaderProperties = $props();
22
29
 
23
30
  let displayClose = $derived(showClose ?? typeof onclose === 'function');
31
+ let displayExpand = $derived(showExpand ?? typeof onexpandchange === 'function');
32
+
33
+ const toggleExpanded = (): void => {
34
+ expanded = !expanded;
35
+ onexpandchange?.(expanded);
36
+ };
24
37
  </script>
25
38
 
26
39
  <header
@@ -53,6 +66,18 @@
53
66
  {#if typeof actions === 'function'}
54
67
  {@render actions()}
55
68
  {/if}
69
+ {#if displayExpand}
70
+ <div class="expand">
71
+ <Button
72
+ variant="ghost"
73
+ onclick={toggleExpanded}
74
+ ariaLabel={expanded ? collapseLabel : expandLabel}
75
+ >
76
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
77
+ {@html expanded ? exitFullscreenSvg : fullscreenSvg}
78
+ </Button>
79
+ </div>
80
+ {/if}
56
81
  {#if displayClose}
57
82
  <div class="close">
58
83
  <Button onclick={() => onclose?.()} ariaLabel={closeLabel}>
@@ -166,4 +191,21 @@
166
191
  height: 100%;
167
192
  width: 100%;
168
193
  }
194
+
195
+ .expand {
196
+ --button-width: var(--chat-header-expand-size, var(--chat-header-close-size, 36px));
197
+ --button-height: var(--chat-header-expand-size, var(--chat-header-close-size, 36px));
198
+ --button-padding: var(--chat-header-expand-padding, var(--chat-header-close-padding, 8px));
199
+ --button-border-radius: var(--chat-header-expand-border-radius, 50%);
200
+ --button-color: var(--chat-header-expand-background-color, transparent);
201
+ --button-text-color: var(--chat-header-expand-color, #52525b);
202
+ --button-hover-text-color: var(--chat-header-expand-color, #52525b);
203
+ --button-content-gap: 0px;
204
+ --button-hover-color: var(--chat-header-expand-hover-background-color, #f4f4f5);
205
+ }
206
+
207
+ .expand :global(svg) {
208
+ height: 100%;
209
+ width: 100%;
210
+ }
169
211
  </style>
@@ -1,4 +1,4 @@
1
1
  import type { ChatHeaderProperties } from './properties';
2
- declare const ChatHeader: import("svelte").Component<ChatHeaderProperties, {}, "">;
2
+ declare const ChatHeader: import("svelte").Component<ChatHeaderProperties, {}, "expanded">;
3
3
  type ChatHeader = ReturnType<typeof ChatHeader>;
4
4
  export default ChatHeader;
@@ -7,6 +7,10 @@ export type OptionalChatHeaderProperties = {
7
7
  imageAlt?: string;
8
8
  avatar?: Snippet;
9
9
  actions?: Snippet;
10
+ expanded?: boolean;
11
+ showExpand?: boolean;
12
+ expandLabel?: string;
13
+ collapseLabel?: string;
10
14
  closeIcon?: Snippet;
11
15
  closeLabel?: string;
12
16
  showClose?: boolean;
@@ -16,4 +20,5 @@ export type OptionalChatHeaderProperties = {
16
20
  };
17
21
  export type ChatHeaderEventProperties = {
18
22
  onclose?: () => void;
23
+ onexpandchange?: (expanded: boolean) => void;
19
24
  };
@@ -88,8 +88,13 @@
88
88
  event.preventDefault();
89
89
  const startW = clampWidth(measuredWidth());
90
90
  const startH = clampHeight(measuredHeight());
91
- width = startW;
92
- height = startH;
91
+ const direction = DIRS[edge];
92
+ if (direction.x !== 0) {
93
+ width = startW;
94
+ }
95
+ if (direction.y !== 0) {
96
+ height = startH;
97
+ }
93
98
  active = {
94
99
  edge,
95
100
  pointerId: event.pointerId,
@@ -147,8 +152,12 @@
147
152
 
148
153
  if (handled) {
149
154
  event.preventDefault();
150
- width = nextWidth;
151
- height = nextHeight;
155
+ if (dir.x !== 0 && (event.key === 'ArrowRight' || event.key === 'ArrowLeft')) {
156
+ width = nextWidth;
157
+ }
158
+ if (dir.y !== 0 && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
159
+ height = nextHeight;
160
+ }
152
161
  onresize?.({ width: nextWidth, height: nextHeight });
153
162
  }
154
163
  }
@@ -167,6 +167,8 @@
167
167
  --chat-header-subtitle-color: #9ca3af;
168
168
  --chat-header-close-color: #9ca3af;
169
169
  --chat-header-close-hover-background-color: #252535;
170
+ --chat-header-expand-color: #9ca3af;
171
+ --chat-header-expand-hover-background-color: #252535;
170
172
 
171
173
  /* ChatMessageList */
172
174
  --chat-message-list-jump-background-color: #1e1e2e;