@juspay/svelte-ui-components 2.120.1 → 2.120.3

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.
@@ -263,7 +263,7 @@
263
263
  </span>
264
264
  {:else if typeof item.icon === 'string' && item.icon.length > 0}
265
265
  <div class="command-menu-item-icon-img-wrapper">
266
- <Img src={item.icon} alt="" />
266
+ <Img inlineSvg src={item.icon} alt="" />
267
267
  </div>
268
268
  {/if}
269
269
  <span class="command-menu-item-label">{item.label}</span>
@@ -147,6 +147,12 @@
147
147
  let isOpen: boolean = $state(false);
148
148
  let panelRef: HTMLDivElement | null = $state(null);
149
149
  let triggerRef: HTMLDivElement | null = $state(null);
150
+ // The panel anchors below the trigger by default. When the trigger sits low in
151
+ // the viewport there is no room for it there, and because the panel is
152
+ // position:absolute (not portaled, not fixed) it simply extends past the fold
153
+ // and the lower weeks become unreachable without scrolling whatever container
154
+ // it happens to live in. Flipping it above the trigger keeps it on screen.
155
+ let opensUpward: boolean = $state(false);
150
156
  let comparePanelRef: HTMLDivElement | null = $state(null);
151
157
  let compareTriggerRef: HTMLDivElement | null = $state(null);
152
158
  // Stores the element that opened the compare panel so focus can be restored on close.
@@ -307,9 +313,44 @@
307
313
 
308
314
  function closePicker(): void {
309
315
  isOpen = false;
316
+ opensUpward = false;
310
317
  onopentoggle?.({ open: false });
311
318
  }
312
319
 
320
+ /**
321
+ * Decide which side of the trigger the panel should open on, by measuring
322
+ * rather than guessing: flip up only when the panel genuinely does not fit
323
+ * below AND fits better above. A panel that overflows in both directions
324
+ * stays below, where its first weeks are at least readable.
325
+ */
326
+ function positionPanel(panelNode: HTMLDivElement | null = panelRef): void {
327
+ if (panelNode === null || triggerRef === null || typeof window === 'undefined') {
328
+ return;
329
+ }
330
+ const trigger = triggerRef.getBoundingClientRect();
331
+ const panelHeight = panelNode.offsetHeight;
332
+ const offset = 6;
333
+ const roomBelow = window.innerHeight - trigger.bottom - offset;
334
+ const roomAbove = trigger.top - offset;
335
+ opensUpward = panelHeight > roomBelow && roomAbove > roomBelow;
336
+ }
337
+
338
+ function positionPanelWhileMounted(panelNode: HTMLDivElement): { destroy: () => void } {
339
+ const updatePanelPosition = () => positionPanel(panelNode);
340
+ updatePanelPosition();
341
+ tick().then(updatePanelPosition);
342
+ window.addEventListener('resize', updatePanelPosition);
343
+ // Scrolling moves the trigger relative to the viewport, so the side that had
344
+ // room when the panel opened may not have it a moment later.
345
+ window.addEventListener('scroll', updatePanelPosition, true);
346
+ return {
347
+ destroy: () => {
348
+ window.removeEventListener('resize', updatePanelPosition);
349
+ window.removeEventListener('scroll', updatePanelPosition, true);
350
+ }
351
+ };
352
+ }
353
+
313
354
  // The trigger toggles: clicking it while the panel is already open dismisses
314
355
  // the picker instead of re-opening it onto itself. The open-only handler left
315
356
  // a second click feeling dead — the panel could only be closed by clicking away.
@@ -854,8 +895,10 @@
854
895
  <!-- Dropdown panel -->
855
896
  {#if isOpen}
856
897
  <div
898
+ use:positionPanelWhileMounted
857
899
  bind:this={panelRef}
858
900
  class="drp-panel"
901
+ class:drp-panel-above={opensUpward}
859
902
  class:drp-panel-align-left={align === 'left'}
860
903
  class:drp-panel-align-right={align === 'right'}
861
904
  role="dialog"
@@ -1261,6 +1304,12 @@
1261
1304
  overflow: hidden;
1262
1305
  }
1263
1306
 
