@juspay/svelte-ui-components 2.41.0 → 2.43.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/DateRangePicker/DateRangePicker.svelte +204 -20
- package/dist/DateRangePicker/DateRangePicker.svelte.d.ts +1 -1
- package/dist/DateRangePicker/properties.d.ts +17 -0
- package/dist/Select/Select.svelte +29 -2
- package/dist/Select/properties.d.ts +5 -0
- package/dist/Toggle/Toggle.svelte +24 -5
- package/dist/Toggle/properties.d.ts +5 -2
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { DateRangePickerProperties, DateRangePreset } from './properties';
|
|
3
|
-
import { untrack } from 'svelte';
|
|
3
|
+
import { tick, untrack } from 'svelte';
|
|
4
4
|
import { SvelteDate } from 'svelte/reactivity';
|
|
5
5
|
import Calendar from '../Calendar/Calendar.svelte';
|
|
6
6
|
import Button from '../Button/Button.svelte';
|
|
@@ -30,6 +30,8 @@
|
|
|
30
30
|
triggerIcon,
|
|
31
31
|
clearable = false,
|
|
32
32
|
initialPresetLabel,
|
|
33
|
+
compareTrigger,
|
|
34
|
+
openCompare = $bindable(false),
|
|
33
35
|
onapply,
|
|
34
36
|
onapplysingle,
|
|
35
37
|
onapplycompare,
|
|
@@ -87,6 +89,10 @@
|
|
|
87
89
|
let isOpen: boolean = $state(false);
|
|
88
90
|
let panelRef: HTMLDivElement | null = $state(null);
|
|
89
91
|
let triggerRef: HTMLDivElement | null = $state(null);
|
|
92
|
+
let comparePanelRef: HTMLDivElement | null = $state(null);
|
|
93
|
+
let compareTriggerRef: HTMLDivElement | null = $state(null);
|
|
94
|
+
// Stores the element that opened the compare panel so focus can be restored on close.
|
|
95
|
+
let compareFocusReturnEl: HTMLElement | null = null;
|
|
90
96
|
|
|
91
97
|
// Tracks the active preset label for the trigger display (seeded from initialPresetLabel on mount)
|
|
92
98
|
const resolvedInitialPresetLabel: string | null = untrack(() => {
|
|
@@ -104,15 +110,15 @@
|
|
|
104
110
|
|
|
105
111
|
let activePresetLabel: string | null = $state(resolvedInitialPresetLabel);
|
|
106
112
|
|
|
113
|
+
const now = new SvelteDate();
|
|
114
|
+
|
|
107
115
|
// Dual-month navigation: track the year+month of the left calendar
|
|
108
|
-
let leftYear: number = $state(
|
|
109
|
-
let leftMonth: number = $state(
|
|
116
|
+
let leftYear: number = $state(now.getFullYear());
|
|
117
|
+
let leftMonth: number = $state(now.getMonth());
|
|
110
118
|
|
|
111
119
|
// A key counter — incrementing forces Calendar to re-mount with new initialMonth
|
|
112
120
|
let calendarKey: number = $state(0);
|
|
113
121
|
|
|
114
|
-
const now = new SvelteDate();
|
|
115
|
-
|
|
116
122
|
const leftInitialMonth: Date = $derived(new SvelteDate(leftYear, leftMonth, 1));
|
|
117
123
|
const rightInitialMonth: Date = $derived(new SvelteDate(leftYear, leftMonth + 1, 1));
|
|
118
124
|
|
|
@@ -145,6 +151,13 @@
|
|
|
145
151
|
return placeholder;
|
|
146
152
|
});
|
|
147
153
|
|
|
154
|
+
const compareTriggerLabel: string = $derived.by(() => {
|
|
155
|
+
if (compareStart !== null && compareEnd !== null) {
|
|
156
|
+
return `${formatDate(compareStart)} – ${formatDate(compareEnd)}`;
|
|
157
|
+
}
|
|
158
|
+
return placeholder;
|
|
159
|
+
});
|
|
160
|
+
|
|
148
161
|
function openPicker(): void {
|
|
149
162
|
// Seed draft from current committed values
|
|
150
163
|
draftStart = rangeStart !== null ? rangeStart : null;
|
|
@@ -171,6 +184,46 @@
|
|
|
171
184
|
onopentoggle?.({ open: false });
|
|
172
185
|
}
|
|
173
186
|
|
|
187
|
+
function openComparePicker(): void {
|
|
188
|
+
compareFocusReturnEl =
|
|
189
|
+
document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
190
|
+
draftCompareStart = compareStart ?? null;
|
|
191
|
+
draftCompareEnd = compareEnd ?? null;
|
|
192
|
+
openCompare = true;
|
|
193
|
+
tick().then(() => {
|
|
194
|
+
const firstFocusable = comparePanelRef?.querySelector<HTMLElement>(
|
|
195
|
+
'button:not([disabled]), [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
196
|
+
);
|
|
197
|
+
firstFocusable?.focus();
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function closeComparePicker(): void {
|
|
202
|
+
openCompare = false;
|
|
203
|
+
const returnEl = compareFocusReturnEl;
|
|
204
|
+
compareFocusReturnEl = null;
|
|
205
|
+
tick().then(() => {
|
|
206
|
+
returnEl?.focus();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function handleApplyCompare(): void {
|
|
211
|
+
if (draftCompareStart !== null && draftCompareEnd !== null) {
|
|
212
|
+
compareStart = draftCompareStart;
|
|
213
|
+
compareEnd = draftCompareEnd;
|
|
214
|
+
onapplycompare?.({
|
|
215
|
+
compareStart: draftCompareStart,
|
|
216
|
+
compareEnd: draftCompareEnd,
|
|
217
|
+
presetLabel: selectedPresetLabel
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
closeComparePicker();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function handleCancelCompare(): void {
|
|
224
|
+
closeComparePicker();
|
|
225
|
+
}
|
|
226
|
+
|
|
174
227
|
function navigateDualMonths(delta: number): void {
|
|
175
228
|
const next = new SvelteDate(leftYear, leftMonth + delta, 1);
|
|
176
229
|
leftYear = next.getFullYear();
|
|
@@ -253,7 +306,7 @@
|
|
|
253
306
|
|
|
254
307
|
// Close on outside-click
|
|
255
308
|
function handleDocumentClick(event: MouseEvent): void {
|
|
256
|
-
if (!isOpen) {
|
|
309
|
+
if (!isOpen && !openCompare) {
|
|
257
310
|
return;
|
|
258
311
|
}
|
|
259
312
|
const target = event.target;
|
|
@@ -262,14 +315,35 @@
|
|
|
262
315
|
}
|
|
263
316
|
const clickedInsidePanel = panelRef !== null && panelRef.contains(target);
|
|
264
317
|
const clickedTrigger = triggerRef !== null && triggerRef.contains(target);
|
|
265
|
-
|
|
318
|
+
const clickedInsideComparePanel = comparePanelRef !== null && comparePanelRef.contains(target);
|
|
319
|
+
const clickedCompareTrigger = compareTriggerRef !== null && compareTriggerRef.contains(target);
|
|
320
|
+
if (
|
|
321
|
+
isOpen &&
|
|
322
|
+
!clickedInsidePanel &&
|
|
323
|
+
!clickedTrigger &&
|
|
324
|
+
!clickedInsideComparePanel &&
|
|
325
|
+
!clickedCompareTrigger
|
|
326
|
+
) {
|
|
266
327
|
closePicker();
|
|
267
328
|
}
|
|
329
|
+
if (
|
|
330
|
+
openCompare &&
|
|
331
|
+
!clickedInsideComparePanel &&
|
|
332
|
+
!clickedCompareTrigger &&
|
|
333
|
+
!clickedInsidePanel &&
|
|
334
|
+
!clickedTrigger
|
|
335
|
+
) {
|
|
336
|
+
closeComparePicker();
|
|
337
|
+
}
|
|
268
338
|
}
|
|
269
339
|
|
|
270
340
|
function handleDocumentKeyDown(event: KeyboardEvent): void {
|
|
271
|
-
if (event.key === 'Escape'
|
|
272
|
-
|
|
341
|
+
if (event.key === 'Escape') {
|
|
342
|
+
if (openCompare) {
|
|
343
|
+
handleCancelCompare();
|
|
344
|
+
} else if (isOpen) {
|
|
345
|
+
handleCancel();
|
|
346
|
+
}
|
|
273
347
|
}
|
|
274
348
|
}
|
|
275
349
|
|
|
@@ -340,6 +414,75 @@
|
|
|
340
414
|
</Button>
|
|
341
415
|
</div>
|
|
342
416
|
|
|
417
|
+
<!-- Compare trigger wrapper (standalone): position:relative so the compare panel
|
|
418
|
+
anchors below this trigger rather than the drp-root corner. -->
|
|
419
|
+
{#if typeof compareTrigger === 'function'}
|
|
420
|
+
<div bind:this={compareTriggerRef} class="drp-compare-trigger-wrapper">
|
|
421
|
+
<Button
|
|
422
|
+
onclick={openComparePicker}
|
|
423
|
+
ariaLabel="Open compare period picker"
|
|
424
|
+
classes="drp-compare-trigger {openCompare ? 'drp-compare-trigger-open' : ''}"
|
|
425
|
+
>
|
|
426
|
+
{@render compareTrigger(compareTriggerLabel)}
|
|
427
|
+
</Button>
|
|
428
|
+
|
|
429
|
+
<!-- Compare period picker panel (standalone): nested here so position:absolute
|
|
430
|
+
resolves against .drp-compare-trigger-wrapper, not .drp-root. -->
|
|
431
|
+
{#if openCompare}
|
|
432
|
+
<div
|
|
433
|
+
bind:this={comparePanelRef}
|
|
434
|
+
class="drp-compare-panel"
|
|
435
|
+
role="dialog"
|
|
436
|
+
aria-label="Compare period picker"
|
|
437
|
+
aria-modal="true"
|
|
438
|
+
tabindex="-1"
|
|
439
|
+
onkeydown={(event) => {
|
|
440
|
+
if (event.key === 'Tab') {
|
|
441
|
+
const focusable = comparePanelRef?.querySelectorAll<HTMLElement>(
|
|
442
|
+
'button:not([disabled]), [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
443
|
+
);
|
|
444
|
+
if (!focusable || focusable.length === 0) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const first = focusable[0];
|
|
448
|
+
const last = focusable[focusable.length - 1];
|
|
449
|
+
if (event.shiftKey) {
|
|
450
|
+
if (document.activeElement === first) {
|
|
451
|
+
event.preventDefault();
|
|
452
|
+
last.focus();
|
|
453
|
+
}
|
|
454
|
+
} else {
|
|
455
|
+
if (document.activeElement === last) {
|
|
456
|
+
event.preventDefault();
|
|
457
|
+
first.focus();
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}}
|
|
462
|
+
>
|
|
463
|
+
{#if typeof compareCalendar === 'function'}
|
|
464
|
+
<div class="drp-compare-panel-body">
|
|
465
|
+
{@render compareCalendar()}
|
|
466
|
+
</div>
|
|
467
|
+
{/if}
|
|
468
|
+
<div class="drp-footer">
|
|
469
|
+
<Button
|
|
470
|
+
onclick={handleCancelCompare}
|
|
471
|
+
ariaLabel="Cancel compare selection"
|
|
472
|
+
classes="drp-btn-cancel">Cancel</Button
|
|
473
|
+
>
|
|
474
|
+
<Button
|
|
475
|
+
onclick={handleApplyCompare}
|
|
476
|
+
ariaLabel="Apply compare selection"
|
|
477
|
+
classes="drp-btn-apply"
|
|
478
|
+
disabled={draftCompareStart === null || draftCompareEnd === null}>Apply</Button
|
|
479
|
+
>
|
|
480
|
+
</div>
|
|
481
|
+
</div>
|
|
482
|
+
{/if}
|
|
483
|
+
</div>
|
|
484
|
+
{/if}
|
|
485
|
+
|
|
343
486
|
<!-- Dropdown panel -->
|
|
344
487
|
{#if isOpen}
|
|
345
488
|
<div
|
|
@@ -464,8 +607,11 @@
|
|
|
464
607
|
</div>
|
|
465
608
|
{/if}
|
|
466
609
|
|
|
467
|
-
<!-- Compare calendar slot:
|
|
468
|
-
|
|
610
|
+
<!-- Compare calendar slot: only rendered here when there is no standalone
|
|
611
|
+
compareTrigger. When compareTrigger is provided the consumer uses the
|
|
612
|
+
separate compare panel; rendering the same Snippet in two places at once
|
|
613
|
+
causes a Svelte 5 runtime error. -->
|
|
614
|
+
{#if typeof compareCalendar === 'function' && typeof compareTrigger !== 'function'}
|
|
469
615
|
<div class="drp-compare-section">
|
|
470
616
|
{@render compareCalendar()}
|
|
471
617
|
</div>
|
|
@@ -516,20 +662,22 @@
|
|
|
516
662
|
--button-padding: var(--drp-trigger-padding, 8px 12px);
|
|
517
663
|
--button-min-width: var(--drp-trigger-min-width, 200px);
|
|
518
664
|
--button-gap: var(--drp-trigger-gap, 8px);
|
|
519
|
-
--button-hover-border
|
|
665
|
+
--button-hover-border: var(
|
|
666
|
+
--drp-trigger-hover-border,
|
|
667
|
+
var(--drp-trigger-border, 1px solid currentColor)
|
|
668
|
+
);
|
|
520
669
|
}
|
|
521
670
|
|
|
522
|
-
:global(.drp-trigger) {
|
|
523
|
-
display: inline-flex
|
|
671
|
+
:global(.drp-trigger-wrapper .drp-trigger) {
|
|
672
|
+
display: inline-flex;
|
|
524
673
|
align-items: center;
|
|
525
674
|
gap: var(--drp-trigger-gap, 8px);
|
|
526
675
|
white-space: nowrap;
|
|
527
676
|
min-width: var(--drp-trigger-min-width, 200px);
|
|
528
|
-
font-family: inherit;
|
|
529
677
|
}
|
|
530
678
|
|
|
531
|
-
:global(.drp-trigger-open) {
|
|
532
|
-
border-color: var(--drp-trigger-open-border-color, #000000)
|
|
679
|
+
:global(.drp-trigger-wrapper .drp-trigger-open) {
|
|
680
|
+
border-color: var(--drp-trigger-open-border-color, #000000);
|
|
533
681
|
box-shadow: var(--drp-trigger-open-shadow, 0 0 0 2px rgba(0, 0, 0, 0.1));
|
|
534
682
|
}
|
|
535
683
|
|
|
@@ -591,7 +739,6 @@
|
|
|
591
739
|
cursor: pointer;
|
|
592
740
|
white-space: nowrap;
|
|
593
741
|
transition: background 0.12s ease;
|
|
594
|
-
font-family: inherit;
|
|
595
742
|
}
|
|
596
743
|
|
|
597
744
|
.drp-preset-item:hover {
|
|
@@ -778,7 +925,44 @@
|
|
|
778
925
|
--button-border: none;
|
|
779
926
|
--button-text-color: var(--drp-apply-color, #ffffff);
|
|
780
927
|
--button-hover-color: var(--drp-apply-hover-background, #333333);
|
|
781
|
-
--
|
|
782
|
-
--
|
|
928
|
+
--disabled-background-color: var(--drp-apply-disabled-background, #cccccc);
|
|
929
|
+
--disabled-text-color: var(--drp-apply-disabled-color, #888888);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
/* ── Compare standalone trigger ── */
|
|
933
|
+
.drp-compare-trigger-wrapper {
|
|
934
|
+
display: inline-block;
|
|
935
|
+
position: relative;
|
|
936
|
+
--button-color: var(--drp-compare-trigger-background, inherit);
|
|
937
|
+
--button-border: var(--drp-compare-trigger-border, 1px solid currentColor);
|
|
938
|
+
--button-border-radius: var(--drp-compare-trigger-border-radius, 6px);
|
|
939
|
+
--button-text-color: var(--drp-compare-trigger-color, inherit);
|
|
940
|
+
--button-padding: var(--drp-compare-trigger-padding, 8px 12px);
|
|
941
|
+
--button-min-width: var(--drp-compare-trigger-min-width, 160px);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
:global(.drp-compare-trigger-open) {
|
|
945
|
+
border-color: var(--drp-trigger-open-border-color, #000000);
|
|
946
|
+
box-shadow: var(--drp-trigger-open-shadow, 0 0 0 2px rgba(0, 0, 0, 0.1));
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/* ── Compare standalone panel ── */
|
|
950
|
+
.drp-compare-panel {
|
|
951
|
+
position: absolute;
|
|
952
|
+
top: calc(100% + var(--drp-panel-offset, 6px));
|
|
953
|
+
left: var(--drp-compare-panel-left, 0);
|
|
954
|
+
z-index: var(--drp-panel-z-index, 1000);
|
|
955
|
+
background: var(--drp-panel-background, inherit);
|
|
956
|
+
border: var(--drp-panel-border, 1px solid #e0e0e0);
|
|
957
|
+
border-radius: var(--drp-panel-border-radius, 10px);
|
|
958
|
+
box-shadow: var(--drp-panel-shadow, 0 8px 24px rgba(0, 0, 0, 0.12));
|
|
959
|
+
display: flex;
|
|
960
|
+
flex-direction: column;
|
|
961
|
+
min-width: var(--drp-compare-panel-min-width, 280px);
|
|
962
|
+
overflow: hidden;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.drp-compare-panel-body {
|
|
966
|
+
padding: var(--drp-calendars-padding, 16px);
|
|
783
967
|
}
|
|
784
968
|
</style>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { DateRangePickerProperties } from './properties';
|
|
2
|
-
declare const DateRangePicker: import("svelte").Component<DateRangePickerProperties, {}, "value" | "rangeStart" | "rangeEnd" | "compareStart" | "compareEnd">;
|
|
2
|
+
declare const DateRangePicker: import("svelte").Component<DateRangePickerProperties, {}, "value" | "rangeStart" | "rangeEnd" | "compareStart" | "compareEnd" | "openCompare">;
|
|
3
3
|
type DateRangePicker = ReturnType<typeof DateRangePicker>;
|
|
4
4
|
export default DateRangePicker;
|
|
@@ -70,6 +70,23 @@ export type OptionalDateRangePickerProperties = {
|
|
|
70
70
|
* Only takes effect once on mount; subsequent prop changes are not tracked.
|
|
71
71
|
*/
|
|
72
72
|
initialPresetLabel?: string;
|
|
73
|
+
/**
|
|
74
|
+
* Snippet rendered as the standalone compare-period trigger. When provided,
|
|
75
|
+
* the component renders this snippet in its own wrapper div
|
|
76
|
+
* (.drp-compare-trigger-wrapper) adjacent to the main trigger. Clicking the
|
|
77
|
+
* wrapper toggles openCompare. Receives the current compare trigger label
|
|
78
|
+
* string as its argument (formatted "start – end" or placeholder).
|
|
79
|
+
*/
|
|
80
|
+
compareTrigger?: Snippet<[string]>;
|
|
81
|
+
/**
|
|
82
|
+
* Bindable. Controls whether the compare-period picker panel is open.
|
|
83
|
+
* The component writes back on open/close, so `bind:openCompare` lets a
|
|
84
|
+
* parent open/close it programmatically or observe the state. When
|
|
85
|
+
* compareTrigger is not provided this prop is still functional — the parent
|
|
86
|
+
* can drive it without a built-in trigger element.
|
|
87
|
+
* Default: false.
|
|
88
|
+
*/
|
|
89
|
+
openCompare?: boolean;
|
|
73
90
|
};
|
|
74
91
|
export type DateRangePickerEventProperties = {
|
|
75
92
|
/** Fired when the user clicks Apply in range mode. presetLabel is the sidebar preset name if one was active, or null for a custom calendar selection. */
|
|
@@ -17,9 +17,12 @@
|
|
|
17
17
|
testId,
|
|
18
18
|
itemTestId,
|
|
19
19
|
onchange,
|
|
20
|
+
onopen,
|
|
21
|
+
onclose,
|
|
20
22
|
classes,
|
|
21
23
|
open = $bindable(false),
|
|
22
|
-
dropdownAlign = 'left'
|
|
24
|
+
dropdownAlign = 'left',
|
|
25
|
+
hierarchy = 'default'
|
|
23
26
|
}: SelectProperties = $props();
|
|
24
27
|
|
|
25
28
|
function normalizeItems(source: SelectItem[] | string[]): SelectItem[] {
|
|
@@ -65,6 +68,7 @@
|
|
|
65
68
|
return;
|
|
66
69
|
}
|
|
67
70
|
open = true;
|
|
71
|
+
onopen?.();
|
|
68
72
|
highlightedIndex = -1;
|
|
69
73
|
query = '';
|
|
70
74
|
if (searchable) {
|
|
@@ -77,6 +81,7 @@
|
|
|
77
81
|
|
|
78
82
|
function close(): void {
|
|
79
83
|
open = false;
|
|
84
|
+
onclose?.();
|
|
80
85
|
query = '';
|
|
81
86
|
highlightedIndex = -1;
|
|
82
87
|
}
|
|
@@ -208,7 +213,7 @@
|
|
|
208
213
|
}
|
|
209
214
|
query = event.target.value;
|
|
210
215
|
if (!open) {
|
|
211
|
-
|
|
216
|
+
openDropdown();
|
|
212
217
|
}
|
|
213
218
|
highlightedIndex = -1;
|
|
214
219
|
}
|
|
@@ -233,6 +238,9 @@
|
|
|
233
238
|
document.addEventListener('click', handleClickOutside);
|
|
234
239
|
return () => {
|
|
235
240
|
document.removeEventListener('click', handleClickOutside);
|
|
241
|
+
if (open) {
|
|
242
|
+
onclose?.();
|
|
243
|
+
}
|
|
236
244
|
};
|
|
237
245
|
});
|
|
238
246
|
</script>
|
|
@@ -241,6 +249,7 @@
|
|
|
241
249
|
class="select {classes ?? ''}"
|
|
242
250
|
class:open
|
|
243
251
|
class:disabled
|
|
252
|
+
class:ghost={hierarchy === 'ghost'}
|
|
244
253
|
bind:this={containerEl}
|
|
245
254
|
{...typeof testId === 'string' ? { 'data-pw': testId } : {}}
|
|
246
255
|
>
|
|
@@ -552,4 +561,22 @@
|
|
|
552
561
|
--pill-padding: var(--select-pill-padding, 2px 8px);
|
|
553
562
|
--pill-font-size: var(--select-pill-font-size, 13px);
|
|
554
563
|
}
|
|
564
|
+
|
|
565
|
+
/* Ghost hierarchy: transparent, borderless trigger */
|
|
566
|
+
.select.ghost .select-trigger {
|
|
567
|
+
background: var(--select-ghost-trigger-background, transparent);
|
|
568
|
+
border-color: var(--select-ghost-trigger-border-color, transparent);
|
|
569
|
+
box-shadow: none;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
.select.ghost .select-trigger:hover:not(.disabled .select-trigger) {
|
|
573
|
+
border-color: var(--select-ghost-trigger-hover-border-color, transparent);
|
|
574
|
+
background: var(--select-ghost-trigger-hover-background, rgba(0, 0, 0, 0.04));
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.select.ghost.open .select-trigger {
|
|
578
|
+
border-color: var(--select-ghost-trigger-open-border-color, transparent);
|
|
579
|
+
background: var(--select-ghost-trigger-open-background, rgba(0, 0, 0, 0.06));
|
|
580
|
+
box-shadow: none;
|
|
581
|
+
}
|
|
555
582
|
</style>
|
|
@@ -9,6 +9,7 @@ export type SelectItem = {
|
|
|
9
9
|
export type MandatorySelectProperties = {
|
|
10
10
|
items: SelectItem[] | string[];
|
|
11
11
|
};
|
|
12
|
+
export type SelectHierarchy = 'default' | 'ghost';
|
|
12
13
|
export type OptionalSelectProperties = {
|
|
13
14
|
value?: string[];
|
|
14
15
|
multiple?: boolean;
|
|
@@ -32,7 +33,11 @@ export type OptionalSelectProperties = {
|
|
|
32
33
|
value: string[];
|
|
33
34
|
items: SelectItem[];
|
|
34
35
|
}]>;
|
|
36
|
+
/** Visual hierarchy of the trigger. `'ghost'` renders a transparent, borderless trigger — useful when the Select is embedded in a toolbar or header where a full bordered input would be visually heavy. Defaults to `'default'`. */
|
|
37
|
+
hierarchy?: SelectHierarchy;
|
|
35
38
|
};
|
|
36
39
|
export type SelectEventProperties = {
|
|
37
40
|
onchange?: (value: string[]) => void;
|
|
41
|
+
onopen?: () => void;
|
|
42
|
+
onclose?: () => void;
|
|
38
43
|
};
|
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ToggleProperties } from './properties';
|
|
3
3
|
|
|
4
|
-
let {
|
|
4
|
+
let {
|
|
5
|
+
checked = false,
|
|
6
|
+
text = '',
|
|
7
|
+
disabled = false,
|
|
8
|
+
testId,
|
|
9
|
+
classes,
|
|
10
|
+
onclick
|
|
11
|
+
}: ToggleProperties = $props();
|
|
5
12
|
|
|
6
|
-
|
|
13
|
+
const handleCheckboxClick = (e: MouseEvent): void => {
|
|
7
14
|
if (e.target instanceof HTMLInputElement && typeof e.target.checked === 'boolean') {
|
|
8
15
|
checked = e.target.checked;
|
|
9
16
|
}
|
|
10
17
|
onclick?.(checked);
|
|
11
|
-
}
|
|
18
|
+
};
|
|
12
19
|
</script>
|
|
13
20
|
|
|
14
|
-
<div class="container {classes ?? ''}">
|
|
21
|
+
<div class="container {classes ?? ''}" class:disabled aria-disabled={disabled} data-pw={testId}>
|
|
15
22
|
<div class="text" hidden={text.length === 0}>{text}</div>
|
|
16
23
|
<label class="switch">
|
|
17
|
-
<input
|
|
24
|
+
<input
|
|
25
|
+
class="input-checkbox"
|
|
26
|
+
type="checkbox"
|
|
27
|
+
{checked}
|
|
28
|
+
{disabled}
|
|
29
|
+
onclick={handleCheckboxClick}
|
|
30
|
+
/>
|
|
18
31
|
<span class="slider round"></span>
|
|
19
32
|
</label>
|
|
20
33
|
</div>
|
|
@@ -26,6 +39,12 @@
|
|
|
26
39
|
gap: var(--toggle-container-gap, 8px);
|
|
27
40
|
}
|
|
28
41
|
|
|
42
|
+
.container.disabled {
|
|
43
|
+
opacity: var(--toggle-disabled-opacity, 0.4);
|
|
44
|
+
cursor: not-allowed;
|
|
45
|
+
pointer-events: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
29
48
|
.text {
|
|
30
49
|
font-size: var(--toggle-text-font-size, 14px);
|
|
31
50
|
font-weight: var(--toggle-text-font-weight, 400);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
export type ToggleProperties =
|
|
1
|
+
export type ToggleProperties = OptionalToggleProperties & ToggleEventProperties;
|
|
2
|
+
export type OptionalToggleProperties = {
|
|
3
|
+
text?: string;
|
|
2
4
|
checked?: boolean;
|
|
3
|
-
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
testId?: string;
|
|
4
7
|
classes?: string;
|
|
5
8
|
};
|
|
6
9
|
export type ToggleEventProperties = {
|