@juspay/svelte-ui-components 2.120.0 → 2.120.2
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.
|
@@ -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;
|
|
@@ -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 {
|
package/dist/Pill/Pill.svelte
CHANGED
|
@@ -1,16 +1,54 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ProgressProperties } from './properties';
|
|
3
3
|
|
|
4
|
-
let {
|
|
4
|
+
let {
|
|
5
|
+
value,
|
|
6
|
+
max = 100,
|
|
7
|
+
showLabel = false,
|
|
8
|
+
ariaLabel,
|
|
9
|
+
testId,
|
|
10
|
+
classes
|
|
11
|
+
}: ProgressProperties = $props();
|
|
5
12
|
|
|
6
|
-
|
|
13
|
+
// `max` has to be a finite positive number for a percentage to mean
|
|
14
|
+
// anything. A zero, negative, or non-finite max (an as-yet-unloaded total,
|
|
15
|
+
// say) would make (value / max) NaN or Infinity and put "NaN" into
|
|
16
|
+
// aria-valuenow, which is not a valid ARIA value -- and a non-finite value
|
|
17
|
+
// has the same effect. Treat an unusable range as an empty (0%) bar
|
|
18
|
+
// instead. `value` is guarded here too, not just `max`: a NaN value isn't
|
|
19
|
+
// `< 0`, so it would otherwise slip past the isIndeterminate check below
|
|
20
|
+
// and hit the same NaN-percentage path. This check is deliberately kept
|
|
21
|
+
// independent of `isIndeterminate` below -- a negative `value` (e.g. -1)
|
|
22
|
+
// still signals indeterminate on its own, even paired with an invalid
|
|
23
|
+
// `max`, since indeterminate mode never reads `percentage` in the markup.
|
|
24
|
+
let hasValidRange = $derived(Number.isFinite(value) && Number.isFinite(max) && max > 0);
|
|
25
|
+
let percentage = $derived(hasValidRange ? Math.min(100, Math.max(0, (value / max) * 100)) : 0);
|
|
7
26
|
let isIndeterminate = $derived(value < 0);
|
|
27
|
+
// Rounded to 2 decimal places rather than the nearest whole number, so
|
|
28
|
+
// aria-valuenow stays effectively in step with the bar's raw fractional
|
|
29
|
+
// width (matching Gauge's convention of tying aria-valuenow to the raw
|
|
30
|
+
// computed value, not the rounded display text). Full raw precision isn't
|
|
31
|
+
// used because (value / max) * 100 can land on binary floating-point noise
|
|
32
|
+
// like 55.00000000000001 -- rounding to 2 decimals clears that while still
|
|
33
|
+
// keeping the value far closer to the bar's true width than whole numbers.
|
|
34
|
+
let preciseValueNow = $derived(Math.round(percentage * 100) / 100);
|
|
35
|
+
// Shared by the visible label and the aria-label fallback, mirroring
|
|
36
|
+
// Gauge's labelText convention. "Loading" matches the aria-label
|
|
37
|
+
// LoadingDots already uses for its own indeterminate role="status" element.
|
|
38
|
+
let labelText = $derived(isIndeterminate ? 'Loading' : `${Math.round(percentage)}%`);
|
|
8
39
|
</script>
|
|
9
40
|
|
|
10
41
|
<div
|
|
11
42
|
class="container {classes ?? ''}"
|
|
12
43
|
data-pw={typeof testId === 'string' ? testId : null}
|
|
13
44
|
testID={typeof testId === 'string' ? testId : null}
|
|
45
|
+
role="progressbar"
|
|
46
|
+
aria-valuenow={isIndeterminate ? null : preciseValueNow}
|
|
47
|
+
aria-valuemin={0}
|
|
48
|
+
aria-valuemax={100}
|
|
49
|
+
aria-valuetext={isIndeterminate ? 'indeterminate' : null}
|
|
50
|
+
aria-busy={isIndeterminate ? true : null}
|
|
51
|
+
aria-label={ariaLabel ?? labelText}
|
|
14
52
|
>
|
|
15
53
|
<div class="track">
|
|
16
54
|
<div
|
|
@@ -20,7 +58,7 @@
|
|
|
20
58
|
></div>
|
|
21
59
|
</div>
|
|
22
60
|
{#if showLabel && !isIndeterminate}
|
|
23
|
-
<div class="label">{
|
|
61
|
+
<div class="label">{labelText}</div>
|
|
24
62
|
{/if}
|
|
25
63
|
</div>
|
|
26
64
|
|
|
@@ -3,8 +3,23 @@ export type MandatoryProgressProperties = {
|
|
|
3
3
|
value: number;
|
|
4
4
|
};
|
|
5
5
|
export type OptionalProgressProperties = {
|
|
6
|
+
/**
|
|
7
|
+
* The maximum value representing 100% completion. Must be a finite,
|
|
8
|
+
* positive number for the percentage to be meaningful. A zero, negative,
|
|
9
|
+
* or non-finite `max` (and likewise a non-finite `value`) is treated as an
|
|
10
|
+
* invalid range: the bar renders at 0% rather than propagating `NaN` into
|
|
11
|
+
* `aria-valuenow` and the percentage fallback text below.
|
|
12
|
+
*/
|
|
6
13
|
max?: number;
|
|
7
14
|
showLabel?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Accessible label for the progress element (`role="progressbar"`). Falls
|
|
17
|
+
* back to the computed percentage text (e.g. `"75%"`) when determinate, or
|
|
18
|
+
* `"Loading"` when indeterminate, so assistive technology always announces
|
|
19
|
+
* a name by default. For an invalid `value`/`max` range (see `max`), the
|
|
20
|
+
* fallback reads `"0%"`.
|
|
21
|
+
*/
|
|
22
|
+
ariaLabel?: string;
|
|
8
23
|
testId?: string;
|
|
9
24
|
classes?: string;
|
|
10
25
|
};
|