@warkypublic/svelix 0.1.46 → 0.2.1
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/README.md +93 -33
- package/dist/components/BetterMenu/BetterMenu.svelte +21 -37
- package/dist/components/BetterMenu/BetterMenuAsyncButton.svelte +21 -31
- package/dist/components/BetterMenu/BetterMenuPreview.svelte +27 -29
- package/dist/components/BetterMenu/MenuRenderer.svelte +40 -51
- package/dist/components/Boxer/Boxer.svelte +492 -602
- package/dist/components/Boxer/BoxerTarget.svelte +6 -46
- package/dist/components/Boxer/BoxerTarget.svelte.d.ts +1 -0
- package/dist/components/Boxer/types.d.ts +0 -1
- package/dist/components/Button.svelte +19 -43
- package/dist/components/ButtonPreview.svelte +2 -11
- package/dist/components/CardGrid/CardGrid.svelte +489 -614
- package/dist/components/CardGrid/CardGrid.svelte.d.ts +4 -0
- package/dist/components/CardGrid/CardGridFilterPanel.svelte +111 -90
- package/dist/components/CardGrid/DefaultCard.svelte +86 -97
- package/dist/components/CardGrid/DefaultCard.svelte.d.ts +1 -0
- package/dist/components/CardGrid/ImageCardStory.svelte +87 -36
- package/dist/components/CardGrid/cardGrid.d.ts +24 -0
- package/dist/components/CardGrid/cardGrid.js +20 -0
- package/dist/components/ContentEditor/CollaboraInsertTextDemo.svelte +5 -10
- package/dist/components/ContentEditor/ContentEditor.svelte +24 -50
- package/dist/components/ContentEditor/subcomponents/AudioPlayer.svelte +17 -17
- package/dist/components/ContentEditor/subcomponents/CollaboraEditor.svelte +244 -309
- package/dist/components/ContentEditor/subcomponents/EmailViewer.svelte +2 -3
- package/dist/components/ContentEditor/subcomponents/ImageViewer.svelte +14 -15
- package/dist/components/ContentEditor/subcomponents/MarkdownViewer.svelte +74 -90
- package/dist/components/ContentEditor/subcomponents/MonacoEditor.svelte +75 -103
- package/dist/components/ContentEditor/subcomponents/Office365Editor.svelte +234 -297
- package/dist/components/ContentEditor/subcomponents/PdfViewer.svelte +14 -20
- package/dist/components/ContentEditor/subcomponents/TextEditor.svelte +122 -150
- package/dist/components/ContentEditor/subcomponents/UnknownFile.svelte +105 -126
- package/dist/components/ContentEditor/subcomponents/VideoPlayer.svelte +17 -17
- package/dist/components/ContentEditor/subcomponents/ZipViewer.svelte +2 -3
- package/dist/components/ErrorBoundary/ErrorBoundary.svelte +41 -58
- package/dist/components/ErrorBoundary/ErrorBoundaryPreview.svelte +8 -16
- package/dist/components/Former/Former.svelte +306 -424
- package/dist/components/Former/FormerButtonArea.svelte +17 -68
- package/dist/components/Former/FormerDrawer.svelte +16 -80
- package/dist/components/Former/FormerDrawerPreview.svelte +99 -89
- package/dist/components/Former/FormerModal.svelte +16 -74
- package/dist/components/Former/FormerModalPreview.svelte +99 -89
- package/dist/components/Former/FormerPreview.svelte +76 -78
- package/dist/components/Former/FormerRestApiPreview.svelte +55 -48
- package/dist/components/Former/formerOverlayHelpers.svelte.d.ts +11 -0
- package/dist/components/Former/formerOverlayHelpers.svelte.js +38 -0
- package/dist/components/Former/index.d.ts +1 -0
- package/dist/components/Former/index.js +1 -0
- package/dist/components/FormerControllers/ButtonCtrl.svelte +13 -49
- package/dist/components/FormerControllers/CtrlFieldShell.svelte +15 -0
- package/dist/components/FormerControllers/CtrlFieldShell.svelte.d.ts +12 -0
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrl.svelte +285 -457
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlCalendar.svelte +95 -136
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlPickerPanel.svelte +3 -62
- package/dist/components/FormerControllers/DateTimeCtrl/DateTimeCtrlTimeFields.svelte +143 -217
- package/dist/components/FormerControllers/IconButtonCtrl.svelte +13 -47
- package/dist/components/FormerControllers/InlineWrapper.svelte +12 -41
- package/dist/components/FormerControllers/InlineWrapperPreview.svelte +4 -26
- package/dist/components/FormerControllers/NativeSelectCtrl.svelte +7 -39
- package/dist/components/FormerControllers/NumberInputCtrl.svelte +21 -43
- package/dist/components/FormerControllers/PasswordInputCtrl.svelte +8 -33
- package/dist/components/FormerControllers/SwitchCtrl.svelte +4 -20
- package/dist/components/FormerControllers/TextAreaCtrl.svelte +7 -34
- package/dist/components/FormerControllers/TextInputCtrl.svelte +7 -34
- package/dist/components/FormerControllers/ctrlLoading.svelte.d.ts +8 -0
- package/dist/components/FormerControllers/ctrlLoading.svelte.js +43 -0
- package/dist/components/FormerControllers/index.d.ts +2 -0
- package/dist/components/FormerControllers/index.js +2 -0
- package/dist/components/FormerControllers/utils.d.ts +1 -0
- package/dist/components/FormerControllers/utils.js +3 -0
- package/dist/components/GlobalStateStore/GlobalStateStoreProvider.svelte +73 -120
- package/dist/components/Gridler/components/Gridler.svelte +344 -442
- package/dist/components/Gridler/components/GridlerCanvas.svelte +720 -1115
- package/dist/components/Gridler/components/GridlerEditor.svelte +2 -15
- package/dist/components/Gridler/components/GridlerFull.svelte +521 -841
- package/dist/components/Gridler/components/GridlerFull.svelte.d.ts +1 -1
- package/dist/components/Gridler/components/GridlerFullWithFormerPreview.svelte +194 -147
- package/dist/components/Gridler/components/GridlerSearch.svelte +7 -16
- package/dist/components/Gridler/components/GridlerSearchToggle.svelte +7 -19
- package/dist/components/Gridler/types.d.ts +1 -1
- package/dist/components/Gridler/utils/serverPaging.d.ts +1 -0
- package/dist/components/Gridler/utils/serverPaging.js +7 -0
- package/dist/components/Icons/IconAdd.svelte +1 -3
- package/dist/components/Icons/IconAlertCircle.svelte +1 -3
- package/dist/components/Icons/IconAlertTriangle.svelte +1 -3
- package/dist/components/Icons/IconCamera.svelte +1 -3
- package/dist/components/Icons/IconClose.svelte +1 -3
- package/dist/components/Icons/IconEdit.svelte +1 -3
- package/dist/components/Icons/IconFilter.svelte +1 -3
- package/dist/components/Icons/IconSearch.svelte +1 -3
- package/dist/components/Icons/IconSort.svelte +1 -3
- package/dist/components/Icons/IconTrash.svelte +1 -3
- package/dist/components/Portal/Portal.svelte +14 -38
- package/dist/components/Screenshot/Screenshot.svelte +7 -13
- package/dist/components/Svark/Svark.svelte +680 -852
- package/dist/components/Svark/SvarkContextMenu.svelte +44 -63
- package/dist/components/Svark/SvarkInfiniteLoader.svelte +2 -6
- package/dist/components/Svark/SvarkPager.svelte +2 -28
- package/dist/components/Svark/SvarkSelectionDemo.svelte +4 -8
- package/dist/components/Svark/SvarkTopBar.svelte +9 -74
- package/dist/components/SvarkGrid/SvarkGrid.svelte +877 -1123
- package/dist/components/SvarkGrid/components/SvarkColumnFilterForm.svelte +45 -58
- package/dist/components/SvarkGrid/components/SvarkContextMenu.svelte +44 -63
- package/dist/components/SvarkGrid/components/SvarkHeaderFilterCell.svelte +41 -75
- package/dist/components/SvarkGrid/components/SvarkInfiniteLoader.svelte +2 -6
- package/dist/components/SvarkGrid/components/SvarkPager.svelte +2 -28
- package/dist/components/SvarkGrid/components/SvarkSearchPopover.svelte +8 -16
- package/dist/components/SvarkGrid/components/SvarkTopBar.svelte +6 -62
- package/dist/components/SvarkGrid/internal/SvarkGridView.svelte +345 -507
- package/dist/components/VTree/VTree.svelte +454 -431
- package/dist/components/VTree/VTree.utils.d.ts +23 -0
- package/dist/components/VTree/VTree.utils.js +73 -0
- package/dist/components/VTree/VTreeContextMenu.svelte +21 -33
- package/dist/components/VTree/VTreeControlledSelectionDemo.svelte +20 -0
- package/dist/components/VTree/VTreeControlledSelectionDemo.svelte.d.ts +18 -0
- package/dist/components/VTree/VTreeCustomizationDemo.svelte +23 -0
- package/dist/components/VTree/VTreeCustomizationDemo.svelte.d.ts +18 -0
- package/dist/components/VTree/VTreeEventsDemo.svelte +48 -58
- package/dist/components/VTree/VTreeInteractionDemo.svelte +29 -0
- package/dist/components/VTree/VTreeInteractionDemo.svelte.d.ts +18 -0
- package/dist/components/VTree/VTreeMultiSelectDemo.svelte +27 -0
- package/dist/components/VTree/VTreeMultiSelectDemo.svelte.d.ts +18 -0
- package/dist/components/VTree/VTreeRow.svelte +77 -115
- package/dist/components/VTree/VTreeRow.svelte.d.ts +34 -18
- package/dist/components/VTree/VTreeSearch.svelte +6 -13
- package/dist/components/VTree/VTreeVirtualViewport.svelte +54 -143
- package/dist/components/VTree/VTreeVirtualViewport.svelte.d.ts +35 -18
- package/dist/components/VTree/types.d.ts +39 -1
- package/llm/COMPONENT_GUIDE.md +25 -0
- package/package.json +51 -50
|
@@ -1,462 +1,294 @@
|
|
|
1
|
-
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
applyParts(current, closeAfterApply);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function handleTimeChange(value: { hour: number; minute: number; second: number }): void {
|
|
287
|
-
const current = getCurrentParts();
|
|
288
|
-
current.hour = value.hour;
|
|
289
|
-
current.minute = value.minute;
|
|
290
|
-
current.second = value.second;
|
|
291
|
-
|
|
292
|
-
const closeAfterApply = mode === 'time';
|
|
293
|
-
applyParts(current, closeAfterApply);
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
function handleNow(): void {
|
|
297
|
-
const now = normalizePartsForMode(getNowDateTimeParts(), mode);
|
|
298
|
-
if (!now) {
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
applyParts(now, true);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
function shiftMonth(delta: number): void {
|
|
306
|
-
const shifted = shiftYearMonth(calendarYear, calendarMonth, delta);
|
|
307
|
-
calendarYear = shifted.year;
|
|
308
|
-
calendarMonth = shifted.month;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function handleTextKeydown(event: KeyboardEvent): void {
|
|
312
|
-
if (event.key === 'Enter') {
|
|
313
|
-
commitFromTextInput();
|
|
314
|
-
(event.currentTarget as HTMLInputElement).blur();
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (event.key === 'Escape' && closeOnEscape) {
|
|
319
|
-
pickerOpen = false;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function handleInputBlur(): void {
|
|
324
|
-
commitFromTextInput();
|
|
325
|
-
onblur?.();
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function updatePortalPosition(): void {
|
|
329
|
-
if (!anchorRef) {
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const rect = anchorRef.getBoundingClientRect();
|
|
334
|
-
const viewportWidth = typeof window === 'undefined' ? rect.width : window.innerWidth;
|
|
335
|
-
const horizontalPadding = 8;
|
|
336
|
-
const desktopMaxWidth = 560;
|
|
337
|
-
const maxWidth = Math.min(desktopMaxWidth, Math.max(280, viewportWidth - horizontalPadding * 2));
|
|
338
|
-
const nextWidth = Math.min(Math.max(rect.width, 280), maxWidth);
|
|
339
|
-
const maxLeft = viewportWidth - nextWidth - horizontalPadding;
|
|
340
|
-
|
|
341
|
-
popupTop = rect.bottom + 8;
|
|
342
|
-
popupWidth = nextWidth;
|
|
343
|
-
popupLeft = Math.min(Math.max(rect.left, horizontalPadding), Math.max(horizontalPadding, maxLeft));
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
function handlePickerKeydown(event: KeyboardEvent): void {
|
|
347
|
-
if (event.key === 'Escape' && closeOnEscape) {
|
|
348
|
-
event.preventDefault();
|
|
349
|
-
pickerOpen = false;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
async function focusPickerFirstControl(): Promise<void> {
|
|
354
|
-
if (!pickerOpen || !showDatePicker || typeof document === 'undefined') {
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
await tick();
|
|
359
|
-
|
|
360
|
-
const dialog = document.getElementById(pickerDialogId);
|
|
361
|
-
if (!dialog) {
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
const selectedDay = dialog.querySelector<HTMLButtonElement>(
|
|
366
|
-
'button[data-calendar-day="true"][aria-selected="true"]:not([disabled])',
|
|
367
|
-
);
|
|
368
|
-
if (selectedDay) {
|
|
369
|
-
selectedDay.focus();
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const firstEnabledDay = dialog.querySelector<HTMLButtonElement>(
|
|
374
|
-
'button[data-calendar-day="true"]:not([disabled])',
|
|
375
|
-
);
|
|
376
|
-
firstEnabledDay?.focus();
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
$effect(() => {
|
|
380
|
-
syncFromExternalValue();
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
$effect(() => {
|
|
384
|
-
if (!pickerOpen || disabled || !closeOnClickOutside || typeof document === 'undefined') {
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
const onDocumentPointerDown = (event: MouseEvent) => {
|
|
389
|
-
const target = event.target as Node | null;
|
|
390
|
-
const dialog = document.getElementById(pickerDialogId);
|
|
391
|
-
const insideAnchor = !!(target && anchorRef?.contains(target));
|
|
392
|
-
const insideDialog = !!(target && dialog?.contains(target));
|
|
393
|
-
|
|
394
|
-
if (!insideAnchor && !insideDialog) {
|
|
395
|
-
pickerOpen = false;
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
document.addEventListener('mousedown', onDocumentPointerDown);
|
|
400
|
-
|
|
401
|
-
return () => {
|
|
402
|
-
document.removeEventListener('mousedown', onDocumentPointerDown);
|
|
403
|
-
};
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
$effect(() => {
|
|
407
|
-
if (disablePortal || !pickerOpen || disabled) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
updatePortalPosition();
|
|
412
|
-
|
|
413
|
-
if (typeof window === 'undefined') {
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
const onWindowChange = () => {
|
|
418
|
-
updatePortalPosition();
|
|
419
|
-
};
|
|
420
|
-
|
|
421
|
-
window.addEventListener('resize', onWindowChange);
|
|
422
|
-
window.addEventListener('scroll', onWindowChange, true);
|
|
423
|
-
|
|
424
|
-
return () => {
|
|
425
|
-
window.removeEventListener('resize', onWindowChange);
|
|
426
|
-
window.removeEventListener('scroll', onWindowChange, true);
|
|
427
|
-
};
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
$effect(() => {
|
|
431
|
-
if (!withPicker || !pickerOpen || disabled) {
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
const overlay = registerOverlay({
|
|
436
|
-
kind: 'popover',
|
|
437
|
-
mount: disablePortal ? 'inline' : 'portal',
|
|
438
|
-
});
|
|
439
|
-
pickerZIndex = overlay.layer.content;
|
|
440
|
-
|
|
441
|
-
return () => {
|
|
442
|
-
overlay.unregister();
|
|
443
|
-
pickerZIndex = 1100;
|
|
444
|
-
};
|
|
445
|
-
});
|
|
1
|
+
<script lang="ts">import { tick } from "svelte";
|
|
2
|
+
import Portal from "../../Portal/Portal.svelte";
|
|
3
|
+
import { registerOverlay } from "../../OverlayStack";
|
|
4
|
+
import CtrlFieldShell from "../CtrlFieldShell.svelte";
|
|
5
|
+
import { makeFieldId } from "../utils";
|
|
6
|
+
import DateTimeCtrlPickerPanel from "./DateTimeCtrlPickerPanel.svelte";
|
|
7
|
+
import { formatDateTimeByFormat, getCalendarCells, getNowDateTimeParts, getYearMonthLabel, isDateSelectable, normalizePartsForMode, parseDateTimeByFormat, parsePartialDateTimeByFormat, parseNativeDate, resolveDateTimeFormat, shiftYearMonth, toNativeDate, validateDateTimeParts } from "./DateTimeCtrl.utils";
|
|
8
|
+
let { amLabel, closeOnPick = true, clearable = true, disableAutocomplete = false, disablePortal = false, disabled, error, hoursStep, inputFormat = "ISO", label, maxDate, maxTime, minDate, minTime, minutesStep, mode = "datetime", name, onblur, onchange, outputFormat = "ISO", pmLabel, popoverProps, placeholder, required, sid, tooltip, secondsStep, timePickerProps, timePresets, use12Hour, value = $bindable(null), withPicker = true, withTimeDropdown } = $props();
|
|
9
|
+
const id = $derived(makeFieldId(name, sid));
|
|
10
|
+
const pickerDialogId = $derived(`${id}_picker_dialog`);
|
|
11
|
+
const resolvedInputFormat = $derived(resolveDateTimeFormat(inputFormat, mode));
|
|
12
|
+
const resolvedOutputFormat = $derived(resolveDateTimeFormat(outputFormat, mode));
|
|
13
|
+
const showDatePicker = $derived(mode === "date" || mode === "datetime");
|
|
14
|
+
const showTimePicker = $derived(mode === "time" || mode === "datetime");
|
|
15
|
+
const rightSectionCount = $derived((withPicker ? 1 : 0) + (clearable ? 1 : 0));
|
|
16
|
+
const inputRightPaddingClass = $derived(rightSectionCount >= 2 ? "pr-20" : rightSectionCount === 1 ? "pr-12" : "pr-3");
|
|
17
|
+
const resolvedUse12Hour = $derived(use12Hour ?? timePickerProps?.use12Hour ?? false);
|
|
18
|
+
const resolvedWithTimeDropdown = $derived(withTimeDropdown ?? timePickerProps?.withDropdown ?? false);
|
|
19
|
+
const resolvedHoursStep = $derived(hoursStep ?? timePickerProps?.hoursStep ?? 1);
|
|
20
|
+
const resolvedMinutesStep = $derived(minutesStep ?? timePickerProps?.minutesStep ?? 1);
|
|
21
|
+
const resolvedSecondsStep = $derived(secondsStep ?? timePickerProps?.secondsStep ?? 1);
|
|
22
|
+
const resolvedAmLabel = $derived(amLabel ?? timePickerProps?.amLabel ?? "AM");
|
|
23
|
+
const resolvedPmLabel = $derived(pmLabel ?? timePickerProps?.pmLabel ?? "PM");
|
|
24
|
+
const resolvedTimePresets = $derived(timePresets ?? timePickerProps?.presets ?? []);
|
|
25
|
+
const closeOnClickOutside = $derived(popoverProps?.closeOnClickOutside ?? true);
|
|
26
|
+
const closeOnEscape = $derived(popoverProps?.closeOnEscape ?? true);
|
|
27
|
+
const dialogAriaLabel = $derived(popoverProps?.dialogAriaLabel ?? "Date time picker");
|
|
28
|
+
const panelClass = $derived(popoverProps?.panelClass ?? "");
|
|
29
|
+
let displayValue = $state("");
|
|
30
|
+
let pickerOpen = $state(false);
|
|
31
|
+
let pickerHour = $state(0);
|
|
32
|
+
let pickerMinute = $state(0);
|
|
33
|
+
let pickerSecond = $state(0);
|
|
34
|
+
let selectedDateIso = $state("");
|
|
35
|
+
let calendarYear = $state(getNowDateTimeParts().year);
|
|
36
|
+
let calendarMonth = $state(getNowDateTimeParts().month);
|
|
37
|
+
let localError = $state(undefined);
|
|
38
|
+
const effectiveError = $derived(error ?? localError);
|
|
39
|
+
let anchorRef = $state(null);
|
|
40
|
+
let popupTop = $state(0);
|
|
41
|
+
let popupLeft = $state(0);
|
|
42
|
+
let popupWidth = $state(0);
|
|
43
|
+
let pickerZIndex = $state(1100);
|
|
44
|
+
const calendarMonthLabel = $derived(getYearMonthLabel(calendarYear, calendarMonth));
|
|
45
|
+
const calendarCells = $derived(getCalendarCells(calendarYear, calendarMonth));
|
|
46
|
+
function syncPickerFromParts(parts) {
|
|
47
|
+
selectedDateIso = toNativeDate(parts);
|
|
48
|
+
pickerHour = parts.hour;
|
|
49
|
+
pickerMinute = parts.minute;
|
|
50
|
+
pickerSecond = parts.second;
|
|
51
|
+
calendarYear = parts.year;
|
|
52
|
+
calendarMonth = parts.month;
|
|
53
|
+
}
|
|
54
|
+
function getCurrentParts() {
|
|
55
|
+
const trimmed = displayValue.trim();
|
|
56
|
+
if (trimmed === "") {
|
|
57
|
+
const now = normalizePartsForMode(getNowDateTimeParts(), mode);
|
|
58
|
+
return now ?? getNowDateTimeParts();
|
|
59
|
+
}
|
|
60
|
+
const parsed = parseDateTimeByFormat(trimmed, resolvedInputFormat, mode);
|
|
61
|
+
if (parsed) {
|
|
62
|
+
return parsed;
|
|
63
|
+
}
|
|
64
|
+
const now = normalizePartsForMode(getNowDateTimeParts(), mode);
|
|
65
|
+
return now ?? getNowDateTimeParts();
|
|
66
|
+
}
|
|
67
|
+
function emitParts(parts) {
|
|
68
|
+
const normalizedInput = formatDateTimeByFormat(parts, resolvedInputFormat);
|
|
69
|
+
const normalizedOutput = formatDateTimeByFormat(parts, resolvedOutputFormat);
|
|
70
|
+
displayValue = normalizedInput;
|
|
71
|
+
value = normalizedOutput;
|
|
72
|
+
syncPickerFromParts(parts);
|
|
73
|
+
onchange?.(normalizedOutput);
|
|
74
|
+
}
|
|
75
|
+
function applyParts(parts, closeAfterApply = false) {
|
|
76
|
+
const normalized = normalizePartsForMode(parts, mode);
|
|
77
|
+
if (!normalized) {
|
|
78
|
+
localError = "Invalid date/time value";
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const constraintError = validateDateTimeParts(normalized, mode, {
|
|
82
|
+
minDate,
|
|
83
|
+
maxDate,
|
|
84
|
+
minTime,
|
|
85
|
+
maxTime
|
|
86
|
+
});
|
|
87
|
+
if (constraintError) {
|
|
88
|
+
localError = constraintError;
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
localError = undefined;
|
|
92
|
+
emitParts(normalized);
|
|
93
|
+
if (closeAfterApply && closeOnPick) {
|
|
94
|
+
pickerOpen = false;
|
|
95
|
+
}
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
function syncFromExternalValue() {
|
|
99
|
+
const incoming = value;
|
|
100
|
+
if (!incoming || incoming.trim() === "") {
|
|
101
|
+
displayValue = "";
|
|
102
|
+
selectedDateIso = "";
|
|
103
|
+
localError = undefined;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const parsed = parseDateTimeByFormat(incoming, resolvedOutputFormat, mode);
|
|
107
|
+
if (!parsed) {
|
|
108
|
+
displayValue = incoming;
|
|
109
|
+
selectedDateIso = "";
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
displayValue = formatDateTimeByFormat(parsed, resolvedInputFormat);
|
|
113
|
+
syncPickerFromParts(parsed);
|
|
114
|
+
}
|
|
115
|
+
function clearValue() {
|
|
116
|
+
displayValue = "";
|
|
117
|
+
selectedDateIso = "";
|
|
118
|
+
localError = undefined;
|
|
119
|
+
value = null;
|
|
120
|
+
onchange?.(null);
|
|
121
|
+
}
|
|
122
|
+
function commitFromTextInput() {
|
|
123
|
+
const trimmed = displayValue.trim();
|
|
124
|
+
if (trimmed === "") {
|
|
125
|
+
clearValue();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const parsed = parseDateTimeByFormat(trimmed, resolvedInputFormat, mode);
|
|
129
|
+
if (parsed) {
|
|
130
|
+
applyParts(parsed);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// Fallback: if input is partial, complete missing segments from current date/time
|
|
134
|
+
// and commit without blocking tab/blur flow.
|
|
135
|
+
if (!disableAutocomplete) {
|
|
136
|
+
const partialParsed = parsePartialDateTimeByFormat(trimmed, resolvedInputFormat, mode);
|
|
137
|
+
if (partialParsed) {
|
|
138
|
+
applyParts(partialParsed);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Do not block blur/tab flow on partial or non-matching input.
|
|
143
|
+
// Keep current text and skip hard error here.
|
|
144
|
+
localError = undefined;
|
|
145
|
+
}
|
|
146
|
+
function handleSelectCalendarDate(isoDate) {
|
|
147
|
+
const parsedDate = parseNativeDate(isoDate);
|
|
148
|
+
if (!parsedDate) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const current = getCurrentParts();
|
|
152
|
+
current.year = parsedDate.year;
|
|
153
|
+
current.month = parsedDate.month;
|
|
154
|
+
current.day = parsedDate.day;
|
|
155
|
+
const closeAfterApply = mode === "date";
|
|
156
|
+
applyParts(current, closeAfterApply);
|
|
157
|
+
}
|
|
158
|
+
function handleTimeChange(value) {
|
|
159
|
+
const current = getCurrentParts();
|
|
160
|
+
current.hour = value.hour;
|
|
161
|
+
current.minute = value.minute;
|
|
162
|
+
current.second = value.second;
|
|
163
|
+
const closeAfterApply = mode === "time";
|
|
164
|
+
applyParts(current, closeAfterApply);
|
|
165
|
+
}
|
|
166
|
+
function handleNow() {
|
|
167
|
+
const now = normalizePartsForMode(getNowDateTimeParts(), mode);
|
|
168
|
+
if (!now) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
applyParts(now, true);
|
|
172
|
+
}
|
|
173
|
+
function shiftMonth(delta) {
|
|
174
|
+
const shifted = shiftYearMonth(calendarYear, calendarMonth, delta);
|
|
175
|
+
calendarYear = shifted.year;
|
|
176
|
+
calendarMonth = shifted.month;
|
|
177
|
+
}
|
|
178
|
+
function handleTextKeydown(event) {
|
|
179
|
+
if (event.key === "Enter") {
|
|
180
|
+
commitFromTextInput();
|
|
181
|
+
event.currentTarget.blur();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (event.key === "Escape" && closeOnEscape) {
|
|
185
|
+
pickerOpen = false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function handleInputBlur() {
|
|
189
|
+
commitFromTextInput();
|
|
190
|
+
onblur?.();
|
|
191
|
+
}
|
|
192
|
+
function updatePortalPosition() {
|
|
193
|
+
if (!anchorRef) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const rect = anchorRef.getBoundingClientRect();
|
|
197
|
+
const viewportWidth = typeof window === "undefined" ? rect.width : window.innerWidth;
|
|
198
|
+
const horizontalPadding = 8;
|
|
199
|
+
const desktopMaxWidth = 560;
|
|
200
|
+
const maxWidth = Math.min(desktopMaxWidth, Math.max(280, viewportWidth - horizontalPadding * 2));
|
|
201
|
+
const nextWidth = Math.min(Math.max(rect.width, 280), maxWidth);
|
|
202
|
+
const maxLeft = viewportWidth - nextWidth - horizontalPadding;
|
|
203
|
+
popupTop = rect.bottom + 8;
|
|
204
|
+
popupWidth = nextWidth;
|
|
205
|
+
popupLeft = Math.min(Math.max(rect.left, horizontalPadding), Math.max(horizontalPadding, maxLeft));
|
|
206
|
+
}
|
|
207
|
+
function handlePickerKeydown(event) {
|
|
208
|
+
if (event.key === "Escape" && closeOnEscape) {
|
|
209
|
+
event.preventDefault();
|
|
210
|
+
pickerOpen = false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function focusPickerFirstControl() {
|
|
214
|
+
if (!pickerOpen || !showDatePicker || typeof document === "undefined") {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
await tick();
|
|
218
|
+
const dialog = document.getElementById(pickerDialogId);
|
|
219
|
+
if (!dialog) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const selectedDay = dialog.querySelector("button[data-calendar-day=\"true\"][aria-selected=\"true\"]:not([disabled])");
|
|
223
|
+
if (selectedDay) {
|
|
224
|
+
selectedDay.focus();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
const firstEnabledDay = dialog.querySelector("button[data-calendar-day=\"true\"]:not([disabled])");
|
|
228
|
+
firstEnabledDay?.focus();
|
|
229
|
+
}
|
|
230
|
+
$effect(() => {
|
|
231
|
+
syncFromExternalValue();
|
|
232
|
+
});
|
|
233
|
+
$effect(() => {
|
|
234
|
+
if (!pickerOpen || disabled || !closeOnClickOutside || typeof document === "undefined") {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const onDocumentPointerDown = (event) => {
|
|
238
|
+
const target = event.target;
|
|
239
|
+
const dialog = document.getElementById(pickerDialogId);
|
|
240
|
+
const insideAnchor = !!(target && anchorRef?.contains(target));
|
|
241
|
+
const insideDialog = !!(target && dialog?.contains(target));
|
|
242
|
+
if (!insideAnchor && !insideDialog) {
|
|
243
|
+
pickerOpen = false;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
document.addEventListener("mousedown", onDocumentPointerDown);
|
|
247
|
+
return () => {
|
|
248
|
+
document.removeEventListener("mousedown", onDocumentPointerDown);
|
|
249
|
+
};
|
|
250
|
+
});
|
|
251
|
+
$effect(() => {
|
|
252
|
+
if (disablePortal || !pickerOpen || disabled) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
updatePortalPosition();
|
|
256
|
+
if (typeof window === "undefined") {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const onWindowChange = () => {
|
|
260
|
+
updatePortalPosition();
|
|
261
|
+
};
|
|
262
|
+
window.addEventListener("resize", onWindowChange);
|
|
263
|
+
window.addEventListener("scroll", onWindowChange, true);
|
|
264
|
+
return () => {
|
|
265
|
+
window.removeEventListener("resize", onWindowChange);
|
|
266
|
+
window.removeEventListener("scroll", onWindowChange, true);
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
$effect(() => {
|
|
270
|
+
if (!withPicker || !pickerOpen || disabled) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const overlay = registerOverlay({
|
|
274
|
+
kind: "popover",
|
|
275
|
+
mount: disablePortal ? "inline" : "portal"
|
|
276
|
+
});
|
|
277
|
+
pickerZIndex = overlay.layer.content;
|
|
278
|
+
return () => {
|
|
279
|
+
overlay.unregister();
|
|
280
|
+
pickerZIndex = 1100;
|
|
281
|
+
};
|
|
282
|
+
});
|
|
446
283
|
</script>
|
|
447
284
|
|
|
448
|
-
<
|
|
449
|
-
{#if label}
|
|
450
|
-
<label class="label text-sm font-medium" for={id}>
|
|
451
|
-
{label}{#if required}<span class="text-error-500">*</span>{/if}
|
|
452
|
-
</label>
|
|
453
|
-
{/if}
|
|
454
|
-
|
|
285
|
+
<CtrlFieldShell error={effectiveError} {id} {label} {required} {tooltip}>
|
|
455
286
|
<div class="relative" bind:this={anchorRef}>
|
|
456
287
|
<div class="relative">
|
|
457
288
|
<input
|
|
458
289
|
{id}
|
|
459
290
|
{disabled}
|
|
291
|
+
{name}
|
|
460
292
|
{required}
|
|
461
293
|
class={`input ${inputRightPaddingClass}`}
|
|
462
294
|
class:input-error={!!effectiveError}
|
|
@@ -644,8 +476,4 @@
|
|
|
644
476
|
{/if}
|
|
645
477
|
</Portal>
|
|
646
478
|
</div>
|
|
647
|
-
|
|
648
|
-
{#if effectiveError}
|
|
649
|
-
<p class="text-xs text-error-500">{effectiveError}</p>
|
|
650
|
-
{/if}
|
|
651
|
-
</div>
|
|
479
|
+
</CtrlFieldShell>
|