1307
+ /* Set only when the panel would not fit below the trigger — see positionPanel(). */
1308
+ .drp-panel-above {
1309
+ top: auto;
1310
+ bottom: calc(100% + var(--drp-panel-offset, 6px));
1311
+ }
1312
+
1264
1313
  .drp-panel-align-left {
1265
1314
  left: 0;
1266
1315
  right: auto;
@@ -17,7 +17,7 @@
17
17
  {#if compact}
18
18
  <div class="file-dropzone-trigger-compact {classes ?? ''}">
19
19
  <div class="file-dropzone-trigger-icon-sm" aria-hidden="true">
20
- <Img src={icon} alt="" fallback="" classes="file-dropzone-trigger-icon-sm-img" />
20
+ <Img inlineSvg src={icon} alt="" fallback="" classes="file-dropzone-trigger-icon-sm-img" />
21
21
  </div>
22
22
  <span class="file-dropzone-trigger-heading" data-pw={testId} testID={testId}>{heading}</span>
23
23
  </div>
@@ -28,7 +28,7 @@
28
28
  {...typeof testId === 'string' ? { testId } : {}}
29
29
  >
30
30
  <div class="file-dropzone-trigger-icon" aria-hidden="true">
31
- <Img src={icon} alt="" fallback="" classes="file-dropzone-trigger-icon-img" />
31
+ <Img inlineSvg src={icon} alt="" fallback="" classes="file-dropzone-trigger-icon-img" />
32
32
  </div>
33
33
  <p class="file-dropzone-trigger-heading-wrap">
34
34
  <span class="file-dropzone-trigger-heading">{heading}</span>
@@ -215,6 +215,8 @@
215
215
  .top-section {
216
216
  display: flex;
217
217
  flex-direction: row;
218
+ align-items: var(--list-item-top-section-align-items);
219
+ gap: var(--list-item-top-section-gap);
218
220
  margin-bottom: 0;
219
221
  }
220
222
 
@@ -263,6 +265,7 @@
263
265
 
264
266
  .right-content {
265
267
  display: var(--list-item-right-content-display, flex);
268
+ flex: var(--list-item-right-content-flex);
266
269
  }
267
270
 
268
271
  .right-content-loader {
@@ -92,7 +92,8 @@
92
92
  border: var(--pill-border, none);
93
93
  cursor: var(--pill-cursor, pointer);
94
94
  max-width: var(--pill-max-width);
95
- line-height: 1;
95
+ line-height: var(--pill-line-height, 1);
96
+ flex-shrink: var(--pill-flex-shrink);
96
97
  }
97
98
 
98
99
  .pill:hover:not(.disabled) {
@@ -406,8 +406,10 @@
406
406
  >
407
407
  {#if typeof leftIcon === 'string' && leftIcon.length > 0}
408
408
  <Img
409
+ inlineSvg
409
410
  src={leftIcon}
410
411
  alt=""
412
+ fallback=""
411
413
  classes="select-left-icon"
412
414
  {...typeof leftIconTestId === 'string' ? { testId: leftIconTestId } : {}}
413
415
  />
@@ -601,7 +603,13 @@
601
603
  {/if}
602
604
  {/if}
603
605
  {#if typeof row.item.icon === 'string' && row.item.icon.length > 0}
604
- <Img src={row.item.icon} alt="" fallback="" classes="select-option-icon" />
606
+ <Img
607
+ inlineSvg
608
+ src={row.item.icon}
609
+ alt=""
610
+ fallback=""
611
+ classes="select-option-icon"
612
+ />
605
613
  {/if}
606
614
  <span class="select-option-label">{row.item.label}</span>
607
615
  {#if showSelectedTick && !multiple && value.includes(row.item.id)}
@@ -9,6 +9,8 @@ export type SelectItem = {
9
9
  * Optional image src (URL or data URI) rendered at the left of the option row,
10
10
  * before the label — for icon pickers and any list whose options carry a glyph.
11
11
  * Size is controlled by the `--select-option-icon-size` CSS variable (default 16px).
12
+ * An SVG source is inlined, so an icon drawn with `currentColor` inherits the
13
+ * option row's text colour; anything else renders as a plain `<img>`.
12
14
  * Pair with the trigger's `leftIcon` (driven by the selected option's icon) to show
13
15
  * the current selection in the closed trigger.
14
16
  */
@@ -70,7 +72,12 @@ export type OptionalSelectProperties = {
70
72
  }]>;
71
73
  /** Visual hierarchy of the trigger. `'ghost'` renders a transparent, borderless trigger — useful when the Select is embedded in a toolbar or header where a full bordered input would be visually heavy. Defaults to `'default'`. */
72
74
  hierarchy?: SelectHierarchy;
73
- /** Optional image src (URL or data URI) rendered at the left of the trigger. Size is controlled by the `--select-left-icon-size` CSS variable (default 16px). */
75
+ /**
76
+ * Optional image src (URL or data URI) rendered at the left of the trigger.
77
+ * Size is controlled by the `--select-left-icon-size` CSS variable (default 16px).
78
+ * An SVG source is inlined, so an icon drawn with `currentColor` inherits the
79
+ * trigger's text colour; anything else renders as a plain `<img>`.
80
+ */
74
81
  leftIcon?: string;
75
82
  /** `data-pw` test id forwarded to the leading icon `<Img>` element. */
76
83
  leftIconTestId?: string;
@@ -25,7 +25,7 @@
25
25
  {#if icon}
26
26
  {@render icon()}
27
27
  {:else}
28
- <Img src={statusIcon} alt="status" />
28
+ <Img inlineSvg src={statusIcon} alt="status" />
29
29
  {/if}
30
30
  </div>
31
31
  <div class="status-text">{statusText}</div>
@@ -146,7 +146,7 @@
146
146
  data-pw={column.testId ? `${column.testId}-icon-${iconIndex}` : null}
147
147
  testID={column.testId ? `${column.testId}-icon-${iconIndex}` : null}
148
148
  >
149
- <Img src={String(iconSrc)} alt="" fallback="" />
149
+ <Img inlineSvg src={String(iconSrc)} alt="" fallback="" />
150
150
  </span>
151
151
  {/each}
152
152
  <span>{typeof data.label === 'string' ? data.label : '-'}</span>
@@ -281,7 +281,7 @@
281
281
  })}
282
282
  {:else}
283
283
  {#if typeof tabItem?.icon === 'string' && tabItem.icon.length > 0}
284
- <Img src={tabItem.icon} alt="" fallback="" classes="tabs-item-icon" />
284
+ <Img inlineSvg src={tabItem.icon} alt="" fallback="" classes="tabs-item-icon" />
285
285
  {/if}
286
286
  <span class="tabs-item-label" data-text={label}>{label}</span>
287
287
  {#if tabItem?.status && tabItem.status !== 'none'}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/svelte-ui-components",
3
- "version": "2.120.1",
3
+ "version": "2.120.3",
4
4
  "description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
5
5
  "keywords": [
6
6
  "svelte",