@r2digisolutions/components 0.8.5 → 0.8.7
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.
|
@@ -62,7 +62,22 @@
|
|
|
62
62
|
const today = new Date();
|
|
63
63
|
const todayIso = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
function monthFromIso(iso: string | undefined) {
|
|
66
|
+
if (!iso) return null;
|
|
67
|
+
const [y, m] = iso.split('-').map(Number);
|
|
68
|
+
if (!Number.isFinite(y) || !Number.isFinite(m)) return null;
|
|
69
|
+
return new Date(y, m - 1, 1);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function selectionIso() {
|
|
73
|
+
if (mode === 'range') return start;
|
|
74
|
+
if (mode === 'multiple') return values[0];
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
let view = $state(
|
|
79
|
+
monthFromIso(selectionIso()) ?? new Date(today.getFullYear(), today.getMonth(), 1)
|
|
80
|
+
);
|
|
66
81
|
let panel = $state<Panel>('days');
|
|
67
82
|
/** Which month column owns the month/year picker when months=2. */
|
|
68
83
|
let pickerOffset = $state(0);
|
|
@@ -91,7 +91,8 @@
|
|
|
91
91
|
|
|
92
92
|
let triggerEl = $state<HTMLElement | null>(null);
|
|
93
93
|
let panelEl = $state<HTMLDivElement | null>(null);
|
|
94
|
-
|
|
94
|
+
/** Hide the native popover until top/left are applied (UA default is 0,0). */
|
|
95
|
+
let placed = $state(false);
|
|
95
96
|
let activeField = $state<'start' | 'end' | 'value'>('value');
|
|
96
97
|
let panelId = $state('');
|
|
97
98
|
|
|
@@ -128,9 +129,15 @@
|
|
|
128
129
|
return { side, align: align ?? (months === 2 ? 'center' : 'start') };
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
function
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
function estimatePanelSize() {
|
|
133
|
+
const monthW = months === 2 ? 288 : 320;
|
|
134
|
+
const panelW = months === 2 ? monthW * 2 + 24 : monthW + 16;
|
|
135
|
+
return { panelW, panelH: 360 };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function positionPanel(opts: { measure?: boolean; show?: boolean } = {}) {
|
|
139
|
+
if (!triggerEl) return;
|
|
140
|
+
if (opts.show) placed = true;
|
|
134
141
|
|
|
135
142
|
const trigger = triggerEl.getBoundingClientRect();
|
|
136
143
|
if (trigger.width < 2 && trigger.height < 2) return;
|
|
@@ -142,9 +149,16 @@
|
|
|
142
149
|
const viewTop = vv?.offsetTop ?? 0;
|
|
143
150
|
const gap = 8;
|
|
144
151
|
const pad = 8;
|
|
145
|
-
|
|
146
|
-
const
|
|
147
|
-
const
|
|
152
|
+
const estimated = estimatePanelSize();
|
|
153
|
+
const canMeasure = opts.measure !== false && !!panelEl?.matches(':popover-open');
|
|
154
|
+
const panelW = Math.min(
|
|
155
|
+
canMeasure && panelEl?.offsetWidth ? panelEl.offsetWidth : estimated.panelW,
|
|
156
|
+
viewW - pad * 2
|
|
157
|
+
);
|
|
158
|
+
const panelH = Math.min(
|
|
159
|
+
canMeasure && panelEl?.offsetHeight ? panelEl.offsetHeight : estimated.panelH,
|
|
160
|
+
420
|
|
161
|
+
);
|
|
148
162
|
|
|
149
163
|
const fixed = parsePlacement(placement);
|
|
150
164
|
let side: 'top' | 'bottom' = fixed?.side ?? 'bottom';
|
|
@@ -167,15 +181,19 @@
|
|
|
167
181
|
left = Math.min(Math.max(viewLeft + pad, left), viewLeft + viewW - panelW - pad);
|
|
168
182
|
top = Math.min(Math.max(viewTop + pad, top), viewTop + viewH - panelH - pad);
|
|
169
183
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
'
|
|
176
|
-
'
|
|
177
|
-
'
|
|
178
|
-
|
|
184
|
+
// Imperative styles in `beforetoggle`: a Svelte `style={}` binding
|
|
185
|
+
// flushes too late and the UA popover paints at 0,0 first.
|
|
186
|
+
if (panelEl) {
|
|
187
|
+
const s = panelEl.style;
|
|
188
|
+
s.setProperty('margin', '0');
|
|
189
|
+
s.setProperty('inset', 'auto');
|
|
190
|
+
s.setProperty('top', `${Math.round(top)}px`);
|
|
191
|
+
s.setProperty('left', `${Math.round(left)}px`);
|
|
192
|
+
s.setProperty('right', 'auto');
|
|
193
|
+
s.setProperty('bottom', 'auto');
|
|
194
|
+
s.setProperty('width', 'max-content');
|
|
195
|
+
s.setProperty('visibility', placed ? 'visible' : 'hidden');
|
|
196
|
+
}
|
|
179
197
|
}
|
|
180
198
|
|
|
181
199
|
function schedulePosition() {
|
|
@@ -183,7 +201,7 @@
|
|
|
183
201
|
positionPanel();
|
|
184
202
|
requestAnimationFrame(() => {
|
|
185
203
|
positionPanel();
|
|
186
|
-
requestAnimationFrame(positionPanel);
|
|
204
|
+
requestAnimationFrame(() => positionPanel({ show: true }));
|
|
187
205
|
});
|
|
188
206
|
});
|
|
189
207
|
}
|
|
@@ -205,8 +223,16 @@
|
|
|
205
223
|
}
|
|
206
224
|
|
|
207
225
|
function handleBeforeToggle(event: ToggleEvent) {
|
|
208
|
-
if (event.newState === 'open'
|
|
209
|
-
|
|
226
|
+
if (event.newState === 'open') {
|
|
227
|
+
if (disabled) {
|
|
228
|
+
event.preventDefault();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
placed = false;
|
|
232
|
+
// Set top/left before the first paint — UA popover defaults to 0,0.
|
|
233
|
+
positionPanel({ measure: false });
|
|
234
|
+
} else {
|
|
235
|
+
placed = false;
|
|
210
236
|
}
|
|
211
237
|
}
|
|
212
238
|
|
|
@@ -214,6 +240,7 @@
|
|
|
214
240
|
const next = event.newState === 'open';
|
|
215
241
|
open = next;
|
|
216
242
|
if (next) schedulePosition();
|
|
243
|
+
else placed = false;
|
|
217
244
|
}
|
|
218
245
|
|
|
219
246
|
$effect(() => {
|
|
@@ -427,56 +454,58 @@
|
|
|
427
454
|
aria-label="Choose date"
|
|
428
455
|
onbeforetoggle={handleBeforeToggle}
|
|
429
456
|
ontoggle={handleToggle}
|
|
430
|
-
|
|
457
|
+
data-placed={placed ? true : undefined}
|
|
431
458
|
class={[
|
|
432
459
|
'datepicker-popover m-0 w-max rounded-2xl border border-border bg-surface-elevated p-2 shadow-xl outline-none',
|
|
433
460
|
months === 2 ? 'max-w-[min(42rem,calc(100vw-1rem))]' : 'max-w-[20rem]'
|
|
434
461
|
]}
|
|
435
462
|
>
|
|
436
|
-
{#
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
463
|
+
{#key `${open}:${start}:${value}`}
|
|
464
|
+
{#if mode === 'single'}
|
|
465
|
+
<Calendar
|
|
466
|
+
mode="single"
|
|
467
|
+
{months}
|
|
468
|
+
bind:value
|
|
469
|
+
{min}
|
|
470
|
+
{max}
|
|
471
|
+
{disabledDates}
|
|
472
|
+
{enabledDates}
|
|
473
|
+
{dots}
|
|
474
|
+
framed={false}
|
|
475
|
+
locale={dateLocale}
|
|
476
|
+
onchange={handleChange}
|
|
477
|
+
/>
|
|
478
|
+
{:else if mode === 'multiple'}
|
|
479
|
+
<Calendar
|
|
480
|
+
mode="multiple"
|
|
481
|
+
{months}
|
|
482
|
+
bind:values
|
|
483
|
+
{min}
|
|
484
|
+
{max}
|
|
485
|
+
{disabledDates}
|
|
486
|
+
{enabledDates}
|
|
487
|
+
{dots}
|
|
488
|
+
framed={false}
|
|
489
|
+
locale={dateLocale}
|
|
490
|
+
onchange={handleChange}
|
|
491
|
+
/>
|
|
492
|
+
{:else}
|
|
493
|
+
<Calendar
|
|
494
|
+
mode="range"
|
|
495
|
+
{months}
|
|
496
|
+
bind:start
|
|
497
|
+
bind:end
|
|
498
|
+
{min}
|
|
499
|
+
{max}
|
|
500
|
+
{disabledDates}
|
|
501
|
+
{enabledDates}
|
|
502
|
+
{dots}
|
|
503
|
+
framed={false}
|
|
504
|
+
locale={dateLocale}
|
|
505
|
+
onchange={handleChange}
|
|
506
|
+
/>
|
|
507
|
+
{/if}
|
|
508
|
+
{/key}
|
|
480
509
|
</div>
|
|
481
510
|
</div>
|
|
482
511
|
|
|
@@ -494,6 +523,11 @@
|
|
|
494
523
|
display: block;
|
|
495
524
|
}
|
|
496
525
|
|
|
526
|
+
.datepicker-popover:popover-open:not([data-placed]) {
|
|
527
|
+
visibility: hidden;
|
|
528
|
+
pointer-events: none;
|
|
529
|
+
}
|
|
530
|
+
|
|
497
531
|
.datepicker-popover:not(:popover-open) {
|
|
498
532
|
display: none;
|
|
499
533
|
}
|