@juspay/svelte-ui-components 4.53.0 → 4.54.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/README.md +14 -0
- package/dist/ChatComposer/ChatComposer.svelte +17 -5
- package/dist/ChatComposer/properties.d.ts +10 -4
- package/dist/DateRangePicker/DateRangePicker.svelte +82 -33
- package/dist/DateRangePicker/properties.d.ts +18 -2
- package/dist/ToolCallLog/ToolCallLog.svelte +4 -4
- package/dist-wc/index.js +247 -199
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -154,6 +154,20 @@ fix to something that did not work.
|
|
|
154
154
|
| **RelativeTime** | Auto-updating relative time display ("5 minutes ago") with locale support and optional tooltip. | [docs](docs/RelativeTime.md) |
|
|
155
155
|
| **AspectRatio** | Constrains content to a fixed width-to-height ratio using CSS `aspect-ratio`, reserving layout space before an image, video or iframe loads. | [docs](docs/AspectRatio.md) |
|
|
156
156
|
|
|
157
|
+
### Charts
|
|
158
|
+
|
|
159
|
+
Pure-SVG charts with no charting-library dependency; each takes plain data arrays.
|
|
160
|
+
|
|
161
|
+
| Component | Description | Docs |
|
|
162
|
+
| --- | --- | --- |
|
|
163
|
+
| **AreaChart** | Responsive area chart for volume under a trend: multiple series, regular or 100% stacking, gradient fill, data labels and a hover overlay. | [docs](docs/AreaChart.md) |
|
|
164
|
+
| **BarChart** | Responsive bar chart for comparing categories: vertical or horizontal, single or multi-series (grouped or stacked), value labels, per-point colours and hover tooltips. | [docs](docs/BarChart.md) |
|
|
165
|
+
| **DualAxisBarChart** | Two independent Y-axes over one categorical X-axis; each series declares its own axis and render type. | [docs](docs/DualAxisBarChart.md) |
|
|
166
|
+
| **FunnelChart** | Horizontal funnel: one bar per stage, proportional to its value, with connectors between stages. | [docs](docs/FunnelChart.md) |
|
|
167
|
+
| **LineChart** | Responsive line chart for trends: multiple series, five curve interpolations, gradient fill and an optional area mode. | [docs](docs/LineChart.md) |
|
|
168
|
+
| **PieChart** | Responsive pie or donut chart with slice hover highlighting, a delta badge, custom labels, a legend and donut centre content. | [docs](docs/PieChart.md) |
|
|
169
|
+
| **SankeyChart** | Sankey diagram for flows between nodes, positioned into columns automatically. | [docs](docs/SankeyChart.md) |
|
|
170
|
+
|
|
157
171
|
### Feedback & Loading
|
|
158
172
|
|
|
159
173
|
| Component | Description | Docs |
|
|
@@ -134,15 +134,27 @@
|
|
|
134
134
|
let slashSelected = $state(0);
|
|
135
135
|
let slashList: HTMLUListElement | null = $state(null);
|
|
136
136
|
|
|
137
|
+
// A command opens on the first character of its name: "/model" on "/",
|
|
138
|
+
// "@support" on "@". Letters, digits and whitespace never trigger, so a
|
|
139
|
+
// plain word typed into the composer never opens the menu. Read as a whole
|
|
140
|
+
// code point, so an emoji trigger is never split into half a surrogate pair.
|
|
141
|
+
function slashTrigger(text: string): string | null {
|
|
142
|
+
const [first = ''] = text;
|
|
143
|
+
return /^[^\p{L}\p{N}\s]$/u.test(first) ? first : null;
|
|
144
|
+
}
|
|
145
|
+
|
|
137
146
|
const slashMatches = $derived.by((): ChatComposerSlashCommand[] => {
|
|
138
|
-
|
|
147
|
+
const trigger = slashTrigger(value);
|
|
148
|
+
if (slashCommands === null || slashDismissed || trigger === null || /\s/.test(value)) {
|
|
139
149
|
return [];
|
|
140
150
|
}
|
|
141
151
|
const query = value.toLowerCase();
|
|
142
152
|
return slashCommands.filter(
|
|
143
153
|
(command) =>
|
|
144
|
-
command.name
|
|
145
|
-
(
|
|
154
|
+
slashTrigger(command.name) === trigger &&
|
|
155
|
+
(command.name.toLowerCase().startsWith(query) ||
|
|
156
|
+
(query.length > trigger.length &&
|
|
157
|
+
(command.description ?? '').toLowerCase().includes(query.slice(trigger.length))))
|
|
146
158
|
);
|
|
147
159
|
});
|
|
148
160
|
const slashArg = $derived.by(
|
|
@@ -150,8 +162,8 @@
|
|
|
150
162
|
if (slashCommands === null || slashDismissed) {
|
|
151
163
|
return null;
|
|
152
164
|
}
|
|
153
|
-
const parsed = /^(
|
|
154
|
-
if (parsed === null) {
|
|
165
|
+
const parsed = /^(\S+)\s(\S*)$/.exec(value);
|
|
166
|
+
if (parsed === null || slashTrigger(parsed[1] ?? '') === null) {
|
|
155
167
|
return null;
|
|
156
168
|
}
|
|
157
169
|
const name = (parsed[1] ?? '').toLowerCase();
|
|
@@ -100,9 +100,11 @@ export type OptionalChatComposerProperties = {
|
|
|
100
100
|
/** Test id on the status region (see `statusText`). */
|
|
101
101
|
statusTestId?: string;
|
|
102
102
|
/**
|
|
103
|
-
* Opt-in slash-command menu. With commands passed, typing
|
|
104
|
-
*
|
|
105
|
-
*
|
|
103
|
+
* Opt-in slash-command menu. With commands passed, typing a command's
|
|
104
|
+
* trigger -- the first character of its name, "/" for "/model" or "@" for
|
|
105
|
+
* "@support" -- opens a listbox of the commands sharing that trigger that
|
|
106
|
+
* match (name prefix, or the text after the trigger found in a
|
|
107
|
+
* description); arrows move, Tab completes, Enter picks (Enter on a
|
|
106
108
|
* complete match submits), Escape dismisses -- through the shared
|
|
107
109
|
* dismissible stack, so an overlay above the composer closes first. A
|
|
108
110
|
* command with `choices` keeps the menu open past the space for a second
|
|
@@ -197,7 +199,11 @@ export type ChatComposerSlashCommandChoice = {
|
|
|
197
199
|
};
|
|
198
200
|
/** One entry of ChatComposer's slash-command menu. */
|
|
199
201
|
export type ChatComposerSlashCommand = {
|
|
200
|
-
/**
|
|
202
|
+
/**
|
|
203
|
+
* Including its trigger character, e.g. "/model" or "@support". The first
|
|
204
|
+
* character opens the menu, so it must be punctuation or a symbol: a name
|
|
205
|
+
* starting with a letter, digit or space is never offered.
|
|
206
|
+
*/
|
|
201
207
|
name: string;
|
|
202
208
|
description?: string;
|
|
203
209
|
/** Shown after the name, e.g. "[model]". */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type {
|
|
3
|
+
DateRangeCompareDraft,
|
|
3
4
|
DateRangePickerProperties,
|
|
4
5
|
DateRangePreset,
|
|
5
6
|
TimeDisplayBoundary
|
|
@@ -95,8 +96,11 @@
|
|
|
95
96
|
let draftStart: Date | null = $state(null);
|
|
96
97
|
let draftEnd: Date | null = $state(null);
|
|
97
98
|
let draftValue: Date | null = $state(null);
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
// The compare range is a draft too. It is handed to `compareCalendar` so a consumer's calendar
|
|
100
|
+
// has somewhere to write that Apply commits and Cancel discards; `compareSnapshot` is what the
|
|
101
|
+
// committed range was when the session opened, which is what Cancel restores.
|
|
102
|
+
const compareDraft: DateRangeCompareDraft = $state({ start: null, end: null });
|
|
103
|
+
let compareSnapshot: DateRangeCompareDraft = $state({ start: null, end: null });
|
|
100
104
|
|
|
101
105
|
// Tracks the label of the currently active preset, or null when the user
|
|
102
106
|
// made a custom calendar selection (or before any selection is made).
|
|
@@ -339,6 +343,67 @@
|
|
|
339
343
|
}
|
|
340
344
|
}
|
|
341
345
|
|
|
346
|
+
const sameInstant = (a: Date | null, b: Date | null): boolean =>
|
|
347
|
+
(a === null ? null : a.getTime()) === (b === null ? null : b.getTime());
|
|
348
|
+
|
|
349
|
+
// The compare calendar lives in the main panel unless a standalone `compareTrigger` gives it
|
|
350
|
+
// its own panel and its own Apply/Cancel.
|
|
351
|
+
const compareInMain: boolean = $derived(
|
|
352
|
+
typeof compareCalendar === 'function' && typeof compareTrigger !== 'function'
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
function seedCompareSession(): void {
|
|
356
|
+
compareSnapshot = { start: compareStart, end: compareEnd };
|
|
357
|
+
compareDraft.start = compareStart;
|
|
358
|
+
compareDraft.end = compareEnd;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// What Apply commits. A calendar bound to `compareDraft` edits the draft. A calendar bound
|
|
362
|
+
// straight to `compareStart`/`compareEnd` (how this component's docs wired it before the draft
|
|
363
|
+
// was exposed) edits the committed values in place and leaves the draft at its seed; Apply used
|
|
364
|
+
// to write that stale seed back over the pick, and could not be pressed at all on first use.
|
|
365
|
+
const pendingCompare = $derived.by(() => {
|
|
366
|
+
const edited =
|
|
367
|
+
!sameInstant(compareDraft.start, compareSnapshot.start) ||
|
|
368
|
+
!sameInstant(compareDraft.end, compareSnapshot.end);
|
|
369
|
+
return edited
|
|
370
|
+
? { start: compareDraft.start, end: compareDraft.end }
|
|
371
|
+
: { start: compareStart, end: compareEnd };
|
|
372
|
+
});
|
|
373
|
+
const canApplyCompare: boolean = $derived(
|
|
374
|
+
pendingCompare.start !== null && pendingCompare.end !== null
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
function commitCompare(): void {
|
|
378
|
+
const { start, end } = pendingCompare;
|
|
379
|
+
if (start === null || end === null) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
compareStart = start;
|
|
383
|
+
compareEnd = end;
|
|
384
|
+
onapplycompare?.({ compareStart: start, compareEnd: end, presetLabel: selectedPresetLabel });
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Cancel, Escape and an outside press all land here: whatever the session did to the draft or
|
|
388
|
+
// to the committed range goes back to what it was when the session opened.
|
|
389
|
+
function discardCompare(): void {
|
|
390
|
+
if (!sameInstant(compareStart, compareSnapshot.start)) {
|
|
391
|
+
compareStart = compareSnapshot.start;
|
|
392
|
+
}
|
|
393
|
+
if (!sameInstant(compareEnd, compareSnapshot.end)) {
|
|
394
|
+
compareEnd = compareSnapshot.end;
|
|
395
|
+
}
|
|
396
|
+
compareDraft.start = compareSnapshot.start;
|
|
397
|
+
compareDraft.end = compareSnapshot.end;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function abandonPicker(): void {
|
|
401
|
+
if (compareInMain) {
|
|
402
|
+
discardCompare();
|
|
403
|
+
}
|
|
404
|
+
closePicker();
|
|
405
|
+
}
|
|
406
|
+
|
|
342
407
|
function openPicker(): void {
|
|
343
408
|
// Pointer activation need not focus a button in WebKit. Return to the
|
|
344
409
|
// invoking control, not an unrelated previously focused field or body.
|
|
@@ -347,8 +412,9 @@
|
|
|
347
412
|
draftStart = rangeStart;
|
|
348
413
|
draftEnd = rangeEnd;
|
|
349
414
|
draftValue = value;
|
|
350
|
-
|
|
351
|
-
|
|
415
|
+
if (compareInMain) {
|
|
416
|
+
seedCompareSession();
|
|
417
|
+
}
|
|
352
418
|
|
|
353
419
|
// Re-seed the session from the committed preset so the sidebar re-highlights it
|
|
354
420
|
// by label. A direct calendar click later clears this, reverting to date matching.
|
|
@@ -500,7 +566,7 @@
|
|
|
500
566
|
// a second click feeling dead — the panel could only be closed by clicking away.
|
|
501
567
|
function togglePicker(): void {
|
|
502
568
|
if (isOpen) {
|
|
503
|
-
|
|
569
|
+
abandonPicker();
|
|
504
570
|
} else {
|
|
505
571
|
openPicker();
|
|
506
572
|
}
|
|
@@ -508,8 +574,7 @@
|
|
|
508
574
|
|
|
509
575
|
function openComparePicker(): void {
|
|
510
576
|
compareFocusReturnEl = compareTriggerRef ? findInteractive(compareTriggerRef) : null;
|
|
511
|
-
|
|
512
|
-
draftCompareEnd = compareEnd ?? null;
|
|
577
|
+
seedCompareSession();
|
|
513
578
|
openCompare = true;
|
|
514
579
|
tick().then(() => focusFirstElement(comparePanelRef));
|
|
515
580
|
}
|
|
@@ -524,19 +589,12 @@
|
|
|
524
589
|
}
|
|
525
590
|
|
|
526
591
|
function handleApplyCompare(): void {
|
|
527
|
-
|
|
528
|
-
compareStart = draftCompareStart;
|
|
529
|
-
compareEnd = draftCompareEnd;
|
|
530
|
-
onapplycompare?.({
|
|
531
|
-
compareStart: draftCompareStart,
|
|
532
|
-
compareEnd: draftCompareEnd,
|
|
533
|
-
presetLabel: selectedPresetLabel
|
|
534
|
-
});
|
|
535
|
-
}
|
|
592
|
+
commitCompare();
|
|
536
593
|
closeComparePicker();
|
|
537
594
|
}
|
|
538
595
|
|
|
539
596
|
function handleCancelCompare(): void {
|
|
597
|
+
discardCompare();
|
|
540
598
|
closeComparePicker();
|
|
541
599
|
}
|
|
542
600
|
|
|
@@ -568,18 +626,8 @@
|
|
|
568
626
|
presetLabel: selectedPresetLabel
|
|
569
627
|
});
|
|
570
628
|
}
|
|
571
|
-
if (
|
|
572
|
-
|
|
573
|
-
draftCompareStart !== null &&
|
|
574
|
-
draftCompareEnd !== null
|
|
575
|
-
) {
|
|
576
|
-
compareStart = draftCompareStart;
|
|
577
|
-
compareEnd = draftCompareEnd;
|
|
578
|
-
onapplycompare?.({
|
|
579
|
-
compareStart: draftCompareStart,
|
|
580
|
-
compareEnd: draftCompareEnd,
|
|
581
|
-
presetLabel: selectedPresetLabel
|
|
582
|
-
});
|
|
629
|
+
if (typeof compareCalendar === 'function') {
|
|
630
|
+
commitCompare();
|
|
583
631
|
}
|
|
584
632
|
} else {
|
|
585
633
|
if (draftValue !== null) {
|
|
@@ -601,7 +649,7 @@
|
|
|
601
649
|
|
|
602
650
|
function handleCancel(): void {
|
|
603
651
|
oncancel?.();
|
|
604
|
-
|
|
652
|
+
abandonPicker();
|
|
605
653
|
}
|
|
606
654
|
|
|
607
655
|
function handlePreset(preset: DateRangePreset): void {
|
|
@@ -893,9 +941,10 @@
|
|
|
893
941
|
outside both at once. */
|
|
894
942
|
function handleDismissOutside(): void {
|
|
895
943
|
if (isOpen) {
|
|
896
|
-
|
|
944
|
+
abandonPicker();
|
|
897
945
|
}
|
|
898
946
|
if (openCompare) {
|
|
947
|
+
discardCompare();
|
|
899
948
|
closeComparePicker();
|
|
900
949
|
}
|
|
901
950
|
}
|
|
@@ -1053,7 +1102,7 @@
|
|
|
1053
1102
|
>
|
|
1054
1103
|
{#if typeof compareCalendar === 'function'}
|
|
1055
1104
|
<div class="drp-compare-panel-body">
|
|
1056
|
-
{@render compareCalendar()}
|
|
1105
|
+
{@render compareCalendar(compareDraft)}
|
|
1057
1106
|
</div>
|
|
1058
1107
|
{/if}
|
|
1059
1108
|
<div class="drp-footer">
|
|
@@ -1066,7 +1115,7 @@
|
|
|
1066
1115
|
onclick={handleApplyCompare}
|
|
1067
1116
|
ariaLabel="Apply compare selection"
|
|
1068
1117
|
classes="drp-btn-apply"
|
|
1069
|
-
disabled={
|
|
1118
|
+
disabled={!canApplyCompare}>Apply</Button
|
|
1070
1119
|
>
|
|
1071
1120
|
</div>
|
|
1072
1121
|
</div>
|
|
@@ -1442,7 +1491,7 @@
|
|
|
1442
1491
|
causes a Svelte 5 runtime error. -->
|
|
1443
1492
|
{#if typeof compareCalendar === 'function' && typeof compareTrigger !== 'function'}
|
|
1444
1493
|
<div class="drp-compare-section">
|
|
1445
|
-
{@render compareCalendar()}
|
|
1494
|
+
{@render compareCalendar(compareDraft)}
|
|
1446
1495
|
</div>
|
|
1447
1496
|
{/if}
|
|
1448
1497
|
</div>
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
+
/**
|
|
3
|
+
* The compare range a `compareCalendar` snippet edits before Apply commits it. A plain
|
|
4
|
+
* reactive object, so a Calendar can bind to its fields.
|
|
5
|
+
*/
|
|
6
|
+
export type DateRangeCompareDraft = {
|
|
7
|
+
start: Date | null;
|
|
8
|
+
end: Date | null;
|
|
9
|
+
};
|
|
2
10
|
export type DateRangePreset = {
|
|
3
11
|
label: string;
|
|
4
12
|
getValue: () => {
|
|
@@ -102,8 +110,16 @@ export type OptionalDateRangePickerProperties = {
|
|
|
102
110
|
compareStart?: Date | null;
|
|
103
111
|
/** End of compare range (bindable). Used when compareCalendar snippet is provided. */
|
|
104
112
|
compareEnd?: Date | null;
|
|
105
|
-
/**
|
|
106
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Snippet rendered in the compare-calendar slot. Consumer controls all compare UI.
|
|
115
|
+
*
|
|
116
|
+
* It receives the compare draft, a reactive `{ start, end }` that Apply commits and that
|
|
117
|
+
* Cancel, Escape and an outside press discard. Bind a Calendar to it:
|
|
118
|
+
* `bind:rangeStart={draft.start} bind:rangeEnd={draft.end}`. A snippet that takes no
|
|
119
|
+
* argument and edits `compareStart`/`compareEnd` directly still works: Apply keeps what
|
|
120
|
+
* it wrote, and Cancel restores the range the session opened with.
|
|
121
|
+
*/
|
|
122
|
+
compareCalendar?: Snippet<[DateRangeCompareDraft]>;
|
|
107
123
|
/** Which day of the week starts the calendar. 0=Sunday, 1=Monday. */
|
|
108
124
|
weekStartsOn?: 0 | 1;
|
|
109
125
|
/** BCP-47 locale string for date formatting. */
|
|
@@ -289,10 +289,10 @@
|
|
|
289
289
|
gap: var(--tool-call-log-gap, 8px);
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
-
/* Bespoke <button> markup (
|
|
293
|
-
|
|
294
|
-
pulled from Pill's own tokens (line-height, cursor) so it reads as
|
|
295
|
-
of control as the diffstat Pill badges nested inside it. */
|
|
292
|
+
/* Bespoke <button> markup (the chip holds mixed content -- spinner, label, meta and nested
|
|
293
|
+
diffstat Pill badges -- where Pill renders one `text` string; see docs), but its sizing
|
|
294
|
+
recipe is deliberately pulled from Pill's own tokens (line-height, cursor) so it reads as
|
|
295
|
+
the same family of control as the diffstat Pill badges nested inside it. */
|
|
296
296
|
.chip {
|
|
297
297
|
display: inline-flex;
|
|
298
298
|
align-items: center;
|