@juspay/svelte-ui-components 2.129.0 → 2.129.1

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.
@@ -171,6 +171,7 @@
171
171
  let isServerSearch = $derived(typeof onSearchChange === 'function');
172
172
  let searchInputRef = $state<HTMLInputElement | null>(null);
173
173
  let inlineSearchInputRef = $state<{ focus: () => void } | null>(null);
174
+ let inlineSearchTriggerRef = $state<HTMLElement | null>(null);
174
175
  let isInlineSearch = $derived(searchConfig?.displayMode === 'inline');
175
176
  let isInlineSearchExpanded = $state(false);
176
177
 
@@ -191,8 +192,12 @@
191
192
  };
192
193
 
193
194
  const clearSearch = (): void => {
195
+ const wasInlineExpanded = isInlineSearchExpanded;
194
196
  updateSearch('');
195
197
  isInlineSearchExpanded = false;
198
+ if (wasInlineExpanded) {
199
+ focusInlineSearchTrigger();
200
+ }
196
201
  };
197
202
 
198
203
  const expandInlineSearch = (): void => {
@@ -200,8 +205,31 @@
200
205
  queueMicrotask(() => inlineSearchInputRef?.focus());
201
206
  };
202
207
 
208
+ // Mirror of expandInlineSearch: collapsing unmounts the input, so without an
209
+ // explicit restore the focus falls to the document and a keyboard user loses
210
+ // their place in the table. The trigger only exists once the collapse has
211
+ // rendered, hence the microtask.
212
+ const focusInlineSearchTrigger = (): void => {
213
+ queueMicrotask(() =>
214
+ inlineSearchTriggerRef?.querySelector<HTMLButtonElement>('button')?.focus()
215
+ );
216
+ };
217
+
218
+ // The expanded guard matters: collapsing unmounts the focused input, which can
219
+ // fire blur on the way out. Without it, an Escape (or close-button) collapse
220
+ // re-enters clearSearch and fires a second onSearchChange('') — a duplicate
221
+ // request on the server-search path.
203
222
  const collapseInlineSearchIfEmpty = (): void => {
204
- if (searchTerm.trim() === '') {
223
+ if (isInlineSearchExpanded && searchTerm.trim() === '') {
224
+ clearSearch();
225
+ }
226
+ };
227
+
228
+ // Escape is the expected way out of an expand-in-place search; without it the
229
+ // only exits are the close button and blurring while already empty.
230
+ const handleInlineSearchKeyDown = (event: KeyboardEvent): void => {
231
+ if (event.key === 'Escape') {
232
+ event.stopPropagation();
205
233
  clearSearch();
206
234
  }
207
235
  };
@@ -795,13 +823,14 @@
795
823
  {#if isInlineSearchExpanded}
796
824
  <Input
797
825
  bind:this={inlineSearchInputRef}
798
- bind:value={searchTerm}
826
+ value={searchTerm}
799
827
  placeholder={searchConfig?.placeholder ?? 'Search…'}
800
828
  testId={searchConfig?.testId ?? ''}
801
829
  ariaLabel={searchConfig?.placeholder ?? 'Search'}
802
830
  autoComplete="off"
803
831
  onInput={(value: string) => updateSearch(value)}
804
832
  onBlur={collapseInlineSearchIfEmpty}
833
+ onKeyDown={handleInlineSearchKeyDown}
805
834
  classes="table-inline-search-input"
806
835
  />
807
836
  <span class="table-inline-search-clear">
@@ -818,7 +847,10 @@
818
847
  </Button>
819
848
  </span>
820
849
  {:else}
821
- <span class="table-inline-search-trigger">
850
+ <span
851
+ class="table-inline-search-trigger"
852
+ bind:this={inlineSearchTriggerRef}
853
+ >
822
854
  <Button
823
855
  variant="ghost"
824
856
  iconOnly={true}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/svelte-ui-components",
3
- "version": "2.129.0",
3
+ "version": "2.129.1",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",