@aiaiai-pt/design-system 0.4.4 → 0.5.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/components/Calendar.svelte +971 -0
- package/components/Calendar.svelte.d.ts +50 -0
- package/components/DatePicker.svelte +473 -0
- package/components/DatePicker.svelte.d.ts +59 -0
- package/components/DateRangePicker.svelte +558 -0
- package/components/DateRangePicker.svelte.d.ts +55 -0
- package/components/DateTimePicker.svelte +275 -0
- package/components/DateTimePicker.svelte.d.ts +55 -0
- package/components/MapCluster.svelte +220 -0
- package/components/MapCluster.svelte.d.ts +39 -0
- package/components/MapDisplay.svelte +139 -0
- package/components/MapDisplay.svelte.d.ts +35 -0
- package/components/MapHeatmap.svelte +164 -0
- package/components/MapHeatmap.svelte.d.ts +50 -0
- package/components/MapPicker.svelte +243 -0
- package/components/MapPicker.svelte.d.ts +49 -0
- package/components/MapPopup.svelte +101 -0
- package/components/MapPopup.svelte.d.ts +30 -0
- package/components/StatCard.svelte +195 -0
- package/components/StatCard.svelte.d.ts +42 -0
- package/components/StatGrid.svelte +39 -0
- package/components/StatGrid.svelte.d.ts +29 -0
- package/components/index.d.ts +12 -0
- package/components/index.js +17 -0
- package/components/map-utils.d.ts +100 -0
- package/components/map-utils.js +338 -0
- package/package.json +8 -1
- package/tokens/components.css +215 -0
|
@@ -0,0 +1,558 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@component DateRangePicker
|
|
3
|
+
|
|
4
|
+
Integrated calendar for selecting a date range. Single calendar with
|
|
5
|
+
two-phase selection: click start → hover preview → click end.
|
|
6
|
+
Range highlighted between start and end dates.
|
|
7
|
+
Consumes --datepicker-* tokens from components.css.
|
|
8
|
+
|
|
9
|
+
@example Basic
|
|
10
|
+
<DateRangePicker label="PERIOD" bind:start bind:end />
|
|
11
|
+
|
|
12
|
+
@example With constraints
|
|
13
|
+
<DateRangePicker label="FISCAL YEAR" min={fiscalStart} max={fiscalEnd} bind:start bind:end />
|
|
14
|
+
-->
|
|
15
|
+
<script module>
|
|
16
|
+
let _daterangeUid = 0;
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<script>
|
|
20
|
+
import { format, startOfMonth, endOfMonth, startOfWeek, endOfWeek, addDays, addMonths, subMonths, isSameDay, isSameMonth, isBefore, isAfter, isWithinInterval } from 'date-fns';
|
|
21
|
+
import { enUS } from 'date-fns/locale';
|
|
22
|
+
import Popover from './Popover.svelte';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {'sm' | 'md' | 'lg'} Size
|
|
26
|
+
* @typedef {'start' | 'end'} SelectionPhase
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
let {
|
|
30
|
+
/** @type {Date | null} */
|
|
31
|
+
start = $bindable(null),
|
|
32
|
+
/** @type {Date | null} */
|
|
33
|
+
end = $bindable(null),
|
|
34
|
+
/** @type {string | undefined} */
|
|
35
|
+
label = undefined,
|
|
36
|
+
/** @type {string} */
|
|
37
|
+
startPlaceholder = 'Start date',
|
|
38
|
+
/** @type {string} */
|
|
39
|
+
endPlaceholder = 'End date',
|
|
40
|
+
/** @type {string | undefined} */
|
|
41
|
+
help = undefined,
|
|
42
|
+
/** @type {string | undefined} */
|
|
43
|
+
error = undefined,
|
|
44
|
+
/** @type {Size} */
|
|
45
|
+
size = 'md',
|
|
46
|
+
/** @type {boolean} */
|
|
47
|
+
disabled = false,
|
|
48
|
+
/** @type {boolean} */
|
|
49
|
+
readonly = false,
|
|
50
|
+
/** @type {Date | undefined} */
|
|
51
|
+
min = undefined,
|
|
52
|
+
/** @type {Date | undefined} */
|
|
53
|
+
max = undefined,
|
|
54
|
+
/** @type {string} */
|
|
55
|
+
displayFormat = 'dd/MM/yyyy',
|
|
56
|
+
/** @type {import('date-fns').Locale} */
|
|
57
|
+
locale = enUS,
|
|
58
|
+
/** @type {((range: { start: Date | null, end: Date | null }) => void) | undefined} */
|
|
59
|
+
onchange = undefined,
|
|
60
|
+
/** @type {string | undefined} */
|
|
61
|
+
id = undefined,
|
|
62
|
+
/** @type {string} */
|
|
63
|
+
class: className = '',
|
|
64
|
+
...rest
|
|
65
|
+
} = $props();
|
|
66
|
+
|
|
67
|
+
const fallbackId = `daterange-${_daterangeUid++}`;
|
|
68
|
+
const pickerId = $derived(id ?? fallbackId);
|
|
69
|
+
const hintId = $derived(`${pickerId}-hint`);
|
|
70
|
+
const hasHint = $derived(!!error || !!help);
|
|
71
|
+
|
|
72
|
+
let open = $state(false);
|
|
73
|
+
/** @type {HTMLElement | undefined} */
|
|
74
|
+
let triggerEl = $state();
|
|
75
|
+
let viewDate = $state(start ?? new Date());
|
|
76
|
+
/** @type {SelectionPhase} */
|
|
77
|
+
let phase = $state('start');
|
|
78
|
+
/** @type {Date | null} */
|
|
79
|
+
let hoveredDate = $state(null);
|
|
80
|
+
|
|
81
|
+
const startDisplay = $derived(start ? format(start, displayFormat, { locale }) : '');
|
|
82
|
+
const endDisplay = $derived(end ? format(end, displayFormat, { locale }) : '');
|
|
83
|
+
|
|
84
|
+
const weekdays = $derived(getWeekdays(locale));
|
|
85
|
+
const calendarDays = $derived(getCalendarDays(viewDate, locale));
|
|
86
|
+
|
|
87
|
+
/** @param {import('date-fns').Locale} loc */
|
|
88
|
+
function getWeekdays(loc) {
|
|
89
|
+
const s = startOfWeek(new Date(), { locale: loc });
|
|
90
|
+
return Array.from({ length: 7 }, (_, i) =>
|
|
91
|
+
format(addDays(s, i), 'EEEEEE', { locale: loc })
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** @param {Date} month @param {import('date-fns').Locale} loc */
|
|
96
|
+
function getCalendarDays(month, loc) {
|
|
97
|
+
const monthStart = startOfMonth(month);
|
|
98
|
+
const monthEnd = endOfMonth(month);
|
|
99
|
+
const calStart = startOfWeek(monthStart, { locale: loc });
|
|
100
|
+
const calEnd = endOfWeek(monthEnd, { locale: loc });
|
|
101
|
+
|
|
102
|
+
/** @type {Date[]} */
|
|
103
|
+
const days = [];
|
|
104
|
+
let cursor = calStart;
|
|
105
|
+
while (isBefore(cursor, calEnd) || isSameDay(cursor, calEnd)) {
|
|
106
|
+
days.push(cursor);
|
|
107
|
+
cursor = addDays(cursor, 1);
|
|
108
|
+
}
|
|
109
|
+
return days;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** @param {Date} date */
|
|
113
|
+
function isDisabledDate(date) {
|
|
114
|
+
if (min && isBefore(date, min)) return true;
|
|
115
|
+
if (max && isAfter(date, max)) return true;
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** @param {Date} date */
|
|
120
|
+
function isInRange(date) {
|
|
121
|
+
if (!start) return false;
|
|
122
|
+
const rangeEnd = phase === 'end' && hoveredDate ? hoveredDate : end;
|
|
123
|
+
if (!rangeEnd) return false;
|
|
124
|
+
|
|
125
|
+
const [lo, hi] = isBefore(start, rangeEnd) ? [start, rangeEnd] : [rangeEnd, start];
|
|
126
|
+
return isWithinInterval(date, { start: lo, end: hi }) && !isSameDay(date, lo) && !isSameDay(date, hi);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** @param {Date} date */
|
|
130
|
+
function isRangeStart(date) {
|
|
131
|
+
return start !== null && isSameDay(date, start);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** @param {Date} date */
|
|
135
|
+
function isRangeEnd(date) {
|
|
136
|
+
if (phase === 'end' && hoveredDate) {
|
|
137
|
+
return isSameDay(date, hoveredDate);
|
|
138
|
+
}
|
|
139
|
+
return end !== null && isSameDay(date, end);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** @param {Date} date */
|
|
143
|
+
function selectDate(date) {
|
|
144
|
+
if (isDisabledDate(date)) return;
|
|
145
|
+
|
|
146
|
+
if (phase === 'start') {
|
|
147
|
+
start = date;
|
|
148
|
+
end = null;
|
|
149
|
+
phase = 'end';
|
|
150
|
+
} else {
|
|
151
|
+
// Phase: end
|
|
152
|
+
if (isBefore(date, /** @type {Date} */ (start))) {
|
|
153
|
+
// Clicked before start — swap: this becomes start, reset end
|
|
154
|
+
start = date;
|
|
155
|
+
end = null;
|
|
156
|
+
// Stay in 'end' phase
|
|
157
|
+
} else if (isSameDay(date, /** @type {Date} */ (start))) {
|
|
158
|
+
// Same as start — ignore
|
|
159
|
+
return;
|
|
160
|
+
} else {
|
|
161
|
+
end = date;
|
|
162
|
+
phase = 'start';
|
|
163
|
+
hoveredDate = null;
|
|
164
|
+
open = false;
|
|
165
|
+
onchange?.({ start, end });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function handleTriggerClick() {
|
|
171
|
+
if (disabled || readonly) return;
|
|
172
|
+
open = !open;
|
|
173
|
+
if (open) {
|
|
174
|
+
phase = start && !end ? 'end' : 'start';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** @param {KeyboardEvent} e */
|
|
179
|
+
function handleTriggerKeydown(e) {
|
|
180
|
+
if (disabled || readonly) return;
|
|
181
|
+
if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
|
|
182
|
+
e.preventDefault();
|
|
183
|
+
open = true;
|
|
184
|
+
phase = start && !end ? 'end' : 'start';
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function prevMonth() { viewDate = subMonths(viewDate, 1); }
|
|
189
|
+
function nextMonth() { viewDate = addMonths(viewDate, 1); }
|
|
190
|
+
|
|
191
|
+
$effect(() => {
|
|
192
|
+
if (start && !open) viewDate = start;
|
|
193
|
+
});
|
|
194
|
+
</script>
|
|
195
|
+
|
|
196
|
+
<div class="daterange {className}" {...rest}>
|
|
197
|
+
{#if label}
|
|
198
|
+
<span class="daterange-label">{label}</span>
|
|
199
|
+
{/if}
|
|
200
|
+
|
|
201
|
+
<button
|
|
202
|
+
bind:this={triggerEl}
|
|
203
|
+
id={pickerId}
|
|
204
|
+
type="button"
|
|
205
|
+
class="daterange-trigger daterange-trigger-{size}"
|
|
206
|
+
class:daterange-trigger-error={!!error}
|
|
207
|
+
class:daterange-trigger-readonly={readonly}
|
|
208
|
+
aria-haspopup="dialog"
|
|
209
|
+
aria-expanded={open}
|
|
210
|
+
aria-describedby={hasHint ? hintId : undefined}
|
|
211
|
+
{disabled}
|
|
212
|
+
onclick={handleTriggerClick}
|
|
213
|
+
onkeydown={handleTriggerKeydown}
|
|
214
|
+
>
|
|
215
|
+
<span class="daterange-value" class:daterange-placeholder={!start}>
|
|
216
|
+
{startDisplay || startPlaceholder}
|
|
217
|
+
</span>
|
|
218
|
+
<span class="daterange-arrow" aria-hidden="true">
|
|
219
|
+
<svg viewBox="0 0 256 256" fill="none">
|
|
220
|
+
<line x1="40" y1="128" x2="216" y2="128" stroke="currentColor" stroke-width="16" stroke-linecap="round" />
|
|
221
|
+
<polyline points="168,80 216,128 168,176" fill="none" stroke="currentColor" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" />
|
|
222
|
+
</svg>
|
|
223
|
+
</span>
|
|
224
|
+
<span class="daterange-value" class:daterange-placeholder={!end}>
|
|
225
|
+
{endDisplay || endPlaceholder}
|
|
226
|
+
</span>
|
|
227
|
+
<svg class="daterange-icon" viewBox="0 0 256 256" fill="none" aria-hidden="true">
|
|
228
|
+
<rect x="40" y="40" width="176" height="176" rx="8" stroke="currentColor" stroke-width="16" fill="none" />
|
|
229
|
+
<line x1="176" y1="24" x2="176" y2="56" stroke="currentColor" stroke-width="16" stroke-linecap="round" />
|
|
230
|
+
<line x1="80" y1="24" x2="80" y2="56" stroke="currentColor" stroke-width="16" stroke-linecap="round" />
|
|
231
|
+
<line x1="40" y1="88" x2="216" y2="88" stroke="currentColor" stroke-width="16" stroke-linecap="round" />
|
|
232
|
+
</svg>
|
|
233
|
+
</button>
|
|
234
|
+
|
|
235
|
+
<Popover bind:open anchor={triggerEl} placement="bottom-start" onclose={() => { open = false; hoveredDate = null; }}>
|
|
236
|
+
<div class="daterange-calendar" role="application" aria-label="Date range calendar">
|
|
237
|
+
<div class="daterange-phase-hint">
|
|
238
|
+
<span class="type-caption" style="color: var(--color-text-muted);">
|
|
239
|
+
{phase === 'start' ? 'Select start date' : 'Select end date'}
|
|
240
|
+
</span>
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
<div class="daterange-nav">
|
|
244
|
+
<button type="button" class="daterange-nav-btn" onclick={prevMonth} aria-label="Previous month">
|
|
245
|
+
<svg viewBox="0 0 256 256" aria-hidden="true">
|
|
246
|
+
<polyline points="160,208 80,128 160,48" fill="none" stroke="currentColor" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" />
|
|
247
|
+
</svg>
|
|
248
|
+
</button>
|
|
249
|
+
<span class="daterange-nav-title">
|
|
250
|
+
{format(viewDate, 'LLLL yyyy', { locale })}
|
|
251
|
+
</span>
|
|
252
|
+
<button type="button" class="daterange-nav-btn" onclick={nextMonth} aria-label="Next month">
|
|
253
|
+
<svg viewBox="0 0 256 256" aria-hidden="true">
|
|
254
|
+
<polyline points="96,48 176,128 96,208" fill="none" stroke="currentColor" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" />
|
|
255
|
+
</svg>
|
|
256
|
+
</button>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<div class="daterange-grid" role="grid" aria-label="Calendar dates">
|
|
260
|
+
<div class="daterange-weekdays" role="row">
|
|
261
|
+
{#each weekdays as day}
|
|
262
|
+
<span class="daterange-weekday" role="columnheader">{day}</span>
|
|
263
|
+
{/each}
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
<div class="daterange-days" role="rowgroup">
|
|
267
|
+
{#each calendarDays as date}
|
|
268
|
+
{@const isStart = isRangeStart(date)}
|
|
269
|
+
{@const isEnd = isRangeEnd(date)}
|
|
270
|
+
{@const inRange = isInRange(date)}
|
|
271
|
+
{@const isToday = isSameDay(date, new Date())}
|
|
272
|
+
{@const isOutside = !isSameMonth(date, viewDate)}
|
|
273
|
+
{@const isDisabled = isDisabledDate(date)}
|
|
274
|
+
<button
|
|
275
|
+
type="button"
|
|
276
|
+
class="daterange-day"
|
|
277
|
+
class:daterange-day-start={isStart}
|
|
278
|
+
class:daterange-day-end={isEnd}
|
|
279
|
+
class:daterange-day-in-range={inRange}
|
|
280
|
+
class:daterange-day-today={isToday && !isStart && !isEnd}
|
|
281
|
+
class:daterange-day-outside={isOutside}
|
|
282
|
+
role="gridcell"
|
|
283
|
+
aria-selected={isStart || isEnd}
|
|
284
|
+
aria-disabled={isDisabled}
|
|
285
|
+
tabindex={isStart || isEnd || (!start && isToday) ? 0 : -1}
|
|
286
|
+
disabled={isDisabled}
|
|
287
|
+
onclick={() => selectDate(date)}
|
|
288
|
+
onmouseenter={() => { if (phase === 'end') hoveredDate = date; }}
|
|
289
|
+
onmouseleave={() => { if (phase === 'end') hoveredDate = null; }}
|
|
290
|
+
>
|
|
291
|
+
{date.getDate()}
|
|
292
|
+
</button>
|
|
293
|
+
{/each}
|
|
294
|
+
</div>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
</Popover>
|
|
298
|
+
|
|
299
|
+
{#if error}
|
|
300
|
+
<span id={hintId} class="daterange-error-text" role="alert">{error}</span>
|
|
301
|
+
{:else if help}
|
|
302
|
+
<span id={hintId} class="daterange-help">{help}</span>
|
|
303
|
+
{/if}
|
|
304
|
+
</div>
|
|
305
|
+
|
|
306
|
+
<style>
|
|
307
|
+
.daterange {
|
|
308
|
+
display: flex;
|
|
309
|
+
flex-direction: column;
|
|
310
|
+
gap: var(--input-label-gap);
|
|
311
|
+
width: 100%;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
.daterange-label {
|
|
315
|
+
font-family: var(--input-label-font);
|
|
316
|
+
font-size: var(--input-label-size);
|
|
317
|
+
letter-spacing: var(--input-label-tracking);
|
|
318
|
+
color: var(--input-label-color);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* ─── Trigger ─── */
|
|
322
|
+
.daterange-trigger {
|
|
323
|
+
display: flex;
|
|
324
|
+
align-items: center;
|
|
325
|
+
gap: var(--space-sm);
|
|
326
|
+
font-family: var(--input-font);
|
|
327
|
+
font-size: var(--input-font-size);
|
|
328
|
+
border: var(--input-border);
|
|
329
|
+
border-radius: var(--input-radius);
|
|
330
|
+
background: var(--input-bg);
|
|
331
|
+
color: var(--input-text);
|
|
332
|
+
cursor: pointer;
|
|
333
|
+
width: 100%;
|
|
334
|
+
text-align: left;
|
|
335
|
+
transition: border var(--input-transition);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.daterange-trigger-sm {
|
|
339
|
+
height: var(--input-sm-height);
|
|
340
|
+
padding: 0 var(--input-sm-padding-x);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.daterange-trigger-md {
|
|
344
|
+
height: var(--input-md-height);
|
|
345
|
+
padding: 0 var(--input-md-padding-x);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
.daterange-trigger-lg {
|
|
349
|
+
height: var(--input-lg-height);
|
|
350
|
+
padding: 0 var(--input-lg-padding-x);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.daterange-trigger:focus {
|
|
354
|
+
outline: none;
|
|
355
|
+
border: var(--input-border-focus);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.daterange-trigger:disabled {
|
|
359
|
+
opacity: 0.5;
|
|
360
|
+
cursor: not-allowed;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.daterange-trigger-readonly {
|
|
364
|
+
background: var(--color-surface-secondary);
|
|
365
|
+
cursor: default;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.daterange-trigger-error {
|
|
369
|
+
border-color: var(--input-error-border-color);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
.daterange-value {
|
|
373
|
+
flex: 1;
|
|
374
|
+
min-width: 0;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
.daterange-placeholder {
|
|
378
|
+
color: var(--datepicker-placeholder-color);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.daterange-arrow {
|
|
382
|
+
flex-shrink: 0;
|
|
383
|
+
display: flex;
|
|
384
|
+
align-items: center;
|
|
385
|
+
color: var(--color-text-muted);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
.daterange-arrow svg {
|
|
389
|
+
width: var(--icon-size-xs);
|
|
390
|
+
height: var(--icon-size-xs);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.daterange-icon {
|
|
394
|
+
flex-shrink: 0;
|
|
395
|
+
width: var(--datepicker-icon-size);
|
|
396
|
+
height: var(--datepicker-icon-size);
|
|
397
|
+
color: var(--datepicker-icon-color);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/* ─── Calendar ─── */
|
|
401
|
+
.daterange-calendar {
|
|
402
|
+
padding: var(--datepicker-calendar-padding);
|
|
403
|
+
min-width: 280px;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.daterange-phase-hint {
|
|
407
|
+
text-align: center;
|
|
408
|
+
margin-bottom: var(--space-xs);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.daterange-nav {
|
|
412
|
+
display: flex;
|
|
413
|
+
align-items: center;
|
|
414
|
+
justify-content: space-between;
|
|
415
|
+
margin-bottom: var(--space-sm);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.daterange-nav-title {
|
|
419
|
+
font-family: var(--datepicker-nav-font);
|
|
420
|
+
font-size: var(--datepicker-nav-size);
|
|
421
|
+
letter-spacing: var(--datepicker-nav-tracking);
|
|
422
|
+
color: var(--datepicker-nav-color);
|
|
423
|
+
text-transform: uppercase;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.daterange-nav-btn {
|
|
427
|
+
display: flex;
|
|
428
|
+
align-items: center;
|
|
429
|
+
justify-content: center;
|
|
430
|
+
width: var(--datepicker-nav-btn-size);
|
|
431
|
+
height: var(--datepicker-nav-btn-size);
|
|
432
|
+
border: none;
|
|
433
|
+
border-radius: var(--datepicker-nav-btn-radius);
|
|
434
|
+
background: transparent;
|
|
435
|
+
color: var(--color-text-secondary);
|
|
436
|
+
cursor: pointer;
|
|
437
|
+
transition: background var(--duration-instant) var(--easing-default);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.daterange-nav-btn:hover {
|
|
441
|
+
background: var(--datepicker-nav-btn-hover-bg);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.daterange-nav-btn svg {
|
|
445
|
+
width: var(--icon-size-xs);
|
|
446
|
+
height: var(--icon-size-xs);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/* ─── Grid ─── */
|
|
450
|
+
.daterange-grid {
|
|
451
|
+
display: flex;
|
|
452
|
+
flex-direction: column;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.daterange-weekdays {
|
|
456
|
+
display: grid;
|
|
457
|
+
grid-template-columns: repeat(7, 1fr);
|
|
458
|
+
margin-bottom: var(--space-2xs);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.daterange-weekday {
|
|
462
|
+
display: flex;
|
|
463
|
+
align-items: center;
|
|
464
|
+
justify-content: center;
|
|
465
|
+
height: var(--datepicker-day-size);
|
|
466
|
+
font-family: var(--datepicker-weekday-font);
|
|
467
|
+
font-size: var(--datepicker-weekday-size);
|
|
468
|
+
letter-spacing: var(--datepicker-weekday-tracking);
|
|
469
|
+
color: var(--datepicker-weekday-color);
|
|
470
|
+
text-transform: uppercase;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.daterange-days {
|
|
474
|
+
display: grid;
|
|
475
|
+
grid-template-columns: repeat(7, 1fr);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.daterange-day {
|
|
479
|
+
display: flex;
|
|
480
|
+
align-items: center;
|
|
481
|
+
justify-content: center;
|
|
482
|
+
width: 100%;
|
|
483
|
+
height: var(--datepicker-day-size);
|
|
484
|
+
font-family: var(--datepicker-day-font);
|
|
485
|
+
font-size: var(--datepicker-day-font-size);
|
|
486
|
+
border: none;
|
|
487
|
+
border-radius: 0;
|
|
488
|
+
background: transparent;
|
|
489
|
+
color: var(--color-text);
|
|
490
|
+
cursor: pointer;
|
|
491
|
+
transition: background var(--duration-instant) var(--easing-default);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.daterange-day:hover:not(:disabled) {
|
|
495
|
+
background: var(--datepicker-day-hover-bg);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/* Range endpoints */
|
|
499
|
+
.daterange-day-start,
|
|
500
|
+
.daterange-day-end {
|
|
501
|
+
background: var(--datepicker-range-endpoint-bg);
|
|
502
|
+
color: var(--datepicker-range-endpoint-text);
|
|
503
|
+
border-radius: var(--datepicker-day-radius);
|
|
504
|
+
font-weight: 600;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.daterange-day-start:hover:not(:disabled),
|
|
508
|
+
.daterange-day-end:hover:not(:disabled) {
|
|
509
|
+
background: var(--datepicker-range-endpoint-bg);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/* In-range fill */
|
|
513
|
+
.daterange-day-in-range {
|
|
514
|
+
background: var(--datepicker-range-bg);
|
|
515
|
+
border-radius: 0;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
.daterange-day-in-range:hover:not(:disabled) {
|
|
519
|
+
background: var(--datepicker-range-hover-bg);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/* Today indicator */
|
|
523
|
+
.daterange-day-today {
|
|
524
|
+
border: var(--datepicker-day-today-border);
|
|
525
|
+
border-radius: var(--datepicker-day-radius);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/* Outside month */
|
|
529
|
+
.daterange-day-outside {
|
|
530
|
+
opacity: var(--datepicker-day-outside-opacity);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
.daterange-day:disabled {
|
|
534
|
+
opacity: var(--datepicker-day-disabled-opacity);
|
|
535
|
+
cursor: not-allowed;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/* ─── Hint / Error ─── */
|
|
539
|
+
.daterange-help {
|
|
540
|
+
font-family: var(--input-help-font);
|
|
541
|
+
font-size: var(--input-help-size);
|
|
542
|
+
color: var(--input-help-color);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.daterange-error-text {
|
|
546
|
+
font-family: var(--input-help-font);
|
|
547
|
+
font-size: var(--input-help-size);
|
|
548
|
+
color: var(--input-error-text);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
@media (prefers-reduced-motion: reduce) {
|
|
552
|
+
.daterange-trigger,
|
|
553
|
+
.daterange-nav-btn,
|
|
554
|
+
.daterange-day {
|
|
555
|
+
transition: none;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
</style>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export default DateRangePicker;
|
|
2
|
+
type DateRangePicker = {
|
|
3
|
+
$on?(type: string, callback: (e: any) => void): () => void;
|
|
4
|
+
$set?(props: Partial<$$ComponentProps>): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* DateRangePicker
|
|
8
|
+
*
|
|
9
|
+
* Start/end date pair for filters and ranges. Two coordinated DatePickers
|
|
10
|
+
* where start constrains end and vice versa.
|
|
11
|
+
* Consumes --datepicker-* and --input-* tokens from components.css.
|
|
12
|
+
*
|
|
13
|
+
* @example Basic
|
|
14
|
+
* <DateRangePicker label="PERIOD" bind:start bind:end />
|
|
15
|
+
*
|
|
16
|
+
* @example With constraints
|
|
17
|
+
* <DateRangePicker label="FISCAL YEAR" min={fiscalStart} max={fiscalEnd} bind:start bind:end />
|
|
18
|
+
*/
|
|
19
|
+
declare const DateRangePicker: import("svelte").Component<{
|
|
20
|
+
start?: any;
|
|
21
|
+
end?: any;
|
|
22
|
+
label?: any;
|
|
23
|
+
startPlaceholder?: string;
|
|
24
|
+
endPlaceholder?: string;
|
|
25
|
+
help?: any;
|
|
26
|
+
error?: any;
|
|
27
|
+
size?: string;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
readonly?: boolean;
|
|
30
|
+
min?: any;
|
|
31
|
+
max?: any;
|
|
32
|
+
displayFormat?: string;
|
|
33
|
+
locale?: typeof enUS;
|
|
34
|
+
onchange?: any;
|
|
35
|
+
class?: string;
|
|
36
|
+
} & Record<string, any>, {}, "start" | "end">;
|
|
37
|
+
type $$ComponentProps = {
|
|
38
|
+
start?: any;
|
|
39
|
+
end?: any;
|
|
40
|
+
label?: any;
|
|
41
|
+
startPlaceholder?: string;
|
|
42
|
+
endPlaceholder?: string;
|
|
43
|
+
help?: any;
|
|
44
|
+
error?: any;
|
|
45
|
+
size?: string;
|
|
46
|
+
disabled?: boolean;
|
|
47
|
+
readonly?: boolean;
|
|
48
|
+
min?: any;
|
|
49
|
+
max?: any;
|
|
50
|
+
displayFormat?: string;
|
|
51
|
+
locale?: typeof enUS;
|
|
52
|
+
onchange?: any;
|
|
53
|
+
class?: string;
|
|
54
|
+
} & Record<string, any>;
|
|
55
|
+
import { enUS } from 'date-fns/locale';
|