@juspay/svelte-ui-components 2.28.2 → 2.28.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/Banner/Banner.svelte +34 -7
- package/dist/Banner/properties.d.ts +2 -0
- package/dist/Breadcrumb/Breadcrumb.svelte +58 -0
- package/dist/Breadcrumb/Breadcrumb.svelte.d.ts +4 -0
- package/dist/Breadcrumb/properties.d.ts +19 -0
- package/dist/Breadcrumb/properties.js +1 -0
- package/dist/Calendar/Calendar.svelte +10 -4
- package/dist/Calendar/properties.d.ts +2 -0
- package/dist/DateRangePicker/DateRangePicker.svelte +627 -0
- package/dist/DateRangePicker/DateRangePicker.svelte.d.ts +4 -0
- package/dist/DateRangePicker/properties.d.ts +74 -0
- package/dist/DateRangePicker/properties.js +1 -0
- package/dist/EmptyState/EmptyState.svelte +3 -1
- package/dist/EmptyState/properties.d.ts +1 -1
- package/dist/FileInput/FileInput.svelte +176 -0
- package/dist/FileInput/FileInput.svelte.d.ts +4 -0
- package/dist/FileInput/properties.d.ts +18 -0
- package/dist/FileInput/properties.js +1 -0
- package/dist/Modal/Modal.svelte +5 -4
- package/dist/Pagination/Pagination.svelte +56 -11
- package/dist/Pagination/properties.d.ts +15 -0
- package/dist/Pill/Pill.svelte +10 -0
- package/dist/Pill/properties.d.ts +7 -0
- package/dist/Select/Select.svelte +69 -29
- package/dist/Select/Select.svelte.d.ts +1 -1
- package/dist/Select/properties.d.ts +8 -1
- package/dist/Sheet/Sheet.svelte +16 -0
- package/dist/Sheet/properties.d.ts +4 -0
- package/dist/Table/Table.svelte +28 -6
- package/dist/Table/properties.d.ts +6 -0
- package/dist/Tabs/Tabs.svelte +52 -8
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,627 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { DateRangePickerProperties, DateRangePreset } from './properties';
|
|
3
|
+
import { SvelteDate } from 'svelte/reactivity';
|
|
4
|
+
import Calendar from '../Calendar/Calendar.svelte';
|
|
5
|
+
import Button from '../Button/Button.svelte';
|
|
6
|
+
import chevronDownSvg from '../assets/chevron-down.svg?raw';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
rangeStart = $bindable(null),
|
|
10
|
+
rangeEnd = $bindable(null),
|
|
11
|
+
value = $bindable(null),
|
|
12
|
+
mode = 'range',
|
|
13
|
+
minDate = null,
|
|
14
|
+
maxDate = null,
|
|
15
|
+
disabledDates = [],
|
|
16
|
+
presets = null,
|
|
17
|
+
placeholder = 'Select date',
|
|
18
|
+
dualMonth,
|
|
19
|
+
timePicker,
|
|
20
|
+
compareStart = $bindable(null),
|
|
21
|
+
compareEnd = $bindable(null),
|
|
22
|
+
compareCalendar,
|
|
23
|
+
weekStartsOn = 0,
|
|
24
|
+
locale,
|
|
25
|
+
testId,
|
|
26
|
+
classes,
|
|
27
|
+
triggerSnippet,
|
|
28
|
+
triggerIcon,
|
|
29
|
+
onapply,
|
|
30
|
+
onapplysingle,
|
|
31
|
+
onapplycompare,
|
|
32
|
+
oncancel,
|
|
33
|
+
onopentoggle
|
|
34
|
+
}: DateRangePickerProperties = $props();
|
|
35
|
+
|
|
36
|
+
const isDualMonth: boolean = $derived(
|
|
37
|
+
typeof dualMonth === 'boolean' ? dualMonth : mode === 'range'
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Draft state — only committed on Apply
|
|
41
|
+
let draftStart: Date | null = $state(null);
|
|
42
|
+
let draftEnd: Date | null = $state(null);
|
|
43
|
+
let draftValue: Date | null = $state(null);
|
|
44
|
+
let draftCompareStart: Date | null = $state(null);
|
|
45
|
+
let draftCompareEnd: Date | null = $state(null);
|
|
46
|
+
|
|
47
|
+
let isOpen: boolean = $state(false);
|
|
48
|
+
let panelRef: HTMLDivElement | null = $state(null);
|
|
49
|
+
let triggerRef: HTMLDivElement | null = $state(null);
|
|
50
|
+
|
|
51
|
+
// Dual-month navigation: track the year+month of the left calendar
|
|
52
|
+
let leftYear: number = $state(new SvelteDate().getFullYear());
|
|
53
|
+
let leftMonth: number = $state(new SvelteDate().getMonth());
|
|
54
|
+
|
|
55
|
+
// A key counter — incrementing forces Calendar to re-mount with new initialMonth
|
|
56
|
+
let calendarKey: number = $state(0);
|
|
57
|
+
|
|
58
|
+
const now = new SvelteDate();
|
|
59
|
+
|
|
60
|
+
const leftInitialMonth: Date = $derived(new SvelteDate(leftYear, leftMonth, 1));
|
|
61
|
+
const rightInitialMonth: Date = $derived(new SvelteDate(leftYear, leftMonth + 1, 1));
|
|
62
|
+
|
|
63
|
+
// Right calendar header label (derived from leftInitialMonth)
|
|
64
|
+
const rightMonthLabel: string = $derived(
|
|
65
|
+
rightInitialMonth.toLocaleDateString(locale, { month: 'long', year: 'numeric' })
|
|
66
|
+
);
|
|
67
|
+
const leftMonthLabel: string = $derived(
|
|
68
|
+
leftInitialMonth.toLocaleDateString(locale, { month: 'long', year: 'numeric' })
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
function formatDate(d: Date): string {
|
|
72
|
+
return d.toLocaleDateString(locale, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const triggerLabel: string = $derived.by(() => {
|
|
76
|
+
if (mode === 'single') {
|
|
77
|
+
return value !== null ? formatDate(value) : placeholder;
|
|
78
|
+
}
|
|
79
|
+
if (rangeStart !== null && rangeEnd !== null) {
|
|
80
|
+
return `${formatDate(rangeStart)} – ${formatDate(rangeEnd)}`;
|
|
81
|
+
}
|
|
82
|
+
if (rangeStart !== null) {
|
|
83
|
+
return `${formatDate(rangeStart)} – ...`;
|
|
84
|
+
}
|
|
85
|
+
return placeholder;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
function openPicker(): void {
|
|
89
|
+
// Seed draft from current committed values
|
|
90
|
+
draftStart = rangeStart !== null ? rangeStart : null;
|
|
91
|
+
draftEnd = rangeEnd !== null ? rangeEnd : null;
|
|
92
|
+
draftValue = value !== null ? value : null;
|
|
93
|
+
draftCompareStart = compareStart !== null ? compareStart : null;
|
|
94
|
+
draftCompareEnd = compareEnd !== null ? compareEnd : null;
|
|
95
|
+
|
|
96
|
+
// Navigate left calendar so it shows the committed start month (or today)
|
|
97
|
+
const anchor = rangeStart !== null ? rangeStart : value !== null ? value : now;
|
|
98
|
+
leftYear = anchor.getFullYear();
|
|
99
|
+
leftMonth = anchor.getMonth();
|
|
100
|
+
calendarKey++;
|
|
101
|
+
|
|
102
|
+
isOpen = true;
|
|
103
|
+
onopentoggle?.({ open: true });
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function closePicker(): void {
|
|
107
|
+
isOpen = false;
|
|
108
|
+
onopentoggle?.({ open: false });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function navigateDualMonths(delta: number): void {
|
|
112
|
+
const next = new SvelteDate(leftYear, leftMonth + delta, 1);
|
|
113
|
+
leftYear = next.getFullYear();
|
|
114
|
+
leftMonth = next.getMonth();
|
|
115
|
+
calendarKey++;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function handleApply(): void {
|
|
119
|
+
if (mode === 'range') {
|
|
120
|
+
if (draftStart !== null && draftEnd !== null) {
|
|
121
|
+
rangeStart = draftStart;
|
|
122
|
+
rangeEnd = draftEnd;
|
|
123
|
+
onapply?.({ rangeStart: draftStart, rangeEnd: draftEnd });
|
|
124
|
+
}
|
|
125
|
+
if (
|
|
126
|
+
typeof compareCalendar === 'function' &&
|
|
127
|
+
draftCompareStart !== null &&
|
|
128
|
+
draftCompareEnd !== null
|
|
129
|
+
) {
|
|
130
|
+
compareStart = draftCompareStart;
|
|
131
|
+
compareEnd = draftCompareEnd;
|
|
132
|
+
onapplycompare?.({ compareStart: draftCompareStart, compareEnd: draftCompareEnd });
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
if (draftValue !== null) {
|
|
136
|
+
value = draftValue;
|
|
137
|
+
onapplysingle?.({ date: draftValue });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
closePicker();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function handleCancel(): void {
|
|
144
|
+
oncancel?.();
|
|
145
|
+
closePicker();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function handlePreset(preset: DateRangePreset): void {
|
|
149
|
+
const { start, end } = preset.getValue();
|
|
150
|
+
if (mode === 'range') {
|
|
151
|
+
draftStart = start;
|
|
152
|
+
draftEnd = end;
|
|
153
|
+
// Navigate left calendar to show the preset's start month
|
|
154
|
+
leftYear = start.getFullYear();
|
|
155
|
+
leftMonth = start.getMonth();
|
|
156
|
+
calendarKey++;
|
|
157
|
+
} else {
|
|
158
|
+
draftValue = start;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function handleRangeSelect(event: { rangeStart: Date; rangeEnd: Date }): void {
|
|
163
|
+
draftStart = event.rangeStart;
|
|
164
|
+
draftEnd = event.rangeEnd;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function handleSingleSelect(event: { date: Date }): void {
|
|
168
|
+
draftValue = event.date;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Close on outside-click
|
|
172
|
+
function handleDocumentClick(event: MouseEvent): void {
|
|
173
|
+
if (!isOpen) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const target = event.target;
|
|
177
|
+
if (!(target instanceof Node)) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const clickedInsidePanel = panelRef !== null && panelRef.contains(target);
|
|
181
|
+
const clickedTrigger = triggerRef !== null && triggerRef.contains(target);
|
|
182
|
+
if (!clickedInsidePanel && !clickedTrigger) {
|
|
183
|
+
closePicker();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function handleDocumentKeyDown(event: KeyboardEvent): void {
|
|
188
|
+
if (event.key === 'Escape' && isOpen) {
|
|
189
|
+
handleCancel();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const canApply: boolean = $derived.by(() => {
|
|
194
|
+
if (mode === 'range') {
|
|
195
|
+
return draftStart !== null && draftEnd !== null;
|
|
196
|
+
}
|
|
197
|
+
return draftValue !== null;
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
function isPresetActive(preset: DateRangePreset): boolean {
|
|
201
|
+
if (mode === 'single') {
|
|
202
|
+
if (draftValue === null) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
const { start } = preset.getValue();
|
|
206
|
+
return isSameDay(draftValue, start);
|
|
207
|
+
}
|
|
208
|
+
if (draftStart === null || draftEnd === null) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
const { start, end } = preset.getValue();
|
|
212
|
+
return isSameDay(draftStart, start) && isSameDay(draftEnd, end);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function isSameDay(a: Date, b: Date): boolean {
|
|
216
|
+
return (
|
|
217
|
+
a.getFullYear() === b.getFullYear() &&
|
|
218
|
+
a.getMonth() === b.getMonth() &&
|
|
219
|
+
a.getDate() === b.getDate()
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
</script>
|
|
223
|
+
|
|
224
|
+
<svelte:document onclick={handleDocumentClick} onkeydown={handleDocumentKeyDown} />
|
|
225
|
+
|
|
226
|
+
<div class="drp-root {classes ?? ''}" data-pw={testId}>
|
|
227
|
+
<!-- Trigger wrapper — bind:this here so outside-click detection works -->
|
|
228
|
+
<div bind:this={triggerRef} class="drp-trigger-wrapper">
|
|
229
|
+
<Button
|
|
230
|
+
onclick={openPicker}
|
|
231
|
+
ariaLabel="Open date picker"
|
|
232
|
+
classes="drp-trigger {isOpen ? 'drp-trigger-open' : ''}"
|
|
233
|
+
>
|
|
234
|
+
{#if typeof triggerSnippet === 'function'}
|
|
235
|
+
{@render triggerSnippet(triggerLabel)}
|
|
236
|
+
{:else}
|
|
237
|
+
<span class="drp-trigger-label">{triggerLabel}</span>
|
|
238
|
+
<span class="drp-trigger-icon" aria-hidden="true">
|
|
239
|
+
{#if typeof triggerIcon === 'function'}
|
|
240
|
+
{@render triggerIcon()}
|
|
241
|
+
{:else}
|
|
242
|
+
<!-- eslint-disable svelte/no-at-html-tags -->
|
|
243
|
+
{@html chevronDownSvg}
|
|
244
|
+
{/if}
|
|
245
|
+
</span>
|
|
246
|
+
{/if}
|
|
247
|
+
</Button>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<!-- Dropdown panel -->
|
|
251
|
+
{#if isOpen}
|
|
252
|
+
<div
|
|
253
|
+
bind:this={panelRef}
|
|
254
|
+
class="drp-panel"
|
|
255
|
+
role="dialog"
|
|
256
|
+
aria-label="Date range picker"
|
|
257
|
+
aria-modal="true"
|
|
258
|
+
>
|
|
259
|
+
<div class="drp-panel-inner">
|
|
260
|
+
<!-- Preset sidebar -->
|
|
261
|
+
{#if presets !== null && presets.length > 0}
|
|
262
|
+
<div class="drp-sidebar" role="listbox" aria-label="Date presets">
|
|
263
|
+
{#each presets as preset (preset.label)}
|
|
264
|
+
<button
|
|
265
|
+
type="button"
|
|
266
|
+
class="drp-preset-item"
|
|
267
|
+
class:drp-preset-active={isPresetActive(preset)}
|
|
268
|
+
role="option"
|
|
269
|
+
aria-selected={isPresetActive(preset)}
|
|
270
|
+
onclick={() => handlePreset(preset)}
|
|
271
|
+
>
|
|
272
|
+
{preset.label}
|
|
273
|
+
</button>
|
|
274
|
+
{/each}
|
|
275
|
+
</div>
|
|
276
|
+
{/if}
|
|
277
|
+
|
|
278
|
+
<!-- Calendar area -->
|
|
279
|
+
<div class="drp-calendars">
|
|
280
|
+
{#if isDualMonth}
|
|
281
|
+
<!-- Dual-month layout with shared nav -->
|
|
282
|
+
<div class="drp-dual-header">
|
|
283
|
+
<button
|
|
284
|
+
type="button"
|
|
285
|
+
class="drp-nav-btn"
|
|
286
|
+
onclick={() => navigateDualMonths(-1)}
|
|
287
|
+
aria-label="Previous months"
|
|
288
|
+
>
|
|
289
|
+
<span class="drp-nav-chevron drp-nav-chevron-left" aria-hidden="true"></span>
|
|
290
|
+
</button>
|
|
291
|
+
<div class="drp-dual-month-labels">
|
|
292
|
+
<span class="drp-month-label">{leftMonthLabel}</span>
|
|
293
|
+
<span class="drp-month-label">{rightMonthLabel}</span>
|
|
294
|
+
</div>
|
|
295
|
+
<button
|
|
296
|
+
type="button"
|
|
297
|
+
class="drp-nav-btn"
|
|
298
|
+
onclick={() => navigateDualMonths(1)}
|
|
299
|
+
aria-label="Next months"
|
|
300
|
+
>
|
|
301
|
+
<span class="drp-nav-chevron drp-nav-chevron-right" aria-hidden="true"></span>
|
|
302
|
+
</button>
|
|
303
|
+
</div>
|
|
304
|
+
|
|
305
|
+
<div class="drp-months-row">
|
|
306
|
+
{#key calendarKey}
|
|
307
|
+
<!-- Left month -->
|
|
308
|
+
<Calendar
|
|
309
|
+
mode="range"
|
|
310
|
+
bind:rangeStart={draftStart}
|
|
311
|
+
bind:rangeEnd={draftEnd}
|
|
312
|
+
{weekStartsOn}
|
|
313
|
+
{locale}
|
|
314
|
+
{minDate}
|
|
315
|
+
{maxDate}
|
|
316
|
+
{disabledDates}
|
|
317
|
+
initialMonth={leftInitialMonth}
|
|
318
|
+
onrangeselect={handleRangeSelect}
|
|
319
|
+
classes="drp-calendar-embedded"
|
|
320
|
+
/>
|
|
321
|
+
<!-- Right month -->
|
|
322
|
+
<Calendar
|
|
323
|
+
mode="range"
|
|
324
|
+
bind:rangeStart={draftStart}
|
|
325
|
+
bind:rangeEnd={draftEnd}
|
|
326
|
+
{weekStartsOn}
|
|
327
|
+
{locale}
|
|
328
|
+
{minDate}
|
|
329
|
+
{maxDate}
|
|
330
|
+
{disabledDates}
|
|
331
|
+
initialMonth={rightInitialMonth}
|
|
332
|
+
onrangeselect={handleRangeSelect}
|
|
333
|
+
classes="drp-calendar-embedded"
|
|
334
|
+
/>
|
|
335
|
+
{/key}
|
|
336
|
+
</div>
|
|
337
|
+
{:else}
|
|
338
|
+
<!-- Single month -->
|
|
339
|
+
<Calendar
|
|
340
|
+
mode={mode === 'range' ? 'range' : 'single'}
|
|
341
|
+
bind:rangeStart={draftStart}
|
|
342
|
+
bind:rangeEnd={draftEnd}
|
|
343
|
+
bind:value={draftValue}
|
|
344
|
+
{weekStartsOn}
|
|
345
|
+
{locale}
|
|
346
|
+
{minDate}
|
|
347
|
+
{maxDate}
|
|
348
|
+
{disabledDates}
|
|
349
|
+
onrangeselect={handleRangeSelect}
|
|
350
|
+
onselect={handleSingleSelect}
|
|
351
|
+
classes="drp-calendar-embedded"
|
|
352
|
+
/>
|
|
353
|
+
{/if}
|
|
354
|
+
|
|
355
|
+
<!-- Time picker slot: consumer controls all time UI -->
|
|
356
|
+
{#if typeof timePicker === 'function'}
|
|
357
|
+
<div class="drp-time-row">
|
|
358
|
+
{@render timePicker()}
|
|
359
|
+
</div>
|
|
360
|
+
{/if}
|
|
361
|
+
|
|
362
|
+
<!-- Compare calendar slot: consumer controls all compare UI -->
|
|
363
|
+
{#if typeof compareCalendar === 'function'}
|
|
364
|
+
<div class="drp-compare-section">
|
|
365
|
+
{@render compareCalendar()}
|
|
366
|
+
</div>
|
|
367
|
+
{/if}
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
|
|
371
|
+
<!-- Footer -->
|
|
372
|
+
<div class="drp-footer">
|
|
373
|
+
<Button onclick={handleCancel} ariaLabel="Cancel date selection" classes="drp-btn-cancel">
|
|
374
|
+
Cancel
|
|
375
|
+
</Button>
|
|
376
|
+
<Button
|
|
377
|
+
onclick={handleApply}
|
|
378
|
+
ariaLabel="Apply date selection"
|
|
379
|
+
classes="drp-btn-apply"
|
|
380
|
+
disabled={!canApply}
|
|
381
|
+
>
|
|
382
|
+
Apply
|
|
383
|
+
</Button>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
{/if}
|
|
387
|
+
</div>
|
|
388
|
+
|
|
389
|
+
<style>
|
|
390
|
+
.drp-root {
|
|
391
|
+
position: relative;
|
|
392
|
+
display: inline-block;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* ── Trigger wrapper ── */
|
|
396
|
+
.drp-trigger-wrapper {
|
|
397
|
+
display: inline-block;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/* Bridge drp-trigger CSS vars to Button CSS vars on the wrapper */
|
|
401
|
+
.drp-trigger-wrapper {
|
|
402
|
+
--button-color: var(--drp-trigger-background, inherit);
|
|
403
|
+
--button-border: var(--drp-trigger-border, 1px solid currentColor);
|
|
404
|
+
--button-border-radius: var(--drp-trigger-border-radius, 6px);
|
|
405
|
+
--button-text-color: var(--drp-trigger-color, inherit);
|
|
406
|
+
--button-padding: var(--drp-trigger-padding, 8px 12px);
|
|
407
|
+
--button-min-width: var(--drp-trigger-min-width, 200px);
|
|
408
|
+
--button-gap: var(--drp-trigger-gap, 8px);
|
|
409
|
+
--button-hover-border-color: var(--drp-trigger-hover-border-color, currentColor);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
:global(.drp-trigger) {
|
|
413
|
+
display: inline-flex !important;
|
|
414
|
+
align-items: center;
|
|
415
|
+
gap: var(--drp-trigger-gap, 8px);
|
|
416
|
+
white-space: nowrap;
|
|
417
|
+
min-width: var(--drp-trigger-min-width, 200px);
|
|
418
|
+
font-family: inherit;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
:global(.drp-trigger-open) {
|
|
422
|
+
border-color: var(--drp-trigger-open-border-color, #000000) !important;
|
|
423
|
+
box-shadow: var(--drp-trigger-open-shadow, 0 0 0 2px rgba(0, 0, 0, 0.1));
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.drp-trigger-label {
|
|
427
|
+
flex: 1;
|
|
428
|
+
text-align: left;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.drp-trigger-icon {
|
|
432
|
+
display: inline-flex;
|
|
433
|
+
align-items: center;
|
|
434
|
+
color: var(--drp-trigger-icon-color, inherit);
|
|
435
|
+
flex-shrink: 0;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* ── Panel ── */
|
|
439
|
+
.drp-panel {
|
|
440
|
+
position: absolute;
|
|
441
|
+
top: calc(100% + var(--drp-panel-offset, 6px));
|
|
442
|
+
left: 0;
|
|
443
|
+
z-index: var(--drp-panel-z-index, 1000);
|
|
444
|
+
background: var(--drp-panel-background, inherit);
|
|
445
|
+
border: var(--drp-panel-border, 1px solid #e0e0e0);
|
|
446
|
+
border-radius: var(--drp-panel-border-radius, 10px);
|
|
447
|
+
box-shadow: var(--drp-panel-shadow, 0 8px 24px rgba(0, 0, 0, 0.12));
|
|
448
|
+
display: flex;
|
|
449
|
+
flex-direction: column;
|
|
450
|
+
min-width: var(--drp-panel-min-width, 320px);
|
|
451
|
+
max-width: var(--drp-panel-max-width, 760px);
|
|
452
|
+
overflow: hidden;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.drp-panel-inner {
|
|
456
|
+
display: flex;
|
|
457
|
+
flex-direction: row;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/* ── Sidebar ── */
|
|
461
|
+
.drp-sidebar {
|
|
462
|
+
display: flex;
|
|
463
|
+
flex-direction: column;
|
|
464
|
+
padding: var(--drp-sidebar-padding, 12px 8px);
|
|
465
|
+
border-right: var(--drp-sidebar-border, 1px solid #e8e8e8);
|
|
466
|
+
min-width: var(--drp-sidebar-min-width, 140px);
|
|
467
|
+
gap: 2px;
|
|
468
|
+
overflow-y: auto;
|
|
469
|
+
max-height: var(--drp-sidebar-max-height, 400px);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.drp-preset-item {
|
|
473
|
+
display: block;
|
|
474
|
+
width: 100%;
|
|
475
|
+
text-align: left;
|
|
476
|
+
padding: var(--drp-preset-padding, 7px 12px);
|
|
477
|
+
background: none;
|
|
478
|
+
border: none;
|
|
479
|
+
border-radius: var(--drp-preset-border-radius, 5px);
|
|
480
|
+
color: var(--drp-preset-color, inherit);
|
|
481
|
+
cursor: pointer;
|
|
482
|
+
white-space: nowrap;
|
|
483
|
+
transition: background 0.12s ease;
|
|
484
|
+
font-family: inherit;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
.drp-preset-item:hover {
|
|
488
|
+
background: var(--drp-preset-hover-background, #f5f5f5);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.drp-preset-active {
|
|
492
|
+
background: var(--drp-preset-active-background, currentColor);
|
|
493
|
+
color: var(--drp-preset-active-color, #ffffff);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.drp-preset-active:hover {
|
|
497
|
+
background: var(--drp-preset-active-hover-background, #333333);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/* ── Calendar area ── */
|
|
501
|
+
.drp-calendars {
|
|
502
|
+
display: flex;
|
|
503
|
+
flex-direction: column;
|
|
504
|
+
padding: var(--drp-calendars-padding, 16px);
|
|
505
|
+
gap: var(--drp-calendars-gap, 16px);
|
|
506
|
+
flex: 1;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/* Dual-month shared nav header */
|
|
510
|
+
.drp-dual-header {
|
|
511
|
+
display: flex;
|
|
512
|
+
align-items: center;
|
|
513
|
+
justify-content: space-between;
|
|
514
|
+
gap: 8px;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
.drp-dual-month-labels {
|
|
518
|
+
display: flex;
|
|
519
|
+
flex: 1;
|
|
520
|
+
justify-content: space-around;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.drp-month-label {
|
|
524
|
+
color: var(--drp-month-label-color, inherit);
|
|
525
|
+
text-transform: capitalize;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
.drp-nav-btn {
|
|
529
|
+
display: inline-flex;
|
|
530
|
+
align-items: center;
|
|
531
|
+
justify-content: center;
|
|
532
|
+
width: var(--drp-nav-btn-size, 32px);
|
|
533
|
+
height: var(--drp-nav-btn-size, 32px);
|
|
534
|
+
background: none;
|
|
535
|
+
border: none;
|
|
536
|
+
border-radius: var(--drp-nav-btn-border-radius, 4px);
|
|
537
|
+
cursor: pointer;
|
|
538
|
+
color: var(--drp-nav-btn-color, inherit);
|
|
539
|
+
transition: background 0.12s ease;
|
|
540
|
+
padding: 0;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
.drp-nav-btn:hover {
|
|
544
|
+
background: var(--drp-nav-btn-hover-background, #f0f0f0);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/* CSS-only chevrons for the dual-nav */
|
|
548
|
+
.drp-nav-chevron {
|
|
549
|
+
display: inline-block;
|
|
550
|
+
width: 8px;
|
|
551
|
+
height: 8px;
|
|
552
|
+
border-right: var(--drp-nav-chevron-border, 2px solid currentColor);
|
|
553
|
+
border-top: var(--drp-nav-chevron-border, 2px solid currentColor);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.drp-nav-chevron-left {
|
|
557
|
+
transform: rotate(-135deg);
|
|
558
|
+
margin-left: 2px;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
.drp-nav-chevron-right {
|
|
562
|
+
transform: rotate(45deg);
|
|
563
|
+
margin-right: 2px;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.drp-months-row {
|
|
567
|
+
display: flex;
|
|
568
|
+
gap: var(--drp-months-gap, 24px);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/* Embedded calendar: strip outer chrome so only the grid shows */
|
|
572
|
+
:global(.drp-calendar-embedded) {
|
|
573
|
+
--calendar-border: none;
|
|
574
|
+
--calendar-box-shadow: none;
|
|
575
|
+
--calendar-background: transparent;
|
|
576
|
+
--calendar-padding: 0;
|
|
577
|
+
/* Suppress Calendar's own header in dual-month context via CSS var */
|
|
578
|
+
--calendar-header-display: none;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/* ── Time picker slot ── */
|
|
582
|
+
.drp-time-row {
|
|
583
|
+
display: flex;
|
|
584
|
+
align-items: center;
|
|
585
|
+
gap: var(--drp-time-row-gap, 16px);
|
|
586
|
+
padding-top: var(--drp-time-row-padding-top, 8px);
|
|
587
|
+
border-top: var(--drp-time-divider, 1px solid #e8e8e8);
|
|
588
|
+
flex-wrap: wrap;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/* ── Compare calendar slot ── */
|
|
592
|
+
.drp-compare-section {
|
|
593
|
+
display: flex;
|
|
594
|
+
flex-direction: column;
|
|
595
|
+
gap: 8px;
|
|
596
|
+
padding-top: var(--drp-compare-padding-top, 12px);
|
|
597
|
+
border-top: var(--drp-compare-divider, 1px solid #e8e8e8);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/* ── Footer ── */
|
|
601
|
+
.drp-footer {
|
|
602
|
+
display: flex;
|
|
603
|
+
justify-content: flex-end;
|
|
604
|
+
align-items: center;
|
|
605
|
+
gap: var(--drp-footer-gap, 8px);
|
|
606
|
+
padding: var(--drp-footer-padding, 12px 16px);
|
|
607
|
+
border-top: var(--drp-footer-border, 1px solid #e8e8e8);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/* Cancel button variant via Button's CSS vars */
|
|
611
|
+
:global(.drp-btn-cancel) {
|
|
612
|
+
--button-color: transparent;
|
|
613
|
+
--button-border: 1px solid var(--drp-cancel-border-color, #d0d0d0);
|
|
614
|
+
--button-text-color: var(--drp-cancel-color, inherit);
|
|
615
|
+
--button-hover-color: var(--drp-cancel-hover-background, #f5f5f5);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/* Apply button variant */
|
|
619
|
+
:global(.drp-btn-apply) {
|
|
620
|
+
--button-color: var(--drp-apply-background, currentColor);
|
|
621
|
+
--button-border: none;
|
|
622
|
+
--button-text-color: var(--drp-apply-color, #ffffff);
|
|
623
|
+
--button-hover-color: var(--drp-apply-hover-background, #333333);
|
|
624
|
+
--button-disabled-color: var(--drp-apply-disabled-background, #cccccc);
|
|
625
|
+
--button-disabled-text-color: var(--drp-apply-disabled-color, #888888);
|
|
626
|
+
}
|
|
627
|
+
</style>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DateRangePickerProperties } from './properties';
|
|
2
|
+
declare const DateRangePicker: import("svelte").Component<DateRangePickerProperties, {}, "value" | "rangeStart" | "rangeEnd" | "compareStart" | "compareEnd">;
|
|
3
|
+
type DateRangePicker = ReturnType<typeof DateRangePicker>;
|
|
4
|
+
export default DateRangePicker;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
export type DateRangePreset = {
|
|
3
|
+
label: string;
|
|
4
|
+
getValue: () => {
|
|
5
|
+
start: Date;
|
|
6
|
+
end: Date;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export type DateRangePickerMode = 'range' | 'single';
|
|
10
|
+
export type OptionalDateRangePickerProperties = {
|
|
11
|
+
/** Currently selected start of range (bindable). */
|
|
12
|
+
rangeStart?: Date | null;
|
|
13
|
+
/** Currently selected end of range (bindable). */
|
|
14
|
+
rangeEnd?: Date | null;
|
|
15
|
+
/** Selected date when mode='single' (bindable). */
|
|
16
|
+
value?: Date | null;
|
|
17
|
+
/** Whether the picker operates in range or single-date mode. Default: 'range'. */
|
|
18
|
+
mode?: DateRangePickerMode;
|
|
19
|
+
/** Earliest selectable date. */
|
|
20
|
+
minDate?: Date | null;
|
|
21
|
+
/** Latest selectable date. */
|
|
22
|
+
maxDate?: Date | null;
|
|
23
|
+
/** Specific dates to disable, or a predicate. */
|
|
24
|
+
disabledDates?: Date[] | ((date: Date) => boolean);
|
|
25
|
+
/** Preset options shown in the sidebar. Omit to hide the sidebar. */
|
|
26
|
+
presets?: DateRangePreset[] | null;
|
|
27
|
+
/** Placeholder shown when no range is selected. */
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
/** Show two months side by side. Defaults to true for range mode, false for single. */
|
|
30
|
+
dualMonth?: boolean;
|
|
31
|
+
/** Snippet rendered in the time-picker slot. Consumer controls all time UI. */
|
|
32
|
+
timePicker?: Snippet;
|
|
33
|
+
/** Start of compare range (bindable). Used when compareCalendar snippet is provided. */
|
|
34
|
+
compareStart?: Date | null;
|
|
35
|
+
/** End of compare range (bindable). Used when compareCalendar snippet is provided. */
|
|
36
|
+
compareEnd?: Date | null;
|
|
37
|
+
/** Snippet rendered in the compare-calendar slot. Consumer controls all compare UI. */
|
|
38
|
+
compareCalendar?: Snippet;
|
|
39
|
+
/** Which day of the week starts the calendar. 0=Sunday, 1=Monday. */
|
|
40
|
+
weekStartsOn?: 0 | 1;
|
|
41
|
+
/** BCP-47 locale string for date formatting. */
|
|
42
|
+
locale?: string;
|
|
43
|
+
/** Test ID placed on the root wrapper element. */
|
|
44
|
+
testId?: string;
|
|
45
|
+
/** Extra CSS classes applied to the root wrapper. */
|
|
46
|
+
classes?: string;
|
|
47
|
+
/** Custom trigger content snippet. Receives the current label string as its argument. */
|
|
48
|
+
triggerSnippet?: Snippet<[string]>;
|
|
49
|
+
/** Custom icon snippet for the trigger button. */
|
|
50
|
+
triggerIcon?: Snippet;
|
|
51
|
+
};
|
|
52
|
+
export type DateRangePickerEventProperties = {
|
|
53
|
+
/** Fired when the user clicks Apply in range mode. */
|
|
54
|
+
onapply?: (event: {
|
|
55
|
+
rangeStart: Date;
|
|
56
|
+
rangeEnd: Date;
|
|
57
|
+
}) => void;
|
|
58
|
+
/** Fired when the user clicks Apply in single mode. */
|
|
59
|
+
onapplysingle?: (event: {
|
|
60
|
+
date: Date;
|
|
61
|
+
}) => void;
|
|
62
|
+
/** Fired when the compare range is applied. */
|
|
63
|
+
onapplycompare?: (event: {
|
|
64
|
+
compareStart: Date;
|
|
65
|
+
compareEnd: Date;
|
|
66
|
+
}) => void;
|
|
67
|
+
/** Fired when the user dismisses without applying. */
|
|
68
|
+
oncancel?: () => void;
|
|
69
|
+
/** Fired whenever the picker opens or closes. */
|
|
70
|
+
onopentoggle?: (event: {
|
|
71
|
+
open: boolean;
|
|
72
|
+
}) => void;
|
|
73
|
+
};
|
|
74
|
+
export type DateRangePickerProperties = OptionalDateRangePickerProperties & DateRangePickerEventProperties;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|