@juspay/svelte-ui-components 2.89.0 → 2.89.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.
- package/dist/Modal/Modal.svelte +13 -0
- package/dist/SankeyChart/SankeyChart.svelte +131 -32
- package/dist/Select/Select.svelte +2 -0
- package/dist/Table/BuiltinCell.svelte +1 -0
- package/dist/Table/Table.svelte +22 -4
- package/dist/Table/properties.d.ts +11 -0
- package/dist/Tabs/Tabs.svelte +29 -8
- package/dist/Tooltip/Tooltip.svelte +5 -1
- package/dist/Tooltip/properties.d.ts +3 -1
- package/package.json +1 -1
package/dist/Modal/Modal.svelte
CHANGED
|
@@ -263,10 +263,23 @@
|
|
|
263
263
|
border-radius: var(--modal-border-radius, var(--radius, 4px));
|
|
264
264
|
overflow: var(--modal-content-overflow, auto);
|
|
265
265
|
border-top: var(--modal-content-border-top);
|
|
266
|
+
/* Viewport containment for every size class: only .fit-content used to carry
|
|
267
|
+
a max-height, so a size whose height var is overridden to fit-content (or
|
|
268
|
+
anything taller than the screen) grew past the viewport and pushed its
|
|
269
|
+
footer and bottom rounding off-screen. dvh tracks the real visible
|
|
270
|
+
viewport on mobile; the vh line is the fallback for engines without dvh. */
|
|
271
|
+
max-height: var(--modal-max-height, calc(100vh - 32px));
|
|
272
|
+
max-height: var(--modal-max-height, calc(100dvh - 32px));
|
|
266
273
|
}
|
|
267
274
|
|
|
268
275
|
.slot-content {
|
|
269
276
|
display: var(--modal-display, flex);
|
|
277
|
+
/* Flex children default to min-height: auto and refuse to shrink below their
|
|
278
|
+
content, which defeats the overflow-y scroll once modal-content is
|
|
279
|
+
height-capped — the content spills instead of scrolling and the footer is
|
|
280
|
+
pushed out. 0 lets the slot shrink so its own scrollbar engages and the
|
|
281
|
+
header/footer stay pinned inside the viewport. */
|
|
282
|
+
min-height: var(--modal-slot-content-min-height, 0);
|
|
270
283
|
overflow-y: var(--modal-overflow-y, scroll);
|
|
271
284
|
scrollbar-width: var(--modal-scrollbar-width, none);
|
|
272
285
|
padding: var(--modal-content-padding, 0);
|
|
@@ -53,6 +53,53 @@
|
|
|
53
53
|
let isEmpty = $derived(nodes.length === 0);
|
|
54
54
|
const MARGIN = 40;
|
|
55
55
|
const LABEL_CHAR_PX = 7.2; // ≈ 0.6em at the 12px default label size
|
|
56
|
+
// A 12px label's rendered line box measures ~16px (≈1.33em) across common
|
|
57
|
+
// font stacks; two label centres closer than this overlap visibly.
|
|
58
|
+
const LABEL_LINE_PX = 16;
|
|
59
|
+
|
|
60
|
+
// Per-character width estimate at the 12px default label size. A flat
|
|
61
|
+
// 7.2px/char average underestimates uppercase-heavy labels ("OTP SKIPPED
|
|
62
|
+
// (1,234)" is ~8.2px/char), so "truncated" labels still overflowed their
|
|
63
|
+
// budget and slid under the next column's node bar.
|
|
64
|
+
const estimateCharWidth = (ch: string): number => {
|
|
65
|
+
if (/[mwMW@]/.test(ch)) {
|
|
66
|
+
return 10.6;
|
|
67
|
+
}
|
|
68
|
+
if (/[A-Z0-9_#%&]/.test(ch)) {
|
|
69
|
+
return 8.2;
|
|
70
|
+
}
|
|
71
|
+
if (/[iljtfr.,:;'’()[\]!|]/.test(ch)) {
|
|
72
|
+
return 3.6;
|
|
73
|
+
}
|
|
74
|
+
if (ch === ' ') {
|
|
75
|
+
return 3.8;
|
|
76
|
+
}
|
|
77
|
+
return 6.6;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const estimateTextWidth = (text: string): number => {
|
|
81
|
+
let width = 0;
|
|
82
|
+
for (const ch of text) {
|
|
83
|
+
width += estimateCharWidth(ch);
|
|
84
|
+
}
|
|
85
|
+
return width;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Trim `text` (appending an ellipsis) until its estimated width fits
|
|
89
|
+
// `available` px. Returns '' when even 3 chars + ellipsis cannot fit —
|
|
90
|
+
// callers hide the label and rely on the <title> tooltip instead.
|
|
91
|
+
const fitTextToWidth = (text: string, available: number): string => {
|
|
92
|
+
if (estimateTextWidth(text) <= available) {
|
|
93
|
+
return text;
|
|
94
|
+
}
|
|
95
|
+
for (let keep = text.length - 1; keep >= 3; keep--) {
|
|
96
|
+
const candidate = text.slice(0, keep) + '…';
|
|
97
|
+
if (estimateTextWidth(candidate) <= available) {
|
|
98
|
+
return candidate;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return '';
|
|
102
|
+
};
|
|
56
103
|
|
|
57
104
|
// Final-column labels render to the RIGHT of their node; the bare 40px margin is
|
|
58
105
|
// nowhere near enough for real funnel labels ("PARTIALLY_FAILED (1,234)"), so they
|
|
@@ -70,9 +117,10 @@
|
|
|
70
117
|
if (sinkLabels.length === 0) {
|
|
71
118
|
return 0;
|
|
72
119
|
}
|
|
73
|
-
const
|
|
74
|
-
Math.max(...sinkLabels.map((label) => label
|
|
75
|
-
|
|
120
|
+
const longestPx =
|
|
121
|
+
Math.max(...sinkLabels.map((label) => estimateTextWidth(label))) +
|
|
122
|
+
(showValues ? 9 * LABEL_CHAR_PX : 0);
|
|
123
|
+
const wanted = longestPx + 10 + dataLabelOffsetX;
|
|
76
124
|
// Cap the reservation so labels can never squeeze the diagram below 3/4 width,
|
|
77
125
|
// and floor at 0 — a negative dataLabelOffsetX must not inflate the plot
|
|
78
126
|
// past the right margin.
|
|
@@ -127,29 +175,71 @@
|
|
|
127
175
|
// column count grow; untruncated they collide into one unreadable run. Clip to
|
|
128
176
|
// the column pitch with an ellipsis — the full text stays on the <title>.
|
|
129
177
|
const truncateColumnLabel = (text: string): string => {
|
|
130
|
-
|
|
131
|
-
if (maxChars < 3) {
|
|
132
|
-
return '';
|
|
133
|
-
}
|
|
134
|
-
return text.length > maxChars ? text.slice(0, maxChars - 1) + '…' : text;
|
|
178
|
+
return fitTextToWidth(text, Math.max(0, colWidth - 6));
|
|
135
179
|
};
|
|
136
180
|
|
|
137
181
|
const truncateLabel = (text: string, column: number): string => {
|
|
182
|
+
// Middle columns must budget for dataLabelOffsetX too: the label starts at
|
|
183
|
+
// node.x + nodeWidth + 6 + dataLabelOffsetX, so the room before the next
|
|
184
|
+
// column's bar shrinks by the same offset. Omitting it let "fitting"
|
|
185
|
+
// labels run under the neighbouring column's node rect.
|
|
186
|
+
// First-column labels anchor `end` at node.x - 6 - offset with node.x = 0,
|
|
187
|
+
// so their room is exactly the left margin minus that inset — budgeting
|
|
188
|
+
// more pushes long labels past the SVG's left edge, where they clip.
|
|
138
189
|
const available =
|
|
139
190
|
column === 0
|
|
140
|
-
? MARGIN
|
|
191
|
+
? Math.max(0, MARGIN - 6 - dataLabelOffsetX)
|
|
141
192
|
: column === columnCount - 1
|
|
142
193
|
? Math.max(0, lastColumnLabelGutter + MARGIN - 6 - dataLabelOffsetX)
|
|
143
|
-
: Math.max(0, colWidth - nodeWidth - 12);
|
|
144
|
-
const maxChars = Math.floor(available / LABEL_CHAR_PX);
|
|
194
|
+
: Math.max(0, colWidth - nodeWidth - 12 - dataLabelOffsetX);
|
|
145
195
|
// No usable room — hide the label rather than force text that would overflow;
|
|
146
196
|
// the full text is still reachable via the node's <title> on hover.
|
|
147
|
-
|
|
148
|
-
return '';
|
|
149
|
-
}
|
|
150
|
-
return text.length > maxChars ? text.slice(0, maxChars - 1) + '…' : text;
|
|
197
|
+
return fitTextToWidth(text, available);
|
|
151
198
|
};
|
|
152
199
|
|
|
200
|
+
// Vertical label de-collision: labels sit at each node's centre-y, so two
|
|
201
|
+
// small stacked nodes in a crowded column render their 12px labels on top of
|
|
202
|
+
// each other. Per column, walk labels top-to-bottom and drop the label of
|
|
203
|
+
// the smaller-value node whenever two centres come closer than one label
|
|
204
|
+
// line — the hidden label's text stays reachable via the node's <title>.
|
|
205
|
+
let collidingLabels = $derived.by(() => {
|
|
206
|
+
const hidden = new SvelteSet<string>();
|
|
207
|
+
if (!showLabels) {
|
|
208
|
+
return hidden;
|
|
209
|
+
}
|
|
210
|
+
const byColumn = new SvelteMap<number, typeof layout.nodes>();
|
|
211
|
+
for (const node of layout.nodes) {
|
|
212
|
+
const bucket = byColumn.get(node.column);
|
|
213
|
+
if (bucket) {
|
|
214
|
+
bucket.push(node);
|
|
215
|
+
} else {
|
|
216
|
+
byColumn.set(node.column, [node]);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
for (const columnNodes of byColumn.values()) {
|
|
220
|
+
const sorted = [...columnNodes].sort((a, b) => a.y + a.height / 2 - (b.y + b.height / 2));
|
|
221
|
+
let lastKept: (typeof sorted)[number] | null = null;
|
|
222
|
+
for (const node of sorted) {
|
|
223
|
+
if (lastKept === null) {
|
|
224
|
+
lastKept = node;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const centerGap = node.y + node.height / 2 - (lastKept.y + lastKept.height / 2);
|
|
228
|
+
if (centerGap < LABEL_LINE_PX) {
|
|
229
|
+
if (node.value > lastKept.value) {
|
|
230
|
+
hidden.add(lastKept.id);
|
|
231
|
+
lastKept = node;
|
|
232
|
+
} else {
|
|
233
|
+
hidden.add(node.id);
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
lastKept = node;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return hidden;
|
|
241
|
+
});
|
|
242
|
+
|
|
153
243
|
// ── Helpers ────────────────────────────────────────────────────
|
|
154
244
|
|
|
155
245
|
/** Percentage of source node's total value carried by a link (0–100, 2 dp). */
|
|
@@ -391,24 +481,33 @@
|
|
|
391
481
|
onmouseleave={handleNodeLeave}
|
|
392
482
|
onclick={() => handleNodeClick(node.id)}
|
|
393
483
|
/>
|
|
394
|
-
{#if showLabels}
|
|
395
|
-
<text
|
|
396
|
-
class="sankey-label"
|
|
397
|
-
class:node-dimmed={dimmed}
|
|
398
|
-
x={node.column === 0
|
|
399
|
-
? node.x - 6 - dataLabelOffsetX
|
|
400
|
-
: node.x + node.width + 6 + dataLabelOffsetX}
|
|
401
|
-
y={node.y + node.height / 2}
|
|
402
|
-
text-anchor={node.column === 0 ? 'end' : 'start'}
|
|
403
|
-
dominant-baseline="middle"
|
|
404
|
-
>{truncateLabel(
|
|
405
|
-
showValues ? `${node.label} (${format(node.value)})` : node.label,
|
|
406
|
-
node.column
|
|
407
|
-
)}<title>{showValues ? `${node.label} (${format(node.value)})` : node.label}</title
|
|
408
|
-
></text
|
|
409
|
-
>
|
|
410
|
-
{/if}
|
|
411
484
|
{/each}
|
|
485
|
+
|
|
486
|
+
<!-- Labels render in a second pass, after every node rect: within one
|
|
487
|
+
interleaved loop a label could be over-painted by a later column's
|
|
488
|
+
bar whenever the width estimate ran short. -->
|
|
489
|
+
{#if showLabels}
|
|
490
|
+
{#each layout.nodes as node, ni (ni)}
|
|
491
|
+
{@const dimmed = connectedNodes !== null && !connectedNodes.has(node.id)}
|
|
492
|
+
{#if !collidingLabels.has(node.id)}
|
|
493
|
+
<text
|
|
494
|
+
class="sankey-label"
|
|
495
|
+
class:node-dimmed={dimmed}
|
|
496
|
+
x={node.column === 0
|
|
497
|
+
? node.x - 6 - dataLabelOffsetX
|
|
498
|
+
: node.x + node.width + 6 + dataLabelOffsetX}
|
|
499
|
+
y={node.y + node.height / 2}
|
|
500
|
+
text-anchor={node.column === 0 ? 'end' : 'start'}
|
|
501
|
+
dominant-baseline="middle"
|
|
502
|
+
>{truncateLabel(
|
|
503
|
+
showValues ? `${node.label} (${format(node.value)})` : node.label,
|
|
504
|
+
node.column
|
|
505
|
+
)}<title>{showValues ? `${node.label} (${format(node.value)})` : node.label}</title
|
|
506
|
+
></text
|
|
507
|
+
>
|
|
508
|
+
{/if}
|
|
509
|
+
{/each}
|
|
510
|
+
{/if}
|
|
412
511
|
</g>
|
|
413
512
|
</ChartContainer>
|
|
414
513
|
|
|
@@ -551,6 +551,7 @@
|
|
|
551
551
|
white-space: nowrap;
|
|
552
552
|
overflow: hidden;
|
|
553
553
|
text-overflow: ellipsis;
|
|
554
|
+
text-align: var(--select-value-align, left);
|
|
554
555
|
}
|
|
555
556
|
|
|
556
557
|
.select-placeholder {
|
|
@@ -559,6 +560,7 @@
|
|
|
559
560
|
white-space: nowrap;
|
|
560
561
|
overflow: hidden;
|
|
561
562
|
text-overflow: ellipsis;
|
|
563
|
+
text-align: var(--select-value-align, left);
|
|
562
564
|
}
|
|
563
565
|
|
|
564
566
|
.select-search {
|
package/dist/Table/Table.svelte
CHANGED
|
@@ -49,7 +49,10 @@
|
|
|
49
49
|
onSearchChange,
|
|
50
50
|
pagination,
|
|
51
51
|
toolbarSlot,
|
|
52
|
-
rowNumberColumn = false
|
|
52
|
+
rowNumberColumn = false,
|
|
53
|
+
rowNumberLabel = '#',
|
|
54
|
+
headerTooltipIcon,
|
|
55
|
+
headerTooltipPosition
|
|
53
56
|
}: TableProperties = $props();
|
|
54
57
|
|
|
55
58
|
// ─── Keyed column model → positional projection ─────────────────────────────
|
|
@@ -612,7 +615,7 @@
|
|
|
612
615
|
{/if}
|
|
613
616
|
{#if rowNumberColumn}
|
|
614
617
|
<th class="table-header table-row-number-col" class:table-header-sticky={isStickyHeader}
|
|
615
|
-
|
|
618
|
+
>{rowNumberLabel}</th
|
|
616
619
|
>
|
|
617
620
|
{/if}
|
|
618
621
|
{#each effectiveHeaders as header, colIndex (colIndex)}
|
|
@@ -633,8 +636,16 @@
|
|
|
633
636
|
: null}
|
|
634
637
|
>
|
|
635
638
|
{#if headerColumn?.tooltip}
|
|
636
|
-
<Tooltip
|
|
637
|
-
|
|
639
|
+
<Tooltip
|
|
640
|
+
text={headerColumn.tooltip}
|
|
641
|
+
position={headerTooltipPosition}
|
|
642
|
+
icon={headerTooltipIcon}
|
|
643
|
+
iconPosition="trailing"
|
|
644
|
+
>
|
|
645
|
+
<span
|
|
646
|
+
class="table-header-label"
|
|
647
|
+
class:table-header-label-plain={headerTooltipIcon}>{header}</span
|
|
648
|
+
>
|
|
638
649
|
</Tooltip>
|
|
639
650
|
{:else}
|
|
640
651
|
{header}
|
|
@@ -990,12 +1001,14 @@
|
|
|
990
1001
|
letter-spacing: var(--table-header-letter-spacing, 0.02em);
|
|
991
1002
|
text-transform: var(--table-header-text-transform);
|
|
992
1003
|
color: var(--table-header-color, var(--table-header-font-color, #6b7280));
|
|
1004
|
+
border-bottom: var(--table-header-border, var(--table-inner-border, none));
|
|
993
1005
|
}
|
|
994
1006
|
|
|
995
1007
|
.table-header-content {
|
|
996
1008
|
display: flex;
|
|
997
1009
|
align-items: center;
|
|
998
1010
|
gap: 4px;
|
|
1011
|
+
justify-content: var(--table-header-justify, flex-start);
|
|
999
1012
|
}
|
|
1000
1013
|
|
|
1001
1014
|
.table-header-label {
|
|
@@ -1004,6 +1017,10 @@
|
|
|
1004
1017
|
cursor: help;
|
|
1005
1018
|
}
|
|
1006
1019
|
|
|
1020
|
+
.table-header-label-plain {
|
|
1021
|
+
text-decoration: none;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1007
1024
|
.table-header-filter {
|
|
1008
1025
|
display: inline-flex;
|
|
1009
1026
|
align-items: center;
|
|
@@ -1256,6 +1273,7 @@
|
|
|
1256
1273
|
width: var(--table-row-number-col-width, 48px);
|
|
1257
1274
|
color: var(--table-row-number-color, #6b7280);
|
|
1258
1275
|
font-variant-numeric: tabular-nums;
|
|
1276
|
+
text-align: var(--table-row-number-align, var(--table-text-align, left));
|
|
1259
1277
|
}
|
|
1260
1278
|
|
|
1261
1279
|
/* ── Accessibility ──────────────────────────────────────────────────────── */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { JSONValue } from 'type-decoder';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { TooltipPosition } from '../Tooltip/properties';
|
|
3
4
|
export type SortDirection = 'asc' | 'desc';
|
|
4
5
|
/**
|
|
5
6
|
* Built-in cell renderer vocabulary for the keyed column model.
|
|
@@ -335,6 +336,16 @@ export type OptionalTableProperties = {
|
|
|
335
336
|
}]>;
|
|
336
337
|
/** Prepends a sequential row-number column (1-based, pagination-aware). */
|
|
337
338
|
rowNumberColumn?: boolean;
|
|
339
|
+
/** Header label for the row-number column. Defaults to `'#'`. */
|
|
340
|
+
rowNumberLabel?: string;
|
|
341
|
+
/**
|
|
342
|
+
* Icon snippet shown after each header label that has a `tooltip` — the
|
|
343
|
+
* consumer supplies the glyph; the table places it trailing inside the tooltip
|
|
344
|
+
* trigger. When set, the default underline affordance on those labels is dropped.
|
|
345
|
+
*/
|
|
346
|
+
headerTooltipIcon?: Snippet;
|
|
347
|
+
/** Placement of every header tooltip bubble. Defaults to `'top'`. */
|
|
348
|
+
headerTooltipPosition?: TooltipPosition;
|
|
338
349
|
sortable?: boolean;
|
|
339
350
|
sortableColumns?: number[];
|
|
340
351
|
stickyHeader?: boolean;
|
package/dist/Tabs/Tabs.svelte
CHANGED
|
@@ -252,30 +252,51 @@
|
|
|
252
252
|
display: none;
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
/* Each fade holds FULLY transparent for the first --tabs-fade-solid px before
|
|
256
|
+
ramping to opaque: a plain 0→fade-size ramp still renders the clipped tab
|
|
257
|
+
label at ~20% opacity a few px from the edge, which reads as a stray glyph
|
|
258
|
+
fragment beside the scroll arrow. The solid zone guarantees nothing is
|
|
259
|
+
perceptible there. */
|
|
255
260
|
.tabs-bar.fade-left {
|
|
256
|
-
mask-image: linear-gradient(
|
|
257
|
-
|
|
261
|
+
mask-image: linear-gradient(
|
|
262
|
+
to right,
|
|
263
|
+
transparent var(--tabs-fade-solid, 8px),
|
|
264
|
+
black var(--tabs-fade-size, 32px)
|
|
265
|
+
);
|
|
266
|
+
-webkit-mask-image: linear-gradient(
|
|
267
|
+
to right,
|
|
268
|
+
transparent var(--tabs-fade-solid, 8px),
|
|
269
|
+
black var(--tabs-fade-size, 32px)
|
|
270
|
+
);
|
|
258
271
|
}
|
|
259
272
|
|
|
260
273
|
.tabs-bar.fade-right {
|
|
261
|
-
mask-image: linear-gradient(
|
|
262
|
-
|
|
274
|
+
mask-image: linear-gradient(
|
|
275
|
+
to left,
|
|
276
|
+
transparent var(--tabs-fade-solid, 8px),
|
|
277
|
+
black var(--tabs-fade-size, 32px)
|
|
278
|
+
);
|
|
279
|
+
-webkit-mask-image: linear-gradient(
|
|
280
|
+
to left,
|
|
281
|
+
transparent var(--tabs-fade-solid, 8px),
|
|
282
|
+
black var(--tabs-fade-size, 32px)
|
|
283
|
+
);
|
|
263
284
|
}
|
|
264
285
|
|
|
265
286
|
.tabs-bar.fade-left.fade-right {
|
|
266
287
|
mask-image: linear-gradient(
|
|
267
288
|
to right,
|
|
268
|
-
transparent,
|
|
289
|
+
transparent var(--tabs-fade-solid, 8px),
|
|
269
290
|
black var(--tabs-fade-size, 32px),
|
|
270
291
|
black calc(100% - var(--tabs-fade-size, 32px)),
|
|
271
|
-
transparent
|
|
292
|
+
transparent calc(100% - var(--tabs-fade-solid, 8px))
|
|
272
293
|
);
|
|
273
294
|
-webkit-mask-image: linear-gradient(
|
|
274
295
|
to right,
|
|
275
|
-
transparent,
|
|
296
|
+
transparent var(--tabs-fade-solid, 8px),
|
|
276
297
|
black var(--tabs-fade-size, 32px),
|
|
277
298
|
black calc(100% - var(--tabs-fade-size, 32px)),
|
|
278
|
-
transparent
|
|
299
|
+
transparent calc(100% - var(--tabs-fade-solid, 8px))
|
|
279
300
|
);
|
|
280
301
|
}
|
|
281
302
|
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
classes,
|
|
12
12
|
children,
|
|
13
13
|
icon,
|
|
14
|
+
iconPosition = 'leading',
|
|
14
15
|
content,
|
|
15
16
|
usePortal = false
|
|
16
17
|
}: TooltipProperties = $props();
|
|
@@ -355,10 +356,13 @@
|
|
|
355
356
|
onfocusout={hideTooltip}
|
|
356
357
|
data-pw={testId}
|
|
357
358
|
>
|
|
358
|
-
{#if typeof icon === 'function'}
|
|
359
|
+
{#if typeof icon === 'function' && iconPosition === 'leading'}
|
|
359
360
|
<span class="tooltip-icon" aria-hidden="true">{@render icon()}</span>
|
|
360
361
|
{/if}
|
|
361
362
|
{@render children()}
|
|
363
|
+
{#if typeof icon === 'function' && iconPosition === 'trailing'}
|
|
364
|
+
<span class="tooltip-icon" aria-hidden="true">{@render icon()}</span>
|
|
365
|
+
{/if}
|
|
362
366
|
{#if visible && !usePortal}
|
|
363
367
|
<div
|
|
364
368
|
use:clampInlineBubble
|
|
@@ -9,8 +9,10 @@ export type OptionalTooltipProperties = {
|
|
|
9
9
|
delay?: number;
|
|
10
10
|
testId?: string | null;
|
|
11
11
|
classes?: string;
|
|
12
|
-
/** Snippet rendered as
|
|
12
|
+
/** Snippet rendered as an icon in the trigger wrapper, beside the content. No default glyph is provided — consumers supply their own. Placement is controlled by `iconPosition`. */
|
|
13
13
|
icon?: Snippet;
|
|
14
|
+
/** Which side of the trigger content the `icon` sits on. Defaults to `'leading'`. */
|
|
15
|
+
iconPosition?: 'leading' | 'trailing';
|
|
14
16
|
/** Snippet rendered as the bubble body. When provided, replaces the plain `text` string inside the tooltip bubble. */
|
|
15
17
|
content?: Snippet;
|
|
16
18
|
/**
|