@juspay/svelte-ui-components 2.134.1 → 2.136.0
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/Chat/Chat.svelte +38 -2
- package/dist/Chat/properties.d.ts +7 -0
- package/dist/ChatMessageList/ChatMessageList.svelte +54 -1
- package/dist/CheckListItem/CheckListItem.svelte +4 -1
- package/dist/ChipInput/ChipInput.svelte +150 -10
- package/dist/ChipInput/properties.d.ts +18 -1
- package/dist/CommandMenu/CommandMenu.svelte +7 -3
- package/dist/EmptyState/EmptyState.svelte +17 -3
- package/dist/Input/Input.svelte +1 -0
- package/dist/ListItem/ListItem.svelte +14 -12
- package/dist/ListItem/properties.d.ts +7 -0
- package/dist/Menu/Menu.svelte +2 -1
- package/dist/Menu/properties.d.ts +2 -0
- package/dist/Modal/Modal.svelte +3 -3
- package/dist/Pill/Pill.svelte +2 -0
- package/dist/Pill/properties.d.ts +1 -0
- package/dist/Sheet/Sheet.svelte +3 -23
- package/dist/StatCard/StatCard.svelte +64 -1
- package/dist/StatCard/properties.d.ts +44 -2
- package/dist/Status/Status.svelte +2 -1
- package/dist/Status/properties.d.ts +10 -0
- package/dist/Stepper/Step.svelte +95 -2
- package/dist/Stepper/Stepper.svelte +52 -7
- package/dist/Stepper/properties.d.ts +34 -1
- package/dist/Table/BuiltinCell.svelte +46 -23
- package/dist/Table/Table.svelte +63 -40
- package/dist/Table/cellData.js +3 -0
- package/dist/Table/properties.d.ts +70 -0
- package/dist/ThinkingIndicator/ThinkingIndicator.svelte +93 -5
- package/dist/ThinkingIndicator/properties.d.ts +7 -3
- package/dist/TypewriterText/TypewriterText.svelte +67 -2
- package/dist/TypewriterText/properties.d.ts +97 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +30 -0
- package/dist-wc/index.js +2937 -2658
- package/package.json +1 -1
|
@@ -58,6 +58,32 @@
|
|
|
58
58
|
: ''
|
|
59
59
|
);
|
|
60
60
|
|
|
61
|
+
const defaultTestIdSuffixes = {
|
|
62
|
+
icon: 'icon',
|
|
63
|
+
thumbnail: 'thumb',
|
|
64
|
+
thumbnailPlaceholder: 'thumb-placeholder',
|
|
65
|
+
tag: 'tag',
|
|
66
|
+
trendUp: 'trend-up',
|
|
67
|
+
trendDown: 'trend-down',
|
|
68
|
+
menu: 'menu',
|
|
69
|
+
menuTrigger: 'menu-trigger',
|
|
70
|
+
popup: 'popup',
|
|
71
|
+
popupTrigger: 'popup-trigger',
|
|
72
|
+
link: 'link',
|
|
73
|
+
copy: 'copy',
|
|
74
|
+
linkCopied: 'link-copied'
|
|
75
|
+
} as const;
|
|
76
|
+
|
|
77
|
+
const generatedTestId = (suffixName: keyof typeof defaultTestIdSuffixes, index?: number) => {
|
|
78
|
+
if (!column.testId) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const suffix = column.testIdSuffixes?.[suffixName] ?? defaultTestIdSuffixes[suffixName];
|
|
82
|
+
return typeof index === 'number'
|
|
83
|
+
? `${column.testId}-${suffix}-${index}`
|
|
84
|
+
: `${column.testId}-${suffix}`;
|
|
85
|
+
};
|
|
86
|
+
|
|
61
87
|
let copied = $state(false);
|
|
62
88
|
let copyResetTimer: ReturnType<typeof setTimeout> | null = null;
|
|
63
89
|
|
|
@@ -143,8 +169,8 @@
|
|
|
143
169
|
{#each icons as iconSrc, iconIndex (`${iconIndex}-${iconSrc}`)}
|
|
144
170
|
<span
|
|
145
171
|
class="builtin-icon-label-icon"
|
|
146
|
-
data-pw={
|
|
147
|
-
testID={
|
|
172
|
+
data-pw={generatedTestId('icon', iconIndex)}
|
|
173
|
+
testID={generatedTestId('icon', iconIndex)}
|
|
148
174
|
>
|
|
149
175
|
<Img inlineSvg src={String(iconSrc)} alt="" fallback="" />
|
|
150
176
|
</span>
|
|
@@ -157,8 +183,8 @@
|
|
|
157
183
|
{#if typeof data.imageUrl === 'string' && data.imageUrl}
|
|
158
184
|
<span
|
|
159
185
|
class="builtin-thumb"
|
|
160
|
-
data-pw={
|
|
161
|
-
testID={
|
|
186
|
+
data-pw={generatedTestId('thumbnail')}
|
|
187
|
+
testID={generatedTestId('thumbnail')}
|
|
162
188
|
>
|
|
163
189
|
<Img
|
|
164
190
|
src={data.imageUrl}
|
|
@@ -169,8 +195,8 @@
|
|
|
169
195
|
{:else}
|
|
170
196
|
<span
|
|
171
197
|
class="builtin-thumb builtin-thumb-placeholder"
|
|
172
|
-
data-pw={
|
|
173
|
-
testID={
|
|
198
|
+
data-pw={generatedTestId('thumbnailPlaceholder')}
|
|
199
|
+
testID={generatedTestId('thumbnailPlaceholder')}
|
|
174
200
|
>{typeof data.text1 === 'string' && data.text1
|
|
175
201
|
? data.text1.charAt(0).toUpperCase()
|
|
176
202
|
: ''}</span
|
|
@@ -190,7 +216,7 @@
|
|
|
190
216
|
<Pill
|
|
191
217
|
text={tag.text}
|
|
192
218
|
classes={tag.classes ?? ''}
|
|
193
|
-
testId={tag.testId ?? (
|
|
219
|
+
testId={tag.testId ?? generatedTestId('tag', tagIndex)}
|
|
194
220
|
/>
|
|
195
221
|
{/each}
|
|
196
222
|
</div>
|
|
@@ -227,8 +253,8 @@
|
|
|
227
253
|
{#if compare.trendPercent > 0}
|
|
228
254
|
<span
|
|
229
255
|
class="builtin-trend builtin-trend-up"
|
|
230
|
-
data-pw={
|
|
231
|
-
testID={
|
|
256
|
+
data-pw={generatedTestId('trendUp')}
|
|
257
|
+
testID={generatedTestId('trendUp')}
|
|
232
258
|
>
|
|
233
259
|
<!-- eslint-disable svelte/no-at-html-tags -->
|
|
234
260
|
<span class="builtin-trend-icon">{@html trendUpSvg}</span>
|
|
@@ -237,8 +263,8 @@
|
|
|
237
263
|
{:else if compare.trendPercent < 0}
|
|
238
264
|
<span
|
|
239
265
|
class="builtin-trend builtin-trend-down"
|
|
240
|
-
data-pw={
|
|
241
|
-
testID={
|
|
266
|
+
data-pw={generatedTestId('trendDown')}
|
|
267
|
+
testID={generatedTestId('trendDown')}
|
|
242
268
|
>
|
|
243
269
|
<!-- eslint-disable svelte/no-at-html-tags -->
|
|
244
270
|
<span class="builtin-trend-icon">{@html trendDownSvg}</span>
|
|
@@ -419,16 +445,13 @@
|
|
|
419
445
|
danger: item.danger,
|
|
420
446
|
separator: item.separator
|
|
421
447
|
}))}
|
|
422
|
-
testId={
|
|
448
|
+
testId={generatedTestId('menu', rowIndex)}
|
|
423
449
|
{usePortal}
|
|
424
450
|
onselect={(menuItem) => column.onMenuAction?.(rowIndex, menuItem.value, originalIndex)}
|
|
425
451
|
>
|
|
426
452
|
{#snippet trigger()}
|
|
427
453
|
<span class="builtin-icon-button">
|
|
428
|
-
<Button
|
|
429
|
-
ariaLabel="More actions"
|
|
430
|
-
testId={column.testId && `${column.testId}-menu-trigger-${rowIndex}`}
|
|
431
|
-
>
|
|
454
|
+
<Button ariaLabel="More actions" testId={generatedTestId('menuTrigger', rowIndex)}>
|
|
432
455
|
{#snippet icon()}
|
|
433
456
|
<!-- eslint-disable svelte/no-at-html-tags -->
|
|
434
457
|
<span class="builtin-menu-dots">{@html dotsSvg}</span>
|
|
@@ -458,7 +481,7 @@
|
|
|
458
481
|
danger: item.danger,
|
|
459
482
|
separator: item.separator
|
|
460
483
|
}))}
|
|
461
|
-
testId={
|
|
484
|
+
testId={generatedTestId('popup', rowIndex)}
|
|
462
485
|
{usePortal}
|
|
463
486
|
onselect={(menuItem) => column.onMenuAction?.(rowIndex, menuItem.value, originalIndex)}
|
|
464
487
|
>
|
|
@@ -466,7 +489,7 @@
|
|
|
466
489
|
<span class="builtin-icon-button">
|
|
467
490
|
<Button
|
|
468
491
|
ariaLabel={popupData.ariaLabel ?? 'More actions'}
|
|
469
|
-
testId={
|
|
492
|
+
testId={generatedTestId('popupTrigger', rowIndex)}
|
|
470
493
|
>
|
|
471
494
|
{#snippet icon()}
|
|
472
495
|
<!-- eslint-disable svelte/no-at-html-tags -->
|
|
@@ -490,8 +513,8 @@
|
|
|
490
513
|
target="_blank"
|
|
491
514
|
rel="noopener noreferrer"
|
|
492
515
|
class="builtin-link-anchor"
|
|
493
|
-
data-pw={
|
|
494
|
-
testID={
|
|
516
|
+
data-pw={generatedTestId('link', rowIndex)}
|
|
517
|
+
testID={generatedTestId('link', rowIndex)}
|
|
495
518
|
>
|
|
496
519
|
{link.label ?? link.url}
|
|
497
520
|
</a>
|
|
@@ -499,7 +522,7 @@
|
|
|
499
522
|
<span class="builtin-link-copy">
|
|
500
523
|
<Button
|
|
501
524
|
ariaLabel={copied ? 'Link copied' : 'Copy link'}
|
|
502
|
-
testId={
|
|
525
|
+
testId={generatedTestId('copy', rowIndex)}
|
|
503
526
|
onclick={() => handleCopy(link.url)}
|
|
504
527
|
>
|
|
505
528
|
{#snippet icon()}
|
|
@@ -511,8 +534,8 @@
|
|
|
511
534
|
{#if copied}
|
|
512
535
|
<span
|
|
513
536
|
class="builtin-link-copied"
|
|
514
|
-
data-pw={
|
|
515
|
-
testID={
|
|
537
|
+
data-pw={generatedTestId('linkCopied')}
|
|
538
|
+
testID={generatedTestId('linkCopied')}>Copied</span
|
|
516
539
|
>
|
|
517
540
|
{/if}
|
|
518
541
|
{/if}
|
package/dist/Table/Table.svelte
CHANGED
|
@@ -375,6 +375,14 @@
|
|
|
375
375
|
return total > 0 ? `${from}-${to} of ${total}` : '';
|
|
376
376
|
});
|
|
377
377
|
let pageSizeOptions = $derived(pagination?.pageSizeOptions ?? [10, 25, 50, 100]);
|
|
378
|
+
// `hideControls` remains a shorthand for hiding both at once — existing
|
|
379
|
+
// consumers of it are unaffected. `hidePageSizeSelector`/`hideSteppers` let
|
|
380
|
+
// a consumer suppress either independently, e.g. a working external
|
|
381
|
+
// paginator that only wants Table's range text plus its own steppers.
|
|
382
|
+
let hidePageSizeSelector = $derived(
|
|
383
|
+
(pagination?.hideControls || pagination?.hidePageSizeSelector) ?? false
|
|
384
|
+
);
|
|
385
|
+
let hideSteppers = $derived((pagination?.hideControls || pagination?.hideSteppers) ?? false);
|
|
378
386
|
|
|
379
387
|
const handlePageChange = (page: number): void => {
|
|
380
388
|
pageOverride = page;
|
|
@@ -722,6 +730,7 @@
|
|
|
722
730
|
data-pw={headerColumn?.testId ?? null}
|
|
723
731
|
testID={headerColumn?.testId ?? null}
|
|
724
732
|
style:text-align={headerColumn?.align ?? null}
|
|
733
|
+
style:width={headerColumn?.width ?? null}
|
|
725
734
|
style:max-width={headerColumn?.maxWidth ?? null}
|
|
726
735
|
>
|
|
727
736
|
<span
|
|
@@ -965,6 +974,7 @@
|
|
|
965
974
|
? getCellTestId(row, cellValue, rowIndex)
|
|
966
975
|
: null}
|
|
967
976
|
style:text-align={keyedColumn?.align ?? null}
|
|
977
|
+
style:width={keyedColumn?.width ?? null}
|
|
968
978
|
style:max-width={keyedColumn?.maxWidth ?? null}
|
|
969
979
|
title={keyedColumn?.maxWidth && isScalarCell ? String(cellValue) : null}
|
|
970
980
|
>
|
|
@@ -1007,10 +1017,15 @@
|
|
|
1007
1017
|
<div class="table-footer">
|
|
1008
1018
|
{@render paginatorSlot()}
|
|
1009
1019
|
</div>
|
|
1010
|
-
{:else if pagination && (paginationTotalPages > 1 || pagination.hasMore)}
|
|
1020
|
+
{:else if pagination && (paginationTotalPages > 1 || pagination.hasMore || pagination.showFooterOnSinglePage || (hidePageSizeSelector && hideSteppers))}
|
|
1011
1021
|
<!-- DataGrid parity: pagination chrome only renders when the data spans
|
|
1012
1022
|
more than one page (or the server reports more chunks); a
|
|
1013
|
-
single-page table shows no footer.
|
|
1023
|
+
single-page table shows no footer. `showFooterOnSinglePage` opts
|
|
1024
|
+
a consumer out of that default, e.g. to keep the page-size
|
|
1025
|
+
selector reachable even for a one-page result. Suppressing BOTH
|
|
1026
|
+
controls (via `hideControls`, or `hidePageSizeSelector` +
|
|
1027
|
+
`hideSteppers` together) implies the same — a count-only footer
|
|
1028
|
+
has nothing to hide behind "already on the only page". -->
|
|
1014
1029
|
<div
|
|
1015
1030
|
class="table-footer table-paginator"
|
|
1016
1031
|
data-pw={pagination.testId ?? null}
|
|
@@ -1018,47 +1033,55 @@
|
|
|
1018
1033
|
>
|
|
1019
1034
|
<span
|
|
1020
1035
|
class="table-paginator-range"
|
|
1021
|
-
data-pw={
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
? `${
|
|
1030
|
-
:
|
|
1036
|
+
data-pw={pagination.rangeTestId ??
|
|
1037
|
+
(typeof testId === 'string'
|
|
1038
|
+
? `${testId}-paginator-range`
|
|
1039
|
+
: pagination?.testId
|
|
1040
|
+
? `${pagination.testId}-range`
|
|
1041
|
+
: null)}
|
|
1042
|
+
testID={pagination.rangeTestId ??
|
|
1043
|
+
(typeof testId === 'string'
|
|
1044
|
+
? `${testId}-paginator-range`
|
|
1045
|
+
: pagination?.testId
|
|
1046
|
+
? `${pagination.testId}-range`
|
|
1047
|
+
: null)}>{paginationRangeText}</span
|
|
1031
1048
|
>
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
<
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1049
|
+
{#if !hidePageSizeSelector || !hideSteppers}
|
|
1050
|
+
<span class="table-paginator-controls">
|
|
1051
|
+
{#if !hidePageSizeSelector && pageSizeOptions.length > 0}
|
|
1052
|
+
<span class="table-paginator-size">
|
|
1053
|
+
<Select
|
|
1054
|
+
items={pageSizeOptions.map((sizeOption) => ({
|
|
1055
|
+
id: String(sizeOption),
|
|
1056
|
+
label: String(sizeOption)
|
|
1057
|
+
}))}
|
|
1058
|
+
value={[String(effectivePageSize)]}
|
|
1059
|
+
disabled={pagination.isLoading ?? false}
|
|
1060
|
+
usePortal
|
|
1061
|
+
testId={pagination.testId && `${pagination.testId}-page-size`}
|
|
1062
|
+
onchange={(selectedSizes) => {
|
|
1063
|
+
if (selectedSizes.length > 0) {
|
|
1064
|
+
handlePageSizeChange(Number(selectedSizes[0]));
|
|
1065
|
+
}
|
|
1066
|
+
}}
|
|
1067
|
+
/>
|
|
1068
|
+
</span>
|
|
1069
|
+
{/if}
|
|
1070
|
+
{#if !hideSteppers}
|
|
1071
|
+
<Pagination
|
|
1072
|
+
totalPages={paginationTotalPages}
|
|
1073
|
+
currentPage={effectivePage}
|
|
1074
|
+
hasMore={pagination.hasMore ?? false}
|
|
1041
1075
|
disabled={pagination.isLoading ?? false}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
}
|
|
1048
|
-
}}
|
|
1076
|
+
testId={pagination.testId && `${pagination.testId}-pages`}
|
|
1077
|
+
prevButtonTestId={pagination.prevButtonTestId}
|
|
1078
|
+
nextButtonTestId={pagination.nextButtonTestId}
|
|
1079
|
+
onchange={handlePageChange}
|
|
1080
|
+
onLoadMore={pagination.onLoadMore}
|
|
1049
1081
|
/>
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
totalPages={paginationTotalPages}
|
|
1054
|
-
currentPage={effectivePage}
|
|
1055
|
-
hasMore={pagination.hasMore ?? false}
|
|
1056
|
-
disabled={pagination.isLoading ?? false}
|
|
1057
|
-
testId={pagination.testId && `${pagination.testId}-pages`}
|
|
1058
|
-
onchange={handlePageChange}
|
|
1059
|
-
onLoadMore={pagination.onLoadMore}
|
|
1060
|
-
/>
|
|
1061
|
-
</span>
|
|
1082
|
+
{/if}
|
|
1083
|
+
</span>
|
|
1084
|
+
{/if}
|
|
1062
1085
|
</div>
|
|
1063
1086
|
{/if}
|
|
1064
1087
|
</div>
|
package/dist/Table/cellData.js
CHANGED
|
@@ -201,6 +201,27 @@ export type TablePopupMenuCellData = {
|
|
|
201
201
|
* the options, selection state, and filtering itself belong to the consumer.
|
|
202
202
|
* Selecting the already-selected option clears the filter (emits `null`).
|
|
203
203
|
*/
|
|
204
|
+
/**
|
|
205
|
+
* Customizes the generated `data-pw` suffixes of built-in cells for one
|
|
206
|
+
* column. Values replace only the named suffix; row and item indices remain
|
|
207
|
+
* appended where the default includes them. Cell-data `testId` values still
|
|
208
|
+
* take precedence for renderers that accept one.
|
|
209
|
+
*/
|
|
210
|
+
export type TableBuiltinCellTestIdSuffixes = {
|
|
211
|
+
icon?: string;
|
|
212
|
+
thumbnail?: string;
|
|
213
|
+
thumbnailPlaceholder?: string;
|
|
214
|
+
tag?: string;
|
|
215
|
+
trendUp?: string;
|
|
216
|
+
trendDown?: string;
|
|
217
|
+
menu?: string;
|
|
218
|
+
menuTrigger?: string;
|
|
219
|
+
popup?: string;
|
|
220
|
+
popupTrigger?: string;
|
|
221
|
+
link?: string;
|
|
222
|
+
copy?: string;
|
|
223
|
+
linkCopied?: string;
|
|
224
|
+
};
|
|
204
225
|
export type TableColumnFilterConfig = {
|
|
205
226
|
options: Array<{
|
|
206
227
|
label: string;
|
|
@@ -246,11 +267,18 @@ export type TableColumn = {
|
|
|
246
267
|
* cells follow the table-wide `--table-text-align` (left by default).
|
|
247
268
|
*/
|
|
248
269
|
align?: 'left' | 'center' | 'right';
|
|
270
|
+
/** Fixed/preferred column width (any CSS length), applied inline to its header and cells. */
|
|
271
|
+
width?: string;
|
|
249
272
|
/**
|
|
250
273
|
* Caps the column width (any CSS length). Overflowing scalar cell text
|
|
251
274
|
* ellipsizes with the full value available on the native title tooltip.
|
|
252
275
|
*/
|
|
253
276
|
maxWidth?: string;
|
|
277
|
+
/**
|
|
278
|
+
* Per-built-in generated `data-pw` suffix overrides for this column. Omitted
|
|
279
|
+
* entries preserve the existing suffixes exactly; row/item indices remain.
|
|
280
|
+
*/
|
|
281
|
+
testIdSuffixes?: TableBuiltinCellTestIdSuffixes;
|
|
254
282
|
/**
|
|
255
283
|
* Paints this column's header and body cells with the highlight wash —
|
|
256
284
|
* `--table-col-highlight-background` (body) and
|
|
@@ -342,12 +370,54 @@ export type TablePaginationConfig = {
|
|
|
342
370
|
hasMore?: boolean;
|
|
343
371
|
/** Disables the paginator and page-size selector during a fetch. */
|
|
344
372
|
isLoading?: boolean;
|
|
373
|
+
/**
|
|
374
|
+
* Keeps the paginator footer (range text, page-size selector, steppers)
|
|
375
|
+
* visible even when the data fits on a single page. Default `false`
|
|
376
|
+
* matches DataGrid parity — a single page renders no footer at all.
|
|
377
|
+
*/
|
|
378
|
+
showFooterOnSinglePage?: boolean;
|
|
379
|
+
/**
|
|
380
|
+
* Renders only the range summary text ("{from}-{to} of {total}") and
|
|
381
|
+
* suppresses the page-size selector and page steppers — for a bare
|
|
382
|
+
* "Showing X-Y of Z" affordance with no navigation controls. Implies
|
|
383
|
+
* `showFooterOnSinglePage`: a count-only footer has nothing to hide behind
|
|
384
|
+
* "already on the only page". Shorthand for `hidePageSizeSelector: true`
|
|
385
|
+
* plus `hideSteppers: true`; reach for those two directly when you only
|
|
386
|
+
* want to suppress one of the pair. Default `false`.
|
|
387
|
+
*/
|
|
388
|
+
hideControls?: boolean;
|
|
389
|
+
/**
|
|
390
|
+
* Suppresses just the page-size Select, independently of the steppers —
|
|
391
|
+
* e.g. a fixed page size with no reason to expose the selector, while
|
|
392
|
+
* still keeping working page navigation. Default `false`.
|
|
393
|
+
*/
|
|
394
|
+
hidePageSizeSelector?: boolean;
|
|
395
|
+
/**
|
|
396
|
+
* Suppresses just the Pagination steppers, independently of the page-size
|
|
397
|
+
* Select — for a call site with its own working paginator elsewhere that
|
|
398
|
+
* only wants Table's range text (and, optionally, its page-size selector).
|
|
399
|
+
* Default `false`.
|
|
400
|
+
*/
|
|
401
|
+
hideSteppers?: boolean;
|
|
345
402
|
/** Range text override; default "{from}-{to} of {total}". */
|
|
346
403
|
rangeLabel?: (from: number, to: number, total: number) => string;
|
|
404
|
+
/**
|
|
405
|
+
* Explicit `data-pw`/`testID` for the range summary span. Wins over the
|
|
406
|
+
* derived id — the default derives from the table's own `testId`
|
|
407
|
+
* (`${testId}-paginator-range`), falling back to `${pagination.testId}-range`
|
|
408
|
+
* only when the table has none, so a call site whose table `testId` is
|
|
409
|
+
* already load-bearing (built-in cell ids derive from it) has no way to
|
|
410
|
+
* give the range span an independent locator without this.
|
|
411
|
+
*/
|
|
412
|
+
rangeTestId?: string;
|
|
347
413
|
onPageChange?: (page: number) => void;
|
|
348
414
|
onPageSizeChange?: (pageSize: number) => void;
|
|
349
415
|
onLoadMore?: () => void;
|
|
350
416
|
testId?: string;
|
|
417
|
+
/** Forwarded to Pagination's previous-page button without modification. */
|
|
418
|
+
prevButtonTestId?: string;
|
|
419
|
+
/** Forwarded to Pagination's next-page (or load-more) button without modification. */
|
|
420
|
+
nextButtonTestId?: string;
|
|
351
421
|
};
|
|
352
422
|
/**
|
|
353
423
|
* Configuration for the built-in search bar (C2-3).
|
|
@@ -40,16 +40,20 @@
|
|
|
40
40
|
|
|
41
41
|
// A detail string or a trace is what makes the indicator expandable. Without either
|
|
42
42
|
// there is nothing to reveal, so it renders as a plain live status line instead of a
|
|
43
|
-
// disclosure control. `bare`
|
|
43
|
+
// disclosure control. `bare` and `chip` both override that entirely — neither has
|
|
44
|
+
// room (or a documented reason) to expand.
|
|
44
45
|
const isExpandable = $derived(
|
|
45
|
-
variant
|
|
46
|
+
variant === 'default' && (hasTraceMode || (typeof detail === 'string' && detail.length > 0))
|
|
46
47
|
);
|
|
47
48
|
|
|
48
49
|
// Backward-compatible shimmer rule: without an explicit `busy`, the expandable
|
|
49
50
|
// summary holds still (settled) and every other shape shimmers (live) — exactly
|
|
50
|
-
// today's released behaviour.
|
|
51
|
-
// in
|
|
52
|
-
|
|
51
|
+
// today's released behaviour. `chip` is the one exception, defaulting to static:
|
|
52
|
+
// it exists to be a drop-in for ChatToolStatus, which never shimmered, so a
|
|
53
|
+
// caller porting that usage 1:1 shouldn't have to also learn `busy={false}` just
|
|
54
|
+
// to avoid an unexpected shimmer. Passing `busy` takes direct control of the
|
|
55
|
+
// shimmer in every shape, including `bare` and `chip`.
|
|
56
|
+
const labelIsBusy = $derived(busy ?? (variant === 'chip' ? false : !isExpandable));
|
|
53
57
|
|
|
54
58
|
// Set once a person clicks the disclosure open/closed — from then on the automatic
|
|
55
59
|
// busy-driven machine below leaves `expanded` alone for the rest of this mount.
|
|
@@ -406,6 +410,21 @@
|
|
|
406
410
|
>{label}</span
|
|
407
411
|
>{:else}{label}{/if}</span
|
|
408
412
|
>
|
|
413
|
+
{:else if variant === 'chip'}
|
|
414
|
+
<div
|
|
415
|
+
class="thinking-indicator-chip {classes ?? ''}"
|
|
416
|
+
aria-live="polite"
|
|
417
|
+
aria-atomic="true"
|
|
418
|
+
data-pw={typeof testId === 'string' ? testId : null}
|
|
419
|
+
testID={typeof testId === 'string' ? testId : null}
|
|
420
|
+
>
|
|
421
|
+
<span class="chip-icon">
|
|
422
|
+
{#if avatar}{@render avatar()}{:else}<Loader />{/if}
|
|
423
|
+
</span>
|
|
424
|
+
<span class="chip-label" class:static-label={!labelIsBusy} data-pw={labelTestId ?? null}
|
|
425
|
+
>{label}</span
|
|
426
|
+
>
|
|
427
|
+
</div>
|
|
409
428
|
{:else}
|
|
410
429
|
<div
|
|
411
430
|
class="thinking-indicator status-host {classes ?? ''}"
|
|
@@ -558,6 +577,69 @@
|
|
|
558
577
|
-webkit-text-fill-color: var(--thinking-indicator-label-color, #858585);
|
|
559
578
|
}
|
|
560
579
|
|
|
580
|
+
/* ---- chip variant: a self-contained floating pill (no ancestor supplies
|
|
581
|
+
background/layout, unlike bare) — deliberately not `width: 100%`/box-sizing
|
|
582
|
+
from `.thinking-indicator`, since a pill hugs its content. */
|
|
583
|
+
.thinking-indicator-chip {
|
|
584
|
+
box-sizing: border-box;
|
|
585
|
+
display: inline-flex;
|
|
586
|
+
align-items: center;
|
|
587
|
+
gap: var(--thinking-indicator-chip-gap, 8px);
|
|
588
|
+
width: fit-content;
|
|
589
|
+
padding: var(--thinking-indicator-chip-padding, 8px 14px);
|
|
590
|
+
background: var(--thinking-indicator-chip-background, #ffffff);
|
|
591
|
+
border: var(--thinking-indicator-chip-border, 1px solid #e4e4e7);
|
|
592
|
+
border-radius: var(--thinking-indicator-chip-border-radius, 999px);
|
|
593
|
+
box-shadow: var(--thinking-indicator-chip-box-shadow, 0 6px 20px rgba(0, 0, 0, 0.08));
|
|
594
|
+
max-width: var(--thinking-indicator-chip-max-width, 100%);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
.chip-icon {
|
|
598
|
+
display: inline-flex;
|
|
599
|
+
align-items: center;
|
|
600
|
+
flex-shrink: 0;
|
|
601
|
+
color: var(--thinking-indicator-chip-icon-color, currentColor);
|
|
602
|
+
--loader-foreground: var(--thinking-indicator-chip-spinner-color, currentColor);
|
|
603
|
+
--loader-foreground-end: var(--thinking-indicator-chip-spinner-color-end, transparent);
|
|
604
|
+
--loader-width: var(--thinking-indicator-chip-spinner-size, 14px);
|
|
605
|
+
--loader-height: var(--thinking-indicator-chip-spinner-size, 14px);
|
|
606
|
+
--loader-before-width: 7px;
|
|
607
|
+
--loader-before-height: 7px;
|
|
608
|
+
--loader-after-width: 11px;
|
|
609
|
+
--loader-after-height: 11px;
|
|
610
|
+
--loader-background: var(--thinking-indicator-chip-background, #ffffff);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
.chip-label {
|
|
614
|
+
flex: 0 1 auto;
|
|
615
|
+
min-width: 0;
|
|
616
|
+
white-space: nowrap;
|
|
617
|
+
overflow: hidden;
|
|
618
|
+
text-overflow: ellipsis;
|
|
619
|
+
font-size: var(--thinking-indicator-chip-font-size, 0.85rem);
|
|
620
|
+
font-weight: var(--thinking-indicator-chip-font-weight, 500);
|
|
621
|
+
color: var(--thinking-indicator-chip-color, #52525b);
|
|
622
|
+
background: var(
|
|
623
|
+
--thinking-indicator-chip-shimmer-gradient,
|
|
624
|
+
linear-gradient(90deg, #52525b 0%, #a0a0a0 50%, #52525b 100%)
|
|
625
|
+
);
|
|
626
|
+
background-size: 200% 100%;
|
|
627
|
+
background-clip: text;
|
|
628
|
+
-webkit-text-fill-color: transparent;
|
|
629
|
+
animation: thinking-indicator-shimmer var(--thinking-indicator-chip-shimmer-duration, 2s) linear
|
|
630
|
+
infinite;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/* Chip defaults to a static label (unlike the default/bare variants) since it
|
|
634
|
+
mirrors ChatToolStatus, which never shimmered — pass `busy` explicitly to
|
|
635
|
+
opt in. Higher specificity than the generic `.static-label` rule above, so
|
|
636
|
+
it correctly overrides with the chip's own color token, not the default
|
|
637
|
+
variant's. */
|
|
638
|
+
.chip-label.static-label {
|
|
639
|
+
animation: none;
|
|
640
|
+
-webkit-text-fill-color: var(--thinking-indicator-chip-color, #52525b);
|
|
641
|
+
}
|
|
642
|
+
|
|
561
643
|
.arrow {
|
|
562
644
|
display: inline-flex;
|
|
563
645
|
align-items: center;
|
|
@@ -784,6 +866,7 @@
|
|
|
784
866
|
|
|
785
867
|
@media (prefers-reduced-motion: reduce) {
|
|
786
868
|
.status-label,
|
|
869
|
+
.chip-label,
|
|
787
870
|
.trace-row,
|
|
788
871
|
.trace-more {
|
|
789
872
|
animation-duration: 0.001s;
|
|
@@ -799,5 +882,10 @@
|
|
|
799
882
|
animation: none;
|
|
800
883
|
-webkit-text-fill-color: var(--thinking-indicator-label-color, #858585);
|
|
801
884
|
}
|
|
885
|
+
|
|
886
|
+
.chip-label {
|
|
887
|
+
animation: none;
|
|
888
|
+
-webkit-text-fill-color: var(--thinking-indicator-chip-color, #52525b);
|
|
889
|
+
}
|
|
802
890
|
}
|
|
803
891
|
</style>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
-
export type ThinkingIndicatorVariant = 'default' | 'bare';
|
|
2
|
+
export type ThinkingIndicatorVariant = 'default' | 'bare' | 'chip';
|
|
3
3
|
export type ThinkingIndicatorKind = 'steps' | 'reasoning' | 'search' | 'coding';
|
|
4
4
|
export type ThinkingIndicatorTraceRow = {
|
|
5
5
|
/** The step, sentence, source title, or file action. */
|
|
@@ -29,14 +29,18 @@ export type OptionalThinkingIndicatorProperties = {
|
|
|
29
29
|
expanded?: boolean;
|
|
30
30
|
/**
|
|
31
31
|
* `bare` renders only the shimmering label — for chat bubbles where the surrounding
|
|
32
|
-
* UI already supplies the avatar and layout.
|
|
32
|
+
* UI already supplies the avatar and layout. `chip` renders a self-contained pill
|
|
33
|
+
* (bordered, shadowed, its own background) for a live status floating above other
|
|
34
|
+
* content, e.g. a tool-call indicator above a composer — pass `busy={false}` for a
|
|
35
|
+
* static (non-shimmering) label, matching a plain status badge. Neither `bare` nor
|
|
36
|
+
* `chip` ever becomes expandable.
|
|
33
37
|
*/
|
|
34
38
|
variant?: ThinkingIndicatorVariant;
|
|
35
39
|
/**
|
|
36
40
|
* Renders an elapsed `Ns` counter while the label is live. Starts at 0 when a busy
|
|
37
41
|
* phase begins, ticks every second, and freezes at its final value once the label
|
|
38
42
|
* settles (driven by `busy` when set, otherwise by the legacy status-line/detail
|
|
39
|
-
* shape). No effect on the `bare`
|
|
43
|
+
* shape). No effect on the `bare` or `chip` variants.
|
|
40
44
|
*/
|
|
41
45
|
showElapsed?: boolean;
|
|
42
46
|
onToggle?: () => void;
|