@juspay/svelte-ui-components 2.80.1 → 2.80.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.
- package/dist/Badge/Badge.svelte +1 -1
- package/dist/Banner/Banner.svelte +1 -1
- package/dist/Book/Book.svelte +1 -1
- package/dist/Browser/Browser.svelte +3 -3
- package/dist/Button/Button.svelte +150 -34
- package/dist/Button/properties.d.ts +40 -1
- package/dist/Calendar/Calendar.svelte +2 -2
- package/dist/Card/Card.svelte +1 -1
- package/dist/Checkbox/Checkbox.svelte +1 -1
- package/dist/Choicebox/Choicebox.svelte +1 -1
- package/dist/ColorPicker/ColorPicker.svelte +8 -5
- package/dist/Combobox/Combobox.svelte +2 -2
- package/dist/CommandMenu/CommandMenu.svelte +3 -3
- package/dist/ContextMenu/ContextMenu.svelte +1 -1
- package/dist/DateRangePicker/DateRangePicker.svelte +9 -9
- package/dist/DualAxisBarChart/DualAxisBarChart.svelte +60 -8
- package/dist/DualAxisBarChart/properties.d.ts +26 -0
- package/dist/FileInput/FileInput.svelte +1 -1
- package/dist/GridItem/GridItem.svelte +2 -2
- package/dist/Input/Input.svelte +5 -5
- package/dist/InputButton/InputButton.svelte +6 -3
- package/dist/KeyValue/KeyValue.svelte +129 -0
- package/dist/KeyValue/KeyValue.svelte.d.ts +4 -0
- package/dist/KeyValue/properties.d.ts +52 -0
- package/dist/KeyValue/properties.js +1 -0
- package/dist/KeyboardInput/KeyboardInput.svelte +1 -1
- package/dist/Menu/Menu.svelte +1 -1
- package/dist/Modal/Modal.svelte +3 -3
- package/dist/Pagination/Pagination.svelte +1 -1
- package/dist/PieChart/PieChart.svelte +1 -1
- package/dist/Progress/Progress.svelte +2 -2
- package/dist/ProportionBar/ProportionBar.svelte +2 -2
- package/dist/SankeyChart/SankeyChart.svelte +27 -3
- package/dist/SankeyChart/properties.d.ts +1 -0
- package/dist/Select/Select.svelte +3 -3
- package/dist/Sheet/Sheet.svelte +1 -1
- package/dist/Shimmer/Shimmer.svelte +1 -1
- package/dist/Slider/Slider.svelte +2 -2
- package/dist/Snippet/Snippet.svelte +2 -2
- package/dist/SplitButton/SplitButton.svelte +8 -2
- package/dist/StatCard/StatCard.svelte +4 -4
- package/dist/Table/Table.svelte +4 -4
- package/dist/ThemeSwitcher/ThemeSwitcher.svelte +4 -4
- package/dist/Toast/Toast.svelte +1 -1
- package/dist/Tooltip/Tooltip.svelte +2 -2
- package/dist/Tooltip/tooltip-action.js +1 -1
- package/dist/_chart/ChartContainer.svelte +5 -2
- package/dist/_chart/ChartTooltip.svelte +1 -1
- package/dist/_chart/geometry.js +6 -2
- package/dist/_chart/types.d.ts +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -30,6 +30,9 @@
|
|
|
30
30
|
barRadius = 3,
|
|
31
31
|
barPadding = 0.25,
|
|
32
32
|
aspectRatio = 16 / 9,
|
|
33
|
+
minBarHeight = 2,
|
|
34
|
+
margin,
|
|
35
|
+
tooltipPortal = false,
|
|
33
36
|
tooltipSnippet,
|
|
34
37
|
onbarclick,
|
|
35
38
|
testId,
|
|
@@ -44,6 +47,8 @@
|
|
|
44
47
|
let hoveredCategoryIndex = $state<number | null>(null);
|
|
45
48
|
let mouseX = $state(0);
|
|
46
49
|
let mouseY = $state(0);
|
|
50
|
+
let mouseClientX = $state(0);
|
|
51
|
+
let mouseClientY = $state(0);
|
|
47
52
|
|
|
48
53
|
// ── Formatters ─────────────────────────────────────────────────
|
|
49
54
|
|
|
@@ -53,7 +58,13 @@
|
|
|
53
58
|
// ── Layout — wider right margin to accommodate right-axis labels ─
|
|
54
59
|
|
|
55
60
|
const dims = $derived(
|
|
56
|
-
computeChartDimensions(chartWidth, chartHeight, {
|
|
61
|
+
computeChartDimensions(chartWidth, chartHeight, {
|
|
62
|
+
top: 24,
|
|
63
|
+
right: 56,
|
|
64
|
+
bottom: 40,
|
|
65
|
+
left: 56,
|
|
66
|
+
...margin
|
|
67
|
+
})
|
|
57
68
|
);
|
|
58
69
|
|
|
59
70
|
// ── Scales ─────────────────────────────────────────────────────
|
|
@@ -152,7 +163,7 @@
|
|
|
152
163
|
const barX = bandStart + subIndex * subBandWidth + barGap;
|
|
153
164
|
const valueY = scale(value);
|
|
154
165
|
const barY = value >= 0 ? valueY : zeroY;
|
|
155
|
-
const barHeight = Math.max(
|
|
166
|
+
const barHeight = Math.max(minBarHeight, Math.abs(valueY - zeroY));
|
|
156
167
|
|
|
157
168
|
const path = roundedRectPath(barX, barY, barW, barHeight, barRadius, barRadius, 0, 0);
|
|
158
169
|
|
|
@@ -278,6 +289,22 @@
|
|
|
278
289
|
const rect = containerEl.getBoundingClientRect();
|
|
279
290
|
mouseX = event.clientX - rect.left;
|
|
280
291
|
mouseY = event.clientY - rect.top;
|
|
292
|
+
mouseClientX = event.clientX;
|
|
293
|
+
mouseClientY = event.clientY;
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Svelte action: relocates the tooltip layer to `document.body` so a `position:fixed`
|
|
298
|
+
* tooltip is never clipped by an `overflow`/scroll ancestor. Used only when
|
|
299
|
+
* `tooltipPortal` is set; `use:` actions never run during SSR.
|
|
300
|
+
*/
|
|
301
|
+
const portalToBody = (node: HTMLElement) => {
|
|
302
|
+
document.body.appendChild(node);
|
|
303
|
+
return {
|
|
304
|
+
destroy: () => {
|
|
305
|
+
node.remove();
|
|
306
|
+
}
|
|
307
|
+
};
|
|
281
308
|
};
|
|
282
309
|
|
|
283
310
|
const handleCategoryEnter = (event: MouseEvent, catIdx: number) => {
|
|
@@ -439,12 +466,31 @@
|
|
|
439
466
|
</ChartContainer>
|
|
440
467
|
|
|
441
468
|
<!-- Tooltip -->
|
|
442
|
-
{#if
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
469
|
+
{#if hoveredCategoryIndex !== null}
|
|
470
|
+
{#if tooltipPortal}
|
|
471
|
+
<!-- Portaled to <body> with fixed viewport coords so the tooltip is never
|
|
472
|
+
clipped by an overflow/scroll ancestor (e.g. a scrollable report sheet).
|
|
473
|
+
The inner elements keep their own +12/-12 offset relative to this layer. -->
|
|
474
|
+
<div
|
|
475
|
+
class="chart-tooltip-portal"
|
|
476
|
+
style="left: {mouseClientX}px; top: {mouseClientY}px;"
|
|
477
|
+
use:portalToBody
|
|
478
|
+
>
|
|
479
|
+
{#if typeof tooltipSnippet === 'function'}
|
|
480
|
+
<div class="chart-tooltip-slot" style="left: 12px; top: -12px;">
|
|
481
|
+
{@render tooltipSnippet(buildTooltipContext(hoveredCategoryIndex))}
|
|
482
|
+
</div>
|
|
483
|
+
{:else}
|
|
484
|
+
<ChartTooltip data={tooltipData} mouseX={0} mouseY={0} />
|
|
485
|
+
{/if}
|
|
486
|
+
</div>
|
|
487
|
+
{:else if typeof tooltipSnippet === 'function'}
|
|
488
|
+
<div class="chart-tooltip-slot" style="left: {mouseX + 12}px; top: {mouseY - 12}px;">
|
|
489
|
+
{@render tooltipSnippet(buildTooltipContext(hoveredCategoryIndex))}
|
|
490
|
+
</div>
|
|
491
|
+
{:else}
|
|
492
|
+
<ChartTooltip data={tooltipData} {mouseX} {mouseY} />
|
|
493
|
+
{/if}
|
|
448
494
|
{/if}
|
|
449
495
|
{:else}
|
|
450
496
|
<div class="chart-empty">No data available.</div>
|
|
@@ -513,6 +559,12 @@
|
|
|
513
559
|
pointer-events: none;
|
|
514
560
|
}
|
|
515
561
|
|
|
562
|
+
.chart-tooltip-portal {
|
|
563
|
+
position: fixed;
|
|
564
|
+
z-index: var(--chart-tooltip-z-index, 10);
|
|
565
|
+
pointer-events: none;
|
|
566
|
+
}
|
|
567
|
+
|
|
516
568
|
.chart-empty {
|
|
517
569
|
padding: var(--chart-empty-padding, 32px 24px);
|
|
518
570
|
color: var(--chart-empty-color, #9ca3af);
|
|
@@ -95,6 +95,32 @@ export type OptionalDualAxisBarChartProperties = {
|
|
|
95
95
|
testId?: string;
|
|
96
96
|
/** Extra CSS class string on the root `<div>`. */
|
|
97
97
|
classes?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Minimum rendered bar height in pixels. The default `2` keeps very small non-zero
|
|
100
|
+
* values visible, but it also paints a 2px stub for genuine zeros — set `0` so an
|
|
101
|
+
* all-zero/dormant series renders nothing (letting the consumer detect it and show
|
|
102
|
+
* an empty state instead of a row of indistinguishable baseline stubs). Default `2`.
|
|
103
|
+
*/
|
|
104
|
+
minBarHeight?: number;
|
|
105
|
+
/**
|
|
106
|
+
* Override the plot margins (px) reserved for axis titles and tick labels. Merged
|
|
107
|
+
* over the defaults `{ top: 24, right: 56, bottom: 40, left: 56 }`; widen `left`/`right`
|
|
108
|
+
* when long currency tick labels (e.g. `₹1,00,000`) would otherwise overlap the
|
|
109
|
+
* gridlines in a narrow container.
|
|
110
|
+
*/
|
|
111
|
+
margin?: {
|
|
112
|
+
top?: number;
|
|
113
|
+
right?: number;
|
|
114
|
+
bottom?: number;
|
|
115
|
+
left?: number;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Render the hover tooltip on `document.body` with `position: fixed` viewport
|
|
119
|
+
* coordinates instead of `position: absolute` inside the chart. Set `true` when the
|
|
120
|
+
* chart sits in an `overflow:auto`/scrollable container that would otherwise clip the
|
|
121
|
+
* tooltip. Default `false` (in-chart positioning, unchanged).
|
|
122
|
+
*/
|
|
123
|
+
tooltipPortal?: boolean;
|
|
98
124
|
};
|
|
99
125
|
export type DualAxisBarChartEventProperties = {
|
|
100
126
|
/**
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
justify-content: var(--file-input-justify-content, center);
|
|
147
147
|
padding: var(--file-input-padding);
|
|
148
148
|
border: var(--file-input-border);
|
|
149
|
-
border-radius: var(--file-input-radius);
|
|
149
|
+
border-radius: var(--file-input-radius, var(--radius, 4px));
|
|
150
150
|
background: var(--file-input-background);
|
|
151
151
|
gap: var(--file-input-gap);
|
|
152
152
|
text-align: var(--file-input-text-align, center);
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
width: var(--grid-item-body-width, 64px);
|
|
66
66
|
background-color: var(--grid-item-background-color, #faf9f9);
|
|
67
67
|
border: var(--grid-item-border, 1px solid #eaeaea);
|
|
68
|
-
border-radius: var(--grid-item-border-radius, 4px);
|
|
68
|
+
border-radius: var(--grid-item-border-radius, var(--radius, 4px));
|
|
69
69
|
margin: var(--grid-item-margin, 8px 0 0 0);
|
|
70
70
|
display: flex;
|
|
71
71
|
justify-content: center;
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
position: absolute;
|
|
102
102
|
inset: 0px;
|
|
103
103
|
margin: var(--grid-item-margin, 8px 0 0 0);
|
|
104
|
-
border-radius: var(--grid-item-border-radius, 4px);
|
|
104
|
+
border-radius: var(--grid-item-border-radius, var(--radius, 4px));
|
|
105
105
|
border: var(--animation-version, 32px solid #cbcccf66);
|
|
106
106
|
animation: clipperAnimation var(--loader-animation-duration, 3s) infinite linear;
|
|
107
107
|
}
|
package/dist/Input/Input.svelte
CHANGED
|
@@ -331,12 +331,12 @@
|
|
|
331
331
|
background-color: var(--input-background, white);
|
|
332
332
|
font-size: var(--input-font-size, 16px) !important;
|
|
333
333
|
font-family: var(--input-font-family, inherit);
|
|
334
|
-
border-radius: var(--input-radius, 4px);
|
|
334
|
+
border-radius: var(--input-radius, var(--radius, 4px));
|
|
335
335
|
outline: none;
|
|
336
336
|
padding: var(--input-padding, 16px);
|
|
337
337
|
font-weight: var(--input-font-weight, 500);
|
|
338
338
|
width: var(--input-width, fit-content);
|
|
339
|
-
margin: var(--input-margin,
|
|
339
|
+
margin: var(--input-margin, 0);
|
|
340
340
|
appearance: none !important;
|
|
341
341
|
-webkit-appearance: none !important; /* For Safari MWeb */
|
|
342
342
|
box-shadow: var(--input-box-shadow, 0px 1px 8px #2f537733);
|
|
@@ -364,7 +364,7 @@
|
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
.action-input {
|
|
367
|
-
border-radius: var(--input-radius, 4px
|
|
367
|
+
border-radius: var(--input-radius, var(--radius, 4px) 0 0 var(--radius, 4px));
|
|
368
368
|
box-shadow: var(--input-box-shadow, 0px 0px 0px #ffffff);
|
|
369
369
|
margin-bottom: 0;
|
|
370
370
|
}
|
|
@@ -396,7 +396,7 @@
|
|
|
396
396
|
.input-field-wrap {
|
|
397
397
|
position: relative;
|
|
398
398
|
display: block;
|
|
399
|
-
margin: var(--input-margin,
|
|
399
|
+
margin: var(--input-margin, 0);
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
.input-field-wrap > :global(textarea),
|
|
@@ -429,7 +429,7 @@
|
|
|
429
429
|
.input-icon-button:focus-visible {
|
|
430
430
|
outline: var(--input-icon-focus-outline, 2px solid var(--input-focus-border-color, #005fcc));
|
|
431
431
|
outline-offset: var(--input-icon-focus-outline-offset, 2px);
|
|
432
|
-
border-radius: var(--input-icon-focus-radius,
|
|
432
|
+
border-radius: var(--input-icon-focus-radius, var(--radius, 4px));
|
|
433
433
|
}
|
|
434
434
|
|
|
435
435
|
.input-icon-left {
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
font-size: var(--input-font-size, 16px) !important;
|
|
137
137
|
font-weight: var(--input-button-font-weight, 500);
|
|
138
138
|
margin: var(--input-button-margin);
|
|
139
|
-
border-radius: var(--input-button-radius, 4px);
|
|
139
|
+
border-radius: var(--input-button-radius, var(--radius, 4px));
|
|
140
140
|
border: var(--input-button-container-border);
|
|
141
141
|
background: var(--input-button-container-background);
|
|
142
142
|
padding: var(--input-button-container-padding);
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
.input-button {
|
|
146
146
|
display: flex;
|
|
147
147
|
align-items: center;
|
|
148
|
-
border-radius: var(--input-button-radius, 4px);
|
|
148
|
+
border-radius: var(--input-button-radius, var(--radius, 4px));
|
|
149
149
|
border: var(--input-button-border);
|
|
150
150
|
box-shadow: var(--input-button-box-shadow, 0px 1px 8px #2f537733);
|
|
151
151
|
background: var(--input-button-background);
|
|
@@ -262,7 +262,10 @@
|
|
|
262
262
|
--button-font-size: var(--right-button-font-size);
|
|
263
263
|
--button-height: var(--right-button-height, 54px);
|
|
264
264
|
--button-padding: var(--right-button-padding);
|
|
265
|
-
--button-border-radius: var(
|
|
265
|
+
--button-border-radius: var(
|
|
266
|
+
--right-button-border-radius,
|
|
267
|
+
0 var(--radius, 4px) var(--radius, 4px) 0
|
|
268
|
+
);
|
|
266
269
|
--button-width: var(--right-button-width, 100%);
|
|
267
270
|
--cursor: var(--right-button-cursor);
|
|
268
271
|
--opacity: var(--right-button-opacity);
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { KeyValueProperties, KeyValueItem } from './properties';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
items,
|
|
6
|
+
columns = 2,
|
|
7
|
+
layout = 'vertical',
|
|
8
|
+
size = 'md',
|
|
9
|
+
hideEmpty = true,
|
|
10
|
+
emptyText = '—',
|
|
11
|
+
valueSnippet,
|
|
12
|
+
labelSnippet,
|
|
13
|
+
testId,
|
|
14
|
+
classes = ''
|
|
15
|
+
}: KeyValueProperties = $props();
|
|
16
|
+
|
|
17
|
+
const isEmptyValue = (value: KeyValueItem['value']): boolean =>
|
|
18
|
+
value == null || (typeof value === 'string' && value.trim() === '');
|
|
19
|
+
|
|
20
|
+
let visibleItems = $derived(
|
|
21
|
+
hideEmpty ? items.filter((item) => !isEmptyValue(item.value)) : items
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
const displayValue = (item: KeyValueItem): string =>
|
|
25
|
+
isEmptyValue(item.value) ? emptyText : String(item.value);
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<dl
|
|
29
|
+
class="key-value key-value--{layout} key-value--{size} {classes}"
|
|
30
|
+
style="--keyvalue-columns: {columns};"
|
|
31
|
+
data-pw={testId}
|
|
32
|
+
>
|
|
33
|
+
{#each visibleItems as item, index (item.label + '-' + index)}
|
|
34
|
+
<div class="key-value-item" data-pw={item.testId}>
|
|
35
|
+
<dt class="key-value-label">
|
|
36
|
+
{#if labelSnippet}
|
|
37
|
+
{@render labelSnippet(item, index)}
|
|
38
|
+
{:else}
|
|
39
|
+
{item.label}
|
|
40
|
+
{/if}
|
|
41
|
+
</dt>
|
|
42
|
+
<dd class="key-value-value">
|
|
43
|
+
{#if valueSnippet}
|
|
44
|
+
{@render valueSnippet(item, index)}
|
|
45
|
+
{:else}
|
|
46
|
+
{displayValue(item)}
|
|
47
|
+
{/if}
|
|
48
|
+
</dd>
|
|
49
|
+
</div>
|
|
50
|
+
{/each}
|
|
51
|
+
</dl>
|
|
52
|
+
|
|
53
|
+
<style>
|
|
54
|
+
.key-value {
|
|
55
|
+
display: grid;
|
|
56
|
+
grid-template-columns: repeat(var(--keyvalue-columns, 2), minmax(0, 1fr));
|
|
57
|
+
column-gap: var(--keyvalue-column-gap, 32px);
|
|
58
|
+
row-gap: var(--keyvalue-row-gap, 16px);
|
|
59
|
+
margin: 0;
|
|
60
|
+
width: 100%;
|
|
61
|
+
/* Size-preset defaults (overridable by --keyvalue-label-size / --keyvalue-value-size).
|
|
62
|
+
The value sits 2px below the label, so the key always reads as the heavier, larger element. */
|
|
63
|
+
--_kv-label-size: 16px;
|
|
64
|
+
--_kv-value-size: 14px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.key-value--sm {
|
|
68
|
+
--_kv-label-size: 14px;
|
|
69
|
+
--_kv-value-size: 12px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.key-value--md {
|
|
73
|
+
--_kv-label-size: 16px;
|
|
74
|
+
--_kv-value-size: 14px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.key-value--lg {
|
|
78
|
+
--_kv-label-size: 18px;
|
|
79
|
+
--_kv-value-size: 16px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.key-value-item {
|
|
83
|
+
display: flex;
|
|
84
|
+
min-width: 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.key-value--vertical .key-value-item {
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
gap: var(--keyvalue-pair-gap, 4px);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.key-value--horizontal .key-value-item {
|
|
93
|
+
flex-direction: row;
|
|
94
|
+
align-items: baseline;
|
|
95
|
+
gap: var(--keyvalue-pair-gap, 12px);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.key-value-label {
|
|
99
|
+
margin: 0;
|
|
100
|
+
color: var(--keyvalue-label-color, #1a1a1a);
|
|
101
|
+
font-size: var(--keyvalue-label-size, var(--_kv-label-size, 13px));
|
|
102
|
+
font-weight: var(--keyvalue-label-weight, 600);
|
|
103
|
+
line-height: var(--keyvalue-label-line-height, 1.4);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.key-value--horizontal .key-value-label {
|
|
107
|
+
flex: 0 0 var(--keyvalue-label-width, 140px);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.key-value-value {
|
|
111
|
+
margin: 0;
|
|
112
|
+
color: var(--keyvalue-value-color, #555);
|
|
113
|
+
font-size: var(--keyvalue-value-size, var(--_kv-value-size, 16px));
|
|
114
|
+
font-weight: var(--keyvalue-value-weight, 400);
|
|
115
|
+
line-height: var(--keyvalue-value-line-height, 1.4);
|
|
116
|
+
overflow-wrap: anywhere;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.key-value--horizontal .key-value-value {
|
|
120
|
+
flex: 1 1 auto;
|
|
121
|
+
min-width: 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
@media (max-width: 600px) {
|
|
125
|
+
.key-value {
|
|
126
|
+
grid-template-columns: 1fr;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
</style>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type KeyValueProperties = MandatoryKeyValueProperties & OptionalKeyValueProperties;
|
|
3
|
+
export type KeyValueItem = {
|
|
4
|
+
/** The field label (the "key"). */
|
|
5
|
+
label: string;
|
|
6
|
+
/**
|
|
7
|
+
* The field value. May be `null`/`undefined`/empty — such items are skipped when
|
|
8
|
+
* `hideEmpty` is `true` (the default), or rendered as `emptyText` otherwise.
|
|
9
|
+
*/
|
|
10
|
+
value?: string | number | null;
|
|
11
|
+
/** Optional per-item test id, emitted as `data-pw` on the item wrapper. */
|
|
12
|
+
testId?: string;
|
|
13
|
+
};
|
|
14
|
+
export type KeyValueLayout = 'vertical' | 'horizontal';
|
|
15
|
+
/** Size preset scaling the label/value typography. Mirrors Blend's KeyValuePair sizes. */
|
|
16
|
+
export type KeyValueSize = 'sm' | 'md' | 'lg';
|
|
17
|
+
export type MandatoryKeyValueProperties = {
|
|
18
|
+
items: KeyValueItem[];
|
|
19
|
+
};
|
|
20
|
+
export type OptionalKeyValueProperties = {
|
|
21
|
+
/**
|
|
22
|
+
* Number of columns the pairs flow across (row-major). Defaults to `2`. The grid
|
|
23
|
+
* collapses to a single column below the `--keyvalue-collapse-width` breakpoint.
|
|
24
|
+
*/
|
|
25
|
+
columns?: number;
|
|
26
|
+
/**
|
|
27
|
+
* `'vertical'` (default) stacks the value beneath the label; `'horizontal'` places the
|
|
28
|
+
* label and value side-by-side (label width set by `--keyvalue-label-width`).
|
|
29
|
+
*/
|
|
30
|
+
layout?: KeyValueLayout;
|
|
31
|
+
/**
|
|
32
|
+
* Typography size preset. `'sm'` (12/14px), `'md'` (13/16px, default), `'lg'` (14/18px) —
|
|
33
|
+
* label/value font sizes respectively. Individual `--keyvalue-label-size` /
|
|
34
|
+
* `--keyvalue-value-size` overrides always take precedence over the preset.
|
|
35
|
+
*/
|
|
36
|
+
size?: KeyValueSize;
|
|
37
|
+
/**
|
|
38
|
+
* When `true` (default), items whose value is `null`, `undefined`, or an empty/whitespace
|
|
39
|
+
* string are omitted. Set `false` to render them as `emptyText` instead.
|
|
40
|
+
*/
|
|
41
|
+
hideEmpty?: boolean;
|
|
42
|
+
/** Placeholder rendered for empty values when `hideEmpty` is `false`. Defaults to `'—'`. */
|
|
43
|
+
emptyText?: string;
|
|
44
|
+
/** Custom renderer for the value cell, receiving `(item, index)`. Falls back to plain text. */
|
|
45
|
+
valueSnippet?: Snippet<[KeyValueItem, number]>;
|
|
46
|
+
/** Custom renderer for the label cell, receiving `(item, index)`. Falls back to plain text. */
|
|
47
|
+
labelSnippet?: Snippet<[KeyValueItem, number]>;
|
|
48
|
+
/** Test selector applied as the `data-pw` attribute on the root grid. */
|
|
49
|
+
testId?: string;
|
|
50
|
+
/** Additional CSS classes for theming the root grid. */
|
|
51
|
+
classes?: string;
|
|
52
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
color: var(--keyboard-input-key-color, #333333);
|
|
83
83
|
background-color: var(--keyboard-input-key-background, #f5f5f5);
|
|
84
84
|
border: var(--keyboard-input-key-border, 1px solid #d1d1d1);
|
|
85
|
-
border-radius: var(--keyboard-input-key-border-radius, 4px);
|
|
85
|
+
border-radius: var(--keyboard-input-key-border-radius, var(--radius, 4px));
|
|
86
86
|
box-shadow: var(--keyboard-input-key-box-shadow, 0 1px 0 #c4c4c4);
|
|
87
87
|
min-width: var(--keyboard-input-key-min-width, 1.6em);
|
|
88
88
|
padding: var(--keyboard-input-key-padding, 2px 6px);
|
package/dist/Menu/Menu.svelte
CHANGED
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
left: var(--menu-dropdown-left, 0);
|
|
285
285
|
background-color: var(--menu-background-color, #ffffff);
|
|
286
286
|
border: var(--menu-border, 1px solid #e0e0e0);
|
|
287
|
-
border-radius: var(--menu-border-radius,
|
|
287
|
+
border-radius: var(--menu-border-radius, var(--radius, 4px));
|
|
288
288
|
box-shadow: var(--menu-box-shadow, 0px 4px 16px rgba(0, 0, 0, 0.12));
|
|
289
289
|
min-width: var(--menu-min-width, 160px);
|
|
290
290
|
max-height: var(--menu-max-height, 240px);
|
package/dist/Modal/Modal.svelte
CHANGED
|
@@ -245,7 +245,7 @@
|
|
|
245
245
|
cursor: auto;
|
|
246
246
|
display: flex;
|
|
247
247
|
flex-direction: column;
|
|
248
|
-
border-radius: var(--modal-border-radius,
|
|
248
|
+
border-radius: var(--modal-border-radius, var(--radius, 4px));
|
|
249
249
|
overflow: var(--modal-content-overflow, auto);
|
|
250
250
|
border-top: var(--modal-content-border-top);
|
|
251
251
|
}
|
|
@@ -331,7 +331,7 @@
|
|
|
331
331
|
--button-height: var(--modal-footer-secondary-button-height, fit-content);
|
|
332
332
|
--button-padding: var(--modal-footer-secondary-button-padding, 16px);
|
|
333
333
|
--button-margin: var(--modal-footer-secondary-button-margin);
|
|
334
|
-
--button-border-radius: var(--modal-footer-secondary-button-border-radius,
|
|
334
|
+
--button-border-radius: var(--modal-footer-secondary-button-border-radius, var(--radius, 4px));
|
|
335
335
|
--button-width: var(--modal-footer-secondary-button-width, fit-content);
|
|
336
336
|
--cursor: var(--modal-footer-secondary-button-cursor, pointer);
|
|
337
337
|
--opacity: var(--modal-footer-secondary-button-opacity, 1);
|
|
@@ -372,7 +372,7 @@
|
|
|
372
372
|
--button-height: var(--modal-footer-primary-button-height, fit-content);
|
|
373
373
|
--button-padding: var(--modal-footer-primary-button-padding, 16px);
|
|
374
374
|
--button-margin: var(--modal-footer-primary-button-margin);
|
|
375
|
-
--button-border-radius: var(--modal-footer-primary-button-border-radius,
|
|
375
|
+
--button-border-radius: var(--modal-footer-primary-button-border-radius, var(--radius, 4px));
|
|
376
376
|
--button-width: var(--modal-footer-primary-button-width, fit-content);
|
|
377
377
|
--cursor: var(--modal-footer-primary-button-cursor, pointer);
|
|
378
378
|
--opacity: var(--modal-footer-primary-button-opacity, 1);
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
color: var(--pagination-button-color, #3a4550);
|
|
142
142
|
background: var(--pagination-button-background, transparent);
|
|
143
143
|
border: var(--pagination-button-border, 1px solid #d1d5db);
|
|
144
|
-
border-radius: var(--pagination-button-border-radius, 4px);
|
|
144
|
+
border-radius: var(--pagination-button-border-radius, var(--radius, 4px));
|
|
145
145
|
cursor: var(--pagination-button-cursor, pointer);
|
|
146
146
|
min-width: var(--pagination-button-min-width, 36px);
|
|
147
147
|
height: var(--pagination-button-height, 36px);
|
|
@@ -372,7 +372,7 @@
|
|
|
372
372
|
display: inline-block;
|
|
373
373
|
width: var(--chart-legend-swatch-size, 12px);
|
|
374
374
|
height: var(--chart-legend-swatch-size, 12px);
|
|
375
|
-
border-radius: var(--piechart-legend-swatch-radius,
|
|
375
|
+
border-radius: var(--piechart-legend-swatch-radius, var(--radius, 4px));
|
|
376
376
|
flex-shrink: 0;
|
|
377
377
|
}
|
|
378
378
|
.pie-legend-label {
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
flex: 1;
|
|
34
34
|
height: var(--progress-track-height, 8px);
|
|
35
35
|
background: var(--progress-track-background, #e0e0e0);
|
|
36
|
-
border-radius: var(--progress-track-border-radius, 4px);
|
|
36
|
+
border-radius: var(--progress-track-border-radius, var(--radius, 4px));
|
|
37
37
|
overflow: hidden;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
.bar {
|
|
41
41
|
height: 100%;
|
|
42
42
|
background: var(--progress-bar-background, #2196f3);
|
|
43
|
-
border-radius: var(--progress-bar-border-radius, 4px);
|
|
43
|
+
border-radius: var(--progress-bar-border-radius, var(--radius, 4px));
|
|
44
44
|
transition: var(--progress-bar-transition, width 0.3s ease);
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -159,7 +159,7 @@
|
|
|
159
159
|
.proportion-bar-track {
|
|
160
160
|
width: 100%;
|
|
161
161
|
height: var(--proportion-bar-track-height, 10px);
|
|
162
|
-
border-radius: var(--proportion-bar-track-border-radius, 4px);
|
|
162
|
+
border-radius: var(--proportion-bar-track-border-radius, var(--radius, 4px));
|
|
163
163
|
overflow: hidden;
|
|
164
164
|
background: var(--proportion-bar-track-bg, #f0f0f0);
|
|
165
165
|
}
|
|
@@ -189,7 +189,7 @@
|
|
|
189
189
|
display: inline-block;
|
|
190
190
|
width: var(--proportion-bar-swatch-size, 10px);
|
|
191
191
|
height: var(--proportion-bar-swatch-size, 10px);
|
|
192
|
-
border-radius: var(--proportion-bar-swatch-border-radius,
|
|
192
|
+
border-radius: var(--proportion-bar-swatch-border-radius, var(--radius, 4px));
|
|
193
193
|
flex-shrink: 0;
|
|
194
194
|
}
|
|
195
195
|
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
showValues = false,
|
|
19
19
|
showLabels = true,
|
|
20
20
|
aspectRatio = 16 / 9,
|
|
21
|
+
maxHeight = Infinity,
|
|
21
22
|
valueFormat,
|
|
22
23
|
tooltipSnippet,
|
|
23
24
|
empty,
|
|
@@ -89,6 +90,26 @@
|
|
|
89
90
|
: (Math.max(0, chartWidth - MARGIN * 2) - nodeWidth) / (columnCount - 1)
|
|
90
91
|
);
|
|
91
92
|
|
|
93
|
+
// Node labels render alongside their bar; long labels used to overflow into the
|
|
94
|
+
// next column and collide. Clip each to the horizontal room its column actually
|
|
95
|
+
// has and append an ellipsis (the full text stays available via the node tooltip).
|
|
96
|
+
const LABEL_CHAR_PX = 7.2; // ≈ 0.6em at the 12px default label size
|
|
97
|
+
const truncateLabel = (text: string, column: number): string => {
|
|
98
|
+
const available =
|
|
99
|
+
column === 0
|
|
100
|
+
? MARGIN + 16
|
|
101
|
+
: column === columnCount - 1
|
|
102
|
+
? Math.max(colWidth, MARGIN + 24)
|
|
103
|
+
: Math.max(0, colWidth - nodeWidth - 12);
|
|
104
|
+
const maxChars = Math.floor(available / LABEL_CHAR_PX);
|
|
105
|
+
// No usable room — hide the label rather than force text that would overflow;
|
|
106
|
+
// the full text is still reachable via the node's <title> on hover.
|
|
107
|
+
if (maxChars < 3) {
|
|
108
|
+
return '';
|
|
109
|
+
}
|
|
110
|
+
return text.length > maxChars ? text.slice(0, maxChars - 1) + '…' : text;
|
|
111
|
+
};
|
|
112
|
+
|
|
92
113
|
// ── Helpers ────────────────────────────────────────────────────
|
|
93
114
|
|
|
94
115
|
/** Percentage of source node's total value carried by a link (0–100, 2 dp). */
|
|
@@ -276,7 +297,7 @@
|
|
|
276
297
|
{#if isEmpty && typeof empty === 'function'}
|
|
277
298
|
<div class="chart-empty">{@render empty()}</div>
|
|
278
299
|
{:else}
|
|
279
|
-
<ChartContainer bind:width={chartWidth} bind:height={chartHeight} {aspectRatio}>
|
|
300
|
+
<ChartContainer bind:width={chartWidth} bind:height={chartHeight} {aspectRatio} {maxHeight}>
|
|
280
301
|
<g transform="translate({MARGIN}, {MARGIN})">
|
|
281
302
|
{#if columnLabels != null && columnLabels.length > 0}
|
|
282
303
|
{#each columnLabels.slice(0, columnCount) as label, ci (ci)}
|
|
@@ -340,8 +361,11 @@
|
|
|
340
361
|
y={node.y + node.height / 2}
|
|
341
362
|
text-anchor={node.column === 0 ? 'end' : 'start'}
|
|
342
363
|
dominant-baseline="middle"
|
|
343
|
-
>{
|
|
344
|
-
({format(node.value)})
|
|
364
|
+
>{truncateLabel(
|
|
365
|
+
showValues ? `${node.label} (${format(node.value)})` : node.label,
|
|
366
|
+
node.column
|
|
367
|
+
)}<title>{showValues ? `${node.label} (${format(node.value)})` : node.label}</title
|
|
368
|
+
></text
|
|
345
369
|
>
|
|
346
370
|
{/if}
|
|
347
371
|
{/each}
|
|
@@ -516,7 +516,7 @@
|
|
|
516
516
|
padding: var(--select-trigger-padding, 8px 12px);
|
|
517
517
|
background: var(--select-trigger-background, #ffffff);
|
|
518
518
|
border: var(--select-trigger-border, 1px solid #cccccc);
|
|
519
|
-
border-radius: var(--select-trigger-border-radius,
|
|
519
|
+
border-radius: var(--select-trigger-border-radius, var(--radius, 4px));
|
|
520
520
|
cursor: pointer;
|
|
521
521
|
outline: none;
|
|
522
522
|
-webkit-tap-highlight-color: transparent;
|
|
@@ -603,7 +603,7 @@
|
|
|
603
603
|
margin-top: var(--select-dropdown-gap, 4px);
|
|
604
604
|
background: var(--select-dropdown-background, #ffffff);
|
|
605
605
|
border: var(--select-dropdown-border, 1px solid #cccccc);
|
|
606
|
-
border-radius: var(--select-dropdown-border-radius,
|
|
606
|
+
border-radius: var(--select-dropdown-border-radius, var(--radius, 4px));
|
|
607
607
|
box-shadow: var(--select-dropdown-shadow, 0 4px 12px rgba(0, 0, 0, 0.1));
|
|
608
608
|
max-height: var(--select-dropdown-max-height, 200px);
|
|
609
609
|
overflow-y: auto;
|
|
@@ -659,7 +659,7 @@
|
|
|
659
659
|
width: var(--select-option-indicator-size, 18px);
|
|
660
660
|
height: var(--select-option-indicator-size, 18px);
|
|
661
661
|
border: var(--select-option-indicator-border, 2px solid #757575);
|
|
662
|
-
border-radius: var(--select-option-indicator-border-radius,
|
|
662
|
+
border-radius: var(--select-option-indicator-border-radius, var(--radius, 4px));
|
|
663
663
|
background-color: var(--select-option-indicator-background, transparent);
|
|
664
664
|
color: var(--select-option-indicator-color, currentColor);
|
|
665
665
|
transition:
|
package/dist/Sheet/Sheet.svelte
CHANGED
|
@@ -248,7 +248,7 @@
|
|
|
248
248
|
--button-width: var(--sheet-close-button-size, 32px);
|
|
249
249
|
--button-height: var(--sheet-close-button-size, 32px);
|
|
250
250
|
--button-border: none;
|
|
251
|
-
--button-border-radius: var(--sheet-close-button-border-radius, 4px);
|
|
251
|
+
--button-border-radius: var(--sheet-close-button-border-radius, var(--radius, 4px));
|
|
252
252
|
--button-color: var(--sheet-close-button-background, transparent);
|
|
253
253
|
--button-text-color: var(--sheet-close-button-color, #666666);
|
|
254
254
|
--button-font-size: var(--sheet-close-button-font-size, 16px);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
.shimmer {
|
|
11
11
|
width: var(--shimmer-width, 100%);
|
|
12
12
|
height: var(--shimmer-height, 16px);
|
|
13
|
-
border-radius: var(--shimmer-border-radius, 4px);
|
|
13
|
+
border-radius: var(--shimmer-border-radius, var(--radius, 4px));
|
|
14
14
|
background-color: var(--shimmer-background, #e0e0e0);
|
|
15
15
|
opacity: var(--shimmer-opacity, 1);
|
|
16
16
|
overflow: hidden;
|