@r2digisolutions/components 0.14.14 → 0.14.16
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/components/molecules/DateTimePicker/DateTimePicker.stories.d.ts +2 -2
- package/dist/components/molecules/DateTimePicker/DateTimePicker.stories.js +4 -4
- package/dist/components/molecules/DateTimePicker/DateTimePicker.svelte +300 -101
- package/dist/components/molecules/DateTimePicker/DateTimePicker.svelte.d.ts +6 -0
- package/dist/components/molecules/DateTimePicker/DateTimePickerStory.svelte +1 -1
- package/dist/components/molecules/NumberInput/NumberInput.svelte +9 -0
- package/package.json +1 -1
|
@@ -25,11 +25,11 @@ declare const meta: {
|
|
|
25
25
|
format: "24h";
|
|
26
26
|
minuteStep: number;
|
|
27
27
|
label: string;
|
|
28
|
-
closeOnSelect:
|
|
28
|
+
closeOnSelect: false;
|
|
29
29
|
};
|
|
30
30
|
};
|
|
31
31
|
export default meta;
|
|
32
32
|
type Story = StoryObj<typeof meta>;
|
|
33
33
|
export declare const Default: Story;
|
|
34
34
|
export declare const TwelveHour: Story;
|
|
35
|
-
export declare const
|
|
35
|
+
export declare const InstantClose: Story;
|
|
@@ -12,13 +12,13 @@ const meta = {
|
|
|
12
12
|
format: '24h',
|
|
13
13
|
minuteStep: 5,
|
|
14
14
|
label: 'Date & time',
|
|
15
|
-
closeOnSelect:
|
|
15
|
+
closeOnSelect: false
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
export default meta;
|
|
19
19
|
export const Default = {};
|
|
20
20
|
export const TwelveHour = { args: { format: '12h' } };
|
|
21
|
-
export const
|
|
22
|
-
name: '
|
|
23
|
-
args: { closeOnSelect:
|
|
21
|
+
export const InstantClose = {
|
|
22
|
+
name: 'Instant commit (legacy)',
|
|
23
|
+
args: { closeOnSelect: true }
|
|
24
24
|
};
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
+
import { on } from 'svelte/events';
|
|
3
|
+
import Button from '../../atoms/Button/Button.svelte';
|
|
2
4
|
import Calendar from '../Calendar/Calendar.svelte';
|
|
3
5
|
import type { CalendarDot } from '../Calendar/Calendar.svelte';
|
|
4
6
|
import type { TimeFormat } from '../TimePicker/TimePicker.svelte';
|
|
7
|
+
import { i18n } from '../../../utils/i18n.svelte.js';
|
|
8
|
+
import { createId } from '../../../utils/id.js';
|
|
5
9
|
|
|
6
10
|
interface DateTimePickerProps {
|
|
7
11
|
/** Date part `YYYY-MM-DD`. */
|
|
@@ -20,7 +24,13 @@
|
|
|
20
24
|
dots?: CalendarDot[];
|
|
21
25
|
format?: TimeFormat;
|
|
22
26
|
minuteStep?: number;
|
|
27
|
+
/**
|
|
28
|
+
* When false (default), edits stay in the panel until Save; Cancel discards.
|
|
29
|
+
* When true, each pick commits immediately and may close the panel.
|
|
30
|
+
*/
|
|
23
31
|
closeOnSelect?: boolean;
|
|
32
|
+
cancelLabel?: string;
|
|
33
|
+
saveLabel?: string;
|
|
24
34
|
class?: string;
|
|
25
35
|
onchange?: (detail: { date: string; time: string; value: string }) => void;
|
|
26
36
|
}
|
|
@@ -39,14 +49,31 @@
|
|
|
39
49
|
dots = [],
|
|
40
50
|
format = '24h',
|
|
41
51
|
minuteStep = 5,
|
|
42
|
-
closeOnSelect =
|
|
52
|
+
closeOnSelect = false,
|
|
53
|
+
cancelLabel,
|
|
54
|
+
saveLabel,
|
|
43
55
|
class: className = '',
|
|
44
56
|
onchange
|
|
45
57
|
}: DateTimePickerProps = $props();
|
|
46
58
|
|
|
47
|
-
let
|
|
59
|
+
let triggerEl = $state<HTMLElement | null>(null);
|
|
60
|
+
let panelEl = $state<HTMLDivElement | null>(null);
|
|
48
61
|
let hourListEl = $state<HTMLDivElement | null>(null);
|
|
49
62
|
let minuteListEl = $state<HTMLDivElement | null>(null);
|
|
63
|
+
let placed = $state(false);
|
|
64
|
+
let panelId = $state('');
|
|
65
|
+
let draftDate = $state('');
|
|
66
|
+
let draftTime = $state('');
|
|
67
|
+
|
|
68
|
+
const resolvedCancelLabel = $derived(cancelLabel ?? i18n.t('cancel'));
|
|
69
|
+
const resolvedSaveLabel = $derived(saveLabel ?? i18n.t('save'));
|
|
70
|
+
const confirmMode = $derived(!closeOnSelect);
|
|
71
|
+
const panelDate = $derived(confirmMode ? draftDate : date);
|
|
72
|
+
const panelTime = $derived(confirmMode ? draftTime : time);
|
|
73
|
+
|
|
74
|
+
$effect(() => {
|
|
75
|
+
panelId ||= createId('datetimepicker');
|
|
76
|
+
});
|
|
50
77
|
|
|
51
78
|
const hours24 = Array.from({ length: 24 }, (_, i) => i);
|
|
52
79
|
const minutes = $derived(
|
|
@@ -82,7 +109,6 @@
|
|
|
82
109
|
onchange?.({ date, time, value });
|
|
83
110
|
}
|
|
84
111
|
|
|
85
|
-
// Keep parts in sync when `value` is set externally
|
|
86
112
|
$effect(() => {
|
|
87
113
|
if (!value) return;
|
|
88
114
|
const [d, t] = value.includes('T') ? value.split('T') : [value, time];
|
|
@@ -93,7 +119,7 @@
|
|
|
93
119
|
}
|
|
94
120
|
});
|
|
95
121
|
|
|
96
|
-
const parsedTime = $derived(parseTime(
|
|
122
|
+
const parsedTime = $derived(parseTime(panelTime));
|
|
97
123
|
const selectedH = $derived(parsedTime?.h ?? null);
|
|
98
124
|
const selectedM = $derived(parsedTime?.m ?? null);
|
|
99
125
|
|
|
@@ -122,40 +148,171 @@
|
|
|
122
148
|
return formatTimeDisplay(time);
|
|
123
149
|
});
|
|
124
150
|
|
|
151
|
+
function estimatePanelSize() {
|
|
152
|
+
return { panelW: 672, panelH: confirmMode ? 432 : 380 };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function beginDraft() {
|
|
156
|
+
draftDate = date;
|
|
157
|
+
draftTime = time || '09:00';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function setPanelDate(next: string) {
|
|
161
|
+
if (confirmMode) draftDate = next;
|
|
162
|
+
else date = next;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function setPanelTime(next: string) {
|
|
166
|
+
if (confirmMode) draftTime = next;
|
|
167
|
+
else time = next;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function commitDraft() {
|
|
171
|
+
date = draftDate;
|
|
172
|
+
time = draftTime || '09:00';
|
|
173
|
+
emit();
|
|
174
|
+
setOpen(false);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function cancelDraft() {
|
|
178
|
+
setOpen(false);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function positionPanel(opts: { measure?: boolean; show?: boolean } = {}) {
|
|
182
|
+
if (!triggerEl) return;
|
|
183
|
+
if (opts.show) placed = true;
|
|
184
|
+
|
|
185
|
+
const trigger = triggerEl.getBoundingClientRect();
|
|
186
|
+
if (trigger.width < 2 && trigger.height < 2) return;
|
|
187
|
+
|
|
188
|
+
const vv = window.visualViewport;
|
|
189
|
+
const viewW = vv?.width ?? window.innerWidth;
|
|
190
|
+
const viewH = vv?.height ?? window.innerHeight;
|
|
191
|
+
const viewLeft = vv?.offsetLeft ?? 0;
|
|
192
|
+
const viewTop = vv?.offsetTop ?? 0;
|
|
193
|
+
const gap = 8;
|
|
194
|
+
const pad = 8;
|
|
195
|
+
const estimated = estimatePanelSize();
|
|
196
|
+
const canMeasure = opts.measure !== false && !!panelEl?.matches(':popover-open');
|
|
197
|
+
const panelW = Math.min(
|
|
198
|
+
canMeasure && panelEl?.offsetWidth ? panelEl.offsetWidth : estimated.panelW,
|
|
199
|
+
viewW - pad * 2
|
|
200
|
+
);
|
|
201
|
+
const panelH = Math.min(
|
|
202
|
+
canMeasure && panelEl?.offsetHeight ? panelEl.offsetHeight : estimated.panelH,
|
|
203
|
+
viewH - pad * 2
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const spaceBelow = viewTop + viewH - trigger.bottom - gap - pad;
|
|
207
|
+
const spaceAbove = trigger.top - viewTop - gap - pad;
|
|
208
|
+
const side: 'top' | 'bottom' = spaceBelow < panelH && spaceAbove > spaceBelow ? 'top' : 'bottom';
|
|
209
|
+
|
|
210
|
+
let top = side === 'bottom' ? trigger.bottom + gap : trigger.top - panelH - gap;
|
|
211
|
+
let left = trigger.left;
|
|
212
|
+
|
|
213
|
+
left = Math.min(Math.max(viewLeft + pad, left), viewLeft + viewW - panelW - pad);
|
|
214
|
+
top = Math.min(Math.max(viewTop + pad, top), viewTop + viewH - panelH - pad);
|
|
215
|
+
|
|
216
|
+
if (panelEl) {
|
|
217
|
+
const s = panelEl.style;
|
|
218
|
+
s.setProperty('margin', '0');
|
|
219
|
+
s.setProperty('inset', 'auto');
|
|
220
|
+
s.setProperty('top', `${Math.round(top)}px`);
|
|
221
|
+
s.setProperty('left', `${Math.round(left)}px`);
|
|
222
|
+
s.setProperty('right', 'auto');
|
|
223
|
+
s.setProperty('bottom', 'auto');
|
|
224
|
+
s.setProperty('width', 'max-content');
|
|
225
|
+
s.setProperty('visibility', placed ? 'visible' : 'hidden');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function schedulePosition() {
|
|
230
|
+
queueMicrotask(() => {
|
|
231
|
+
positionPanel();
|
|
232
|
+
requestAnimationFrame(() => {
|
|
233
|
+
positionPanel();
|
|
234
|
+
requestAnimationFrame(() => positionPanel({ show: true }));
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function syncNative() {
|
|
240
|
+
if (!panelEl) return;
|
|
241
|
+
const isOpen = panelEl.matches(':popover-open');
|
|
242
|
+
try {
|
|
243
|
+
if (open && !isOpen) panelEl.showPopover();
|
|
244
|
+
else if (!open && isOpen) panelEl.hidePopover();
|
|
245
|
+
} catch {
|
|
246
|
+
/* ignore */
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
125
250
|
function setOpen(next: boolean) {
|
|
126
|
-
if (disabled) return;
|
|
251
|
+
if (disabled && next) return;
|
|
127
252
|
open = next;
|
|
128
253
|
}
|
|
129
254
|
|
|
130
|
-
function
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
255
|
+
function handleBeforeToggle(event: ToggleEvent) {
|
|
256
|
+
if (event.newState === 'open') {
|
|
257
|
+
if (disabled) {
|
|
258
|
+
event.preventDefault();
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (confirmMode) beginDraft();
|
|
262
|
+
placed = false;
|
|
263
|
+
positionPanel({ measure: false });
|
|
264
|
+
} else {
|
|
265
|
+
placed = false;
|
|
266
|
+
}
|
|
135
267
|
}
|
|
136
268
|
|
|
137
|
-
function
|
|
138
|
-
|
|
269
|
+
function handleToggle(event: ToggleEvent) {
|
|
270
|
+
open = event.newState === 'open';
|
|
271
|
+
if (open) schedulePosition();
|
|
272
|
+
else placed = false;
|
|
139
273
|
}
|
|
140
274
|
|
|
275
|
+
$effect(() => {
|
|
276
|
+
open;
|
|
277
|
+
queueMicrotask(() => {
|
|
278
|
+
syncNative();
|
|
279
|
+
if (open) positionPanel();
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
$effect(() => {
|
|
284
|
+
if (!open) return;
|
|
285
|
+
const offResize = on(window, 'resize', () => positionPanel());
|
|
286
|
+
const offScroll = on(window, 'scroll', () => positionPanel(), { capture: true });
|
|
287
|
+
return () => {
|
|
288
|
+
offResize();
|
|
289
|
+
offScroll();
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
|
|
141
293
|
function handleDateChange(detail: { value: string }) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (
|
|
294
|
+
setPanelDate(detail.value);
|
|
295
|
+
const currentTime = confirmMode ? draftTime : time;
|
|
296
|
+
if (!currentTime) setPanelTime('09:00');
|
|
297
|
+
if (!confirmMode) {
|
|
298
|
+
emit();
|
|
299
|
+
if (closeOnSelect && date && time) setOpen(false);
|
|
300
|
+
}
|
|
146
301
|
}
|
|
147
302
|
|
|
148
303
|
function pickHour(h: number) {
|
|
149
304
|
const m = selectedM ?? 0;
|
|
150
|
-
|
|
151
|
-
emit();
|
|
305
|
+
setPanelTime(toTime(h, m));
|
|
306
|
+
if (!confirmMode) emit();
|
|
152
307
|
}
|
|
153
308
|
|
|
154
309
|
function pickMinute(m: number) {
|
|
155
310
|
const h = selectedH ?? 9;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
311
|
+
setPanelTime(toTime(h, m));
|
|
312
|
+
if (!confirmMode) {
|
|
313
|
+
emit();
|
|
314
|
+
if (closeOnSelect && date && time) setOpen(false);
|
|
315
|
+
}
|
|
159
316
|
}
|
|
160
317
|
|
|
161
318
|
function clear(e: MouseEvent) {
|
|
@@ -184,14 +341,13 @@
|
|
|
184
341
|
});
|
|
185
342
|
</script>
|
|
186
343
|
|
|
187
|
-
<
|
|
188
|
-
|
|
189
|
-
<div class={['relative w-full min-w-[20rem] max-w-[22rem]', className]} bind:this={rootEl}>
|
|
344
|
+
<div class={['w-full min-w-[20rem] max-w-[22rem]', className]}>
|
|
190
345
|
{#if label}
|
|
191
346
|
<span class="mb-1.5 block text-sm font-medium text-primary">{label}</span>
|
|
192
347
|
{/if}
|
|
193
348
|
|
|
194
349
|
<div
|
|
350
|
+
bind:this={triggerEl}
|
|
195
351
|
class={[
|
|
196
352
|
'flex h-10 w-full items-center overflow-hidden rounded-xl border border-border bg-surface-elevated transition-colors',
|
|
197
353
|
open && 'border-brand-500 ring-2 ring-brand-500/20',
|
|
@@ -201,8 +357,11 @@
|
|
|
201
357
|
<button
|
|
202
358
|
type="button"
|
|
203
359
|
{disabled}
|
|
204
|
-
|
|
360
|
+
popovertarget={panelId}
|
|
361
|
+
popovertargetaction="toggle"
|
|
205
362
|
aria-expanded={open}
|
|
363
|
+
aria-haspopup="dialog"
|
|
364
|
+
aria-controls={panelId}
|
|
206
365
|
class={[
|
|
207
366
|
'flex h-full min-w-0 flex-1 items-center gap-2 px-3.5 text-left text-sm',
|
|
208
367
|
'hover:bg-surface-overlay focus-visible:outline-none',
|
|
@@ -241,85 +400,125 @@
|
|
|
241
400
|
{/if}
|
|
242
401
|
</div>
|
|
243
402
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
403
|
+
<div
|
|
404
|
+
bind:this={panelEl}
|
|
405
|
+
id={panelId}
|
|
406
|
+
popover="auto"
|
|
407
|
+
role="dialog"
|
|
408
|
+
aria-label="Choose date and time"
|
|
409
|
+
onbeforetoggle={handleBeforeToggle}
|
|
410
|
+
ontoggle={handleToggle}
|
|
411
|
+
data-placed={placed ? true : undefined}
|
|
412
|
+
class="datetime-picker-popover m-0 flex w-max max-w-[min(100vw-1rem,42rem)] flex-col overflow-hidden rounded-2xl border border-border bg-surface-elevated shadow-xl outline-none"
|
|
413
|
+
>
|
|
414
|
+
{#key open}
|
|
415
|
+
<div class="flex flex-col sm:flex-row sm:items-stretch">
|
|
416
|
+
<div class="w-[22rem] shrink-0 p-1.5">
|
|
417
|
+
<Calendar
|
|
418
|
+
mode="single"
|
|
419
|
+
value={panelDate}
|
|
420
|
+
{min}
|
|
421
|
+
{max}
|
|
422
|
+
{disabledDates}
|
|
423
|
+
{dots}
|
|
424
|
+
framed={false}
|
|
425
|
+
class="w-full"
|
|
426
|
+
onchange={handleDateChange}
|
|
427
|
+
/>
|
|
428
|
+
</div>
|
|
429
|
+
<div
|
|
430
|
+
class="flex w-full shrink-0 flex-col border-t border-border sm:w-40 sm:border-l sm:border-t-0"
|
|
269
431
|
>
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
<div class="grid min-h-0 flex-1 grid-cols-2 divide-x divide-border">
|
|
273
|
-
<div
|
|
274
|
-
bind:this={hourListEl}
|
|
275
|
-
class="h-[18.5rem] overflow-y-auto p-1.5"
|
|
276
|
-
role="listbox"
|
|
277
|
-
aria-label="Hours"
|
|
432
|
+
<span
|
|
433
|
+
class="shrink-0 border-b border-border px-3 py-2.5 text-center text-[11px] font-medium uppercase tracking-wide text-muted"
|
|
278
434
|
>
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
435
|
+
Time
|
|
436
|
+
</span>
|
|
437
|
+
<div class="grid min-h-0 flex-1 grid-cols-2 divide-x divide-border">
|
|
438
|
+
<div
|
|
439
|
+
bind:this={hourListEl}
|
|
440
|
+
class="h-[18.5rem] overflow-y-auto p-1.5"
|
|
441
|
+
role="listbox"
|
|
442
|
+
aria-label="Hours"
|
|
443
|
+
>
|
|
444
|
+
{#each hours24 as h (h)}
|
|
445
|
+
<button
|
|
446
|
+
type="button"
|
|
447
|
+
role="option"
|
|
448
|
+
aria-pressed={selectedH === h}
|
|
449
|
+
aria-selected={selectedH === h}
|
|
450
|
+
onclick={() => pickHour(h)}
|
|
451
|
+
class={[
|
|
452
|
+
'w-full rounded-lg px-2 py-1.5 text-sm transition-colors',
|
|
453
|
+
selectedH === h
|
|
454
|
+
? 'bg-brand-500 font-semibold text-white'
|
|
455
|
+
: 'text-primary hover:bg-surface-overlay'
|
|
456
|
+
]}
|
|
457
|
+
>
|
|
458
|
+
{hourLabel(h)}
|
|
459
|
+
</button>
|
|
460
|
+
{/each}
|
|
461
|
+
</div>
|
|
462
|
+
<div
|
|
463
|
+
bind:this={minuteListEl}
|
|
464
|
+
class="h-[18.5rem] overflow-y-auto p-1.5"
|
|
465
|
+
role="listbox"
|
|
466
|
+
aria-label="Minutes"
|
|
467
|
+
>
|
|
468
|
+
{#each minutes as m (m)}
|
|
469
|
+
<button
|
|
470
|
+
type="button"
|
|
471
|
+
role="option"
|
|
472
|
+
aria-pressed={selectedM === m}
|
|
473
|
+
aria-selected={selectedM === m}
|
|
474
|
+
onclick={() => pickMinute(m)}
|
|
475
|
+
class={[
|
|
476
|
+
'w-full rounded-lg px-2 py-1.5 text-sm transition-colors',
|
|
477
|
+
selectedM === m
|
|
478
|
+
? 'bg-brand-500 font-semibold text-white'
|
|
479
|
+
: 'text-primary hover:bg-surface-overlay'
|
|
480
|
+
]}
|
|
481
|
+
>
|
|
482
|
+
{pad(m)}
|
|
483
|
+
</button>
|
|
484
|
+
{/each}
|
|
485
|
+
</div>
|
|
320
486
|
</div>
|
|
321
487
|
</div>
|
|
322
488
|
</div>
|
|
323
|
-
|
|
324
|
-
|
|
489
|
+
{#if confirmMode}
|
|
490
|
+
<div class="flex items-center justify-end gap-2 border-t border-border px-3 py-2.5">
|
|
491
|
+
<Button type="button" variant="ghost" size="sm" onclick={cancelDraft}>
|
|
492
|
+
{resolvedCancelLabel}
|
|
493
|
+
</Button>
|
|
494
|
+
<Button type="button" size="sm" disabled={!draftDate} onclick={commitDraft}>
|
|
495
|
+
{resolvedSaveLabel}
|
|
496
|
+
</Button>
|
|
497
|
+
</div>
|
|
498
|
+
{/if}
|
|
499
|
+
{/key}
|
|
500
|
+
</div>
|
|
325
501
|
</div>
|
|
502
|
+
|
|
503
|
+
<style>
|
|
504
|
+
.datetime-picker-popover {
|
|
505
|
+
position: fixed;
|
|
506
|
+
inset: unset;
|
|
507
|
+
margin: 0;
|
|
508
|
+
width: max-content;
|
|
509
|
+
height: max-content;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.datetime-picker-popover:popover-open {
|
|
513
|
+
display: flex;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
.datetime-picker-popover:popover-open:not([data-placed]) {
|
|
517
|
+
visibility: hidden;
|
|
518
|
+
pointer-events: none;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.datetime-picker-popover:not(:popover-open) {
|
|
522
|
+
display: none;
|
|
523
|
+
}
|
|
524
|
+
</style>
|
|
@@ -17,7 +17,13 @@ interface DateTimePickerProps {
|
|
|
17
17
|
dots?: CalendarDot[];
|
|
18
18
|
format?: TimeFormat;
|
|
19
19
|
minuteStep?: number;
|
|
20
|
+
/**
|
|
21
|
+
* When false (default), edits stay in the panel until Save; Cancel discards.
|
|
22
|
+
* When true, each pick commits immediately and may close the panel.
|
|
23
|
+
*/
|
|
20
24
|
closeOnSelect?: boolean;
|
|
25
|
+
cancelLabel?: string;
|
|
26
|
+
saveLabel?: string;
|
|
21
27
|
class?: string;
|
|
22
28
|
onchange?: (detail: {
|
|
23
29
|
date: string;
|
|
@@ -105,6 +105,9 @@
|
|
|
105
105
|
{disabled}
|
|
106
106
|
{size}
|
|
107
107
|
{status}
|
|
108
|
+
{min}
|
|
109
|
+
{max}
|
|
110
|
+
{step}
|
|
108
111
|
type="number"
|
|
109
112
|
bind:value={text}
|
|
110
113
|
onblur={() => commit(text)}
|
|
@@ -151,6 +154,9 @@
|
|
|
151
154
|
{disabled}
|
|
152
155
|
{size}
|
|
153
156
|
{status}
|
|
157
|
+
{min}
|
|
158
|
+
{max}
|
|
159
|
+
{step}
|
|
154
160
|
type="number"
|
|
155
161
|
bind:value={text}
|
|
156
162
|
onblur={() => commit(text)}
|
|
@@ -193,6 +199,9 @@
|
|
|
193
199
|
{disabled}
|
|
194
200
|
{size}
|
|
195
201
|
{status}
|
|
202
|
+
{min}
|
|
203
|
+
{max}
|
|
204
|
+
{step}
|
|
196
205
|
type="number"
|
|
197
206
|
bind:value={text}
|
|
198
207
|
onblur={() => commit(text)}
|