@zeyos/zx 4.2.0 → 4.3.4
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/CHANGELOG.md +148 -1
- package/README.md +5 -3
- package/dist/types/components/calendar/calendar-model.d.ts +22 -0
- package/dist/types/components/calendar/calendar.d.ts +19 -2
- package/dist/types/components/card-view/card-view.d.ts +1 -1
- package/dist/types/components/card-view/record-card.d.ts +47 -5
- package/dist/types/components/dialog/dialog.d.ts +6 -1
- package/dist/types/components/dropdown/dropdown.d.ts +5 -0
- package/dist/types/components/kanban-view/kanban-policy.d.ts +268 -0
- package/dist/types/components/kanban-view/kanban-view.d.ts +669 -24
- package/dist/types/components/launcher/launcher.d.ts +5 -0
- package/dist/types/components/message/message.d.ts +9 -1
- package/dist/types/components/modal/modal.d.ts +44 -7
- package/dist/types/components/sheet/sheet.d.ts +22 -12
- package/dist/types/core/component.d.ts +6 -1
- package/dist/types/core/overlay-host.d.ts +12 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/zx-zeyos.esm.js +173 -43
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +876 -230
- package/dist/zx.esm.js +2053 -338
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +2053 -338
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +250 -2
- package/docs/llms.md +67 -48
- package/docs/llms.txt +1 -3
- package/package.json +1 -2
- package/src/components/account-menu/account-menu.css +1 -1
- package/src/components/app-rail/app-rail.css +1 -1
- package/src/components/calendar/calendar-model.js +45 -0
- package/src/components/calendar/calendar.css +96 -34
- package/src/components/calendar/calendar.js +122 -29
- package/src/components/card-view/card-view.css +211 -9
- package/src/components/card-view/card-view.js +7 -7
- package/src/components/card-view/record-card.js +92 -19
- package/src/components/context-menu/context-menu.css +2 -2
- package/src/components/context-menu/context-menu.js +5 -1
- package/src/components/date-picker/date-picker.css +18 -11
- package/src/components/datebox/date-range-box.css +4 -2
- package/src/components/datebox/datebox.css +4 -2
- package/src/components/dialog/dialog.css +4 -3
- package/src/components/dialog/dialog.js +2 -1
- package/src/components/dropdown/dropdown.css +4 -2
- package/src/components/dropdown/dropdown.js +7 -2
- package/src/components/kanban-view/kanban-policy.js +412 -0
- package/src/components/kanban-view/kanban-view.css +480 -36
- package/src/components/kanban-view/kanban-view.js +1514 -235
- package/src/components/launcher/launcher.css +10 -8
- package/src/components/launcher/launcher.js +9 -2
- package/src/components/menu-button/menu-button.css +2 -2
- package/src/components/message/message.css +15 -25
- package/src/components/message/message.js +17 -11
- package/src/components/modal/modal.css +6 -4
- package/src/components/modal/modal.js +99 -25
- package/src/components/select/select.css +9 -7
- package/src/components/sheet/sheet.css +25 -7
- package/src/components/sheet/sheet.js +43 -23
- package/src/components/table/table.css +5 -3
- package/src/components/tag-picker/tag-picker.css +5 -3
- package/src/components/tooltip/tooltip.css +4 -2
- package/src/components/tooltip/tooltip.js +4 -1
- package/src/core/component.js +8 -3
- package/src/core/overlay-host.js +23 -0
- package/src/index.js +8 -3
- package/styles/tokens/dark.css +4 -0
- package/styles/tokens/semantic.css +83 -8
- package/DESIGN-SYSTEM.md +0 -182
|
@@ -9,15 +9,20 @@
|
|
|
9
9
|
min-inline-size: 0;
|
|
10
10
|
overflow: hidden;
|
|
11
11
|
border: 1px solid var(--zx-color-border);
|
|
12
|
-
border-radius:
|
|
12
|
+
border-radius: 0;
|
|
13
13
|
background: var(--zx-color-bg-surface);
|
|
14
14
|
color: var(--zx-color-text);
|
|
15
15
|
font-size: var(--zx-text-sm);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
/*
|
|
19
|
+
* The heading is the only elastic track. With `1fr auto 1fr` the end-justified commands group
|
|
20
|
+
* kept its min-content width inside a narrower `1fr` track and bled back over the centered
|
|
21
|
+
* heading; giving navigation and commands their natural width lets the heading ellipsize instead.
|
|
22
|
+
*/
|
|
18
23
|
.zx-calendar__toolbar {
|
|
19
24
|
display: grid;
|
|
20
|
-
grid-template-columns: 1fr auto
|
|
25
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
21
26
|
align-items: center;
|
|
22
27
|
gap: var(--zx-space-3);
|
|
23
28
|
min-inline-size: 0;
|
|
@@ -35,7 +40,14 @@
|
|
|
35
40
|
|
|
36
41
|
.zx-calendar__navigation { justify-self: start; gap: var(--zx-space-1); }
|
|
37
42
|
.zx-calendar__commands { justify-self: end; gap: var(--zx-space-3); }
|
|
38
|
-
.zx-calendar__views {
|
|
43
|
+
.zx-calendar__views {
|
|
44
|
+
block-size: var(--zx-control-height);
|
|
45
|
+
gap: 2px;
|
|
46
|
+
overflow-x: auto;
|
|
47
|
+
border-radius: 0;
|
|
48
|
+
background: var(--zx-color-bg-muted);
|
|
49
|
+
padding: 3px;
|
|
50
|
+
}
|
|
39
51
|
|
|
40
52
|
.zx-calendar__toolbar-button,
|
|
41
53
|
.zx-calendar__view-button,
|
|
@@ -59,15 +71,21 @@
|
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
.zx-calendar__toolbar-button:has(.zx-icon) { padding-inline: var(--zx-space-2); }
|
|
62
|
-
.zx-calendar__view-button {
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
.zx-calendar__view-button {
|
|
75
|
+
block-size: 100%;
|
|
76
|
+
min-block-size: 0;
|
|
77
|
+
border-color: transparent;
|
|
78
|
+
border-radius: 0;
|
|
79
|
+
background: transparent;
|
|
80
|
+
padding-inline: var(--zx-space-3);
|
|
81
|
+
}
|
|
65
82
|
.zx-calendar__view-button[aria-pressed="true"] {
|
|
66
83
|
position: relative;
|
|
67
84
|
z-index: 1;
|
|
68
|
-
border-color: var(--zx-color-
|
|
69
|
-
background: var(--zx-color-bg-
|
|
70
|
-
|
|
85
|
+
border-color: var(--zx-color-border-control);
|
|
86
|
+
background: var(--zx-color-bg-control);
|
|
87
|
+
box-shadow: var(--zx-shadow-2);
|
|
88
|
+
color: var(--zx-color-text);
|
|
71
89
|
}
|
|
72
90
|
|
|
73
91
|
.zx-calendar__new {
|
|
@@ -81,7 +99,7 @@
|
|
|
81
99
|
}
|
|
82
100
|
|
|
83
101
|
.zx-calendar__toolbar-button:hover:not(:disabled),
|
|
84
|
-
.zx-calendar__view-button:hover:not(:disabled) { background: var(--zx-color-bg-hover); }
|
|
102
|
+
.zx-calendar__view-button:hover:not(:disabled):not([aria-pressed="true"]) { background: var(--zx-color-bg-hover); }
|
|
85
103
|
.zx-calendar__new:hover:not(:disabled) { background: var(--zx-color-accent-hover); }
|
|
86
104
|
.zx-calendar button:disabled { cursor: default; opacity: .55; }
|
|
87
105
|
|
|
@@ -148,6 +166,7 @@
|
|
|
148
166
|
min-block-size: calc(var(--zx-control-height) + var(--zx-space-2));
|
|
149
167
|
place-content: center;
|
|
150
168
|
border: 0;
|
|
169
|
+
border-radius: 0;
|
|
151
170
|
border-inline-end: 1px solid var(--zx-color-border);
|
|
152
171
|
background: var(--zx-color-bg-surface);
|
|
153
172
|
color: var(--zx-color-text);
|
|
@@ -227,6 +246,7 @@
|
|
|
227
246
|
.zx-calendar__slot {
|
|
228
247
|
min-inline-size: 0;
|
|
229
248
|
border: 0;
|
|
249
|
+
border-radius: 0;
|
|
230
250
|
border-block-start: 1px solid var(--zx-color-border);
|
|
231
251
|
border-inline-end: 1px solid var(--zx-color-border);
|
|
232
252
|
background: var(--zx-color-bg-surface);
|
|
@@ -261,6 +281,7 @@
|
|
|
261
281
|
inline-size: 100%;
|
|
262
282
|
block-size: 100%;
|
|
263
283
|
min-inline-size: 0;
|
|
284
|
+
min-block-size: 0;
|
|
264
285
|
overflow: hidden;
|
|
265
286
|
border: 1px solid color-mix(in srgb, var(--zx-calendar-event-color) 65%, var(--zx-color-border));
|
|
266
287
|
border-inline-start: 3px solid var(--zx-calendar-event-color);
|
|
@@ -310,8 +331,11 @@
|
|
|
310
331
|
z-index: 3;
|
|
311
332
|
display: grid;
|
|
312
333
|
block-size: 8px;
|
|
334
|
+
min-block-size: 0;
|
|
313
335
|
place-items: center;
|
|
336
|
+
appearance: none;
|
|
314
337
|
border: 0;
|
|
338
|
+
border-radius: 0;
|
|
315
339
|
background: transparent;
|
|
316
340
|
padding: 0;
|
|
317
341
|
cursor: ns-resize;
|
|
@@ -329,6 +353,7 @@
|
|
|
329
353
|
.zx-calendar__event[data-variant="span"] .zx-calendar__event-action,
|
|
330
354
|
.zx-calendar__event[data-variant="month"] .zx-calendar__event-action {
|
|
331
355
|
display: flex;
|
|
356
|
+
min-block-size: 0;
|
|
332
357
|
align-items: center;
|
|
333
358
|
gap: var(--zx-space-1);
|
|
334
359
|
padding-block: 2px;
|
|
@@ -358,10 +383,17 @@
|
|
|
358
383
|
}
|
|
359
384
|
|
|
360
385
|
.zx-calendar__agenda { display: grid; }
|
|
361
|
-
.zx-calendar__agenda-day {
|
|
386
|
+
.zx-calendar__agenda-day {
|
|
387
|
+
display: grid;
|
|
388
|
+
grid-template-columns: minmax(10rem, 22%) minmax(0, 1fr);
|
|
389
|
+
border-block-end: 1px solid var(--zx-color-border);
|
|
390
|
+
}
|
|
362
391
|
.zx-calendar__agenda-heading {
|
|
363
|
-
align-self:
|
|
392
|
+
align-self: stretch;
|
|
393
|
+
appearance: none;
|
|
364
394
|
border: 0;
|
|
395
|
+
border-inline-end: 1px solid var(--zx-color-border);
|
|
396
|
+
border-radius: 0;
|
|
365
397
|
background: var(--zx-color-bg-muted);
|
|
366
398
|
color: var(--zx-color-text);
|
|
367
399
|
padding: var(--zx-space-3);
|
|
@@ -371,9 +403,13 @@
|
|
|
371
403
|
cursor: pointer;
|
|
372
404
|
}
|
|
373
405
|
.zx-calendar__agenda-heading[aria-current="date"] { color: var(--zx-color-accent); }
|
|
406
|
+
.zx-calendar__agenda-events {
|
|
407
|
+
display: grid;
|
|
408
|
+
min-inline-size: 0;
|
|
409
|
+
}
|
|
374
410
|
.zx-calendar__agenda-empty { margin: 0; color: var(--zx-color-text-muted); padding: var(--zx-space-3); }
|
|
375
|
-
.zx-calendar__agenda-
|
|
376
|
-
.zx-calendar__agenda-
|
|
411
|
+
.zx-calendar__agenda-events > .zx-calendar__event { border-block-end: 1px solid var(--zx-color-border); }
|
|
412
|
+
.zx-calendar__agenda-events > .zx-calendar__event:last-child { border-block-end: 0; }
|
|
377
413
|
.zx-calendar__event[data-variant="agenda"] .zx-calendar__event-action {
|
|
378
414
|
grid-template-columns: minmax(6rem, 18%) minmax(0, 1fr);
|
|
379
415
|
grid-template-areas: "time title" "time location";
|
|
@@ -418,26 +454,13 @@
|
|
|
418
454
|
text-transform: uppercase;
|
|
419
455
|
letter-spacing: .04em;
|
|
420
456
|
}
|
|
421
|
-
.zx-calendar__month-weekdays .zx-calendar__month-weekday { grid-row: 1; }
|
|
422
|
-
.zx-calendar__month-weekdays .zx-calendar__month-weekday:nth-last-child(7) { grid-column: 2; }
|
|
423
|
-
.zx-calendar__month-weekdays .zx-calendar__month-weekday:nth-last-child(6) { grid-column: 3; }
|
|
424
|
-
.zx-calendar__month-weekdays .zx-calendar__month-weekday:nth-last-child(5) { grid-column: 4; }
|
|
425
|
-
.zx-calendar__month-weekdays .zx-calendar__month-weekday:nth-last-child(4) { grid-column: 5; }
|
|
426
|
-
.zx-calendar__month-weekdays .zx-calendar__month-weekday:nth-last-child(3) { grid-column: 6; }
|
|
427
|
-
.zx-calendar__month-weekdays .zx-calendar__month-weekday:nth-last-child(2) { grid-column: 7; }
|
|
428
|
-
.zx-calendar__month-weekdays .zx-calendar__month-weekday:nth-last-child(1) { grid-column: 8; }
|
|
429
|
-
.zx-calendar__month-weekdays:not(:has(.zx-calendar__month-week-gutter)) .zx-calendar__month-weekday:nth-last-child(7) { grid-column: 1; }
|
|
430
|
-
.zx-calendar__month-weekdays:not(:has(.zx-calendar__month-week-gutter)) .zx-calendar__month-weekday:nth-last-child(6) { grid-column: 2; }
|
|
431
|
-
.zx-calendar__month-weekdays:not(:has(.zx-calendar__month-week-gutter)) .zx-calendar__month-weekday:nth-last-child(5) { grid-column: 3; }
|
|
432
|
-
.zx-calendar__month-weekdays:not(:has(.zx-calendar__month-week-gutter)) .zx-calendar__month-weekday:nth-last-child(4) { grid-column: 4; }
|
|
433
|
-
.zx-calendar__month-weekdays:not(:has(.zx-calendar__month-week-gutter)) .zx-calendar__month-weekday:nth-last-child(3) { grid-column: 5; }
|
|
434
|
-
.zx-calendar__month-weekdays:not(:has(.zx-calendar__month-week-gutter)) .zx-calendar__month-weekday:nth-last-child(2) { grid-column: 6; }
|
|
435
|
-
.zx-calendar__month-weekdays:not(:has(.zx-calendar__month-week-gutter)) .zx-calendar__month-weekday:nth-last-child(1) { grid-column: 7; }
|
|
436
457
|
.zx-calendar__month-week { min-block-size: 7.5rem; border-block-end: 1px solid var(--zx-color-border); }
|
|
437
458
|
.zx-calendar__month-week-number {
|
|
438
|
-
grid-row: 1
|
|
459
|
+
grid-row: 1;
|
|
460
|
+
appearance: none;
|
|
439
461
|
border: 0;
|
|
440
462
|
border-inline-end: 1px solid var(--zx-color-border);
|
|
463
|
+
border-radius: 0;
|
|
441
464
|
background: var(--zx-color-bg-muted);
|
|
442
465
|
color: var(--zx-color-text-muted);
|
|
443
466
|
font: inherit;
|
|
@@ -452,11 +475,21 @@
|
|
|
452
475
|
}
|
|
453
476
|
.zx-calendar__month-week:not(:has(.zx-calendar__month-week-number)) .zx-calendar__month-days,
|
|
454
477
|
.zx-calendar__month-week:not(:has(.zx-calendar__month-week-number)) .zx-calendar__month-events { grid-column: 1; }
|
|
455
|
-
.zx-calendar__month-days {
|
|
478
|
+
.zx-calendar__month-days {
|
|
479
|
+
grid-row: 1;
|
|
480
|
+
min-block-size: 7.5rem;
|
|
481
|
+
align-items: stretch;
|
|
482
|
+
}
|
|
456
483
|
.zx-calendar__month-day {
|
|
484
|
+
display: flex;
|
|
485
|
+
block-size: 100%;
|
|
486
|
+
appearance: none;
|
|
487
|
+
align-items: flex-start;
|
|
488
|
+
justify-content: flex-end;
|
|
457
489
|
min-block-size: var(--zx-control-height);
|
|
458
490
|
border: 0;
|
|
459
491
|
border-inline-end: 1px solid var(--zx-color-border);
|
|
492
|
+
border-radius: 0;
|
|
460
493
|
background: var(--zx-color-bg-surface);
|
|
461
494
|
color: var(--zx-color-text);
|
|
462
495
|
padding: var(--zx-space-2);
|
|
@@ -464,12 +497,34 @@
|
|
|
464
497
|
text-align: end;
|
|
465
498
|
cursor: pointer;
|
|
466
499
|
}
|
|
500
|
+
.zx-calendar__month-day:last-child { border-inline-end: 0; }
|
|
467
501
|
.zx-calendar__month-day[data-other-month="true"] { color: var(--zx-color-text-muted); background: var(--zx-color-bg-muted); }
|
|
468
|
-
.zx-calendar__month-day[aria-current="date"] {
|
|
502
|
+
.zx-calendar__month-day[aria-current="date"] {
|
|
503
|
+
color: var(--zx-color-accent);
|
|
504
|
+
font-weight: 750;
|
|
505
|
+
}
|
|
469
506
|
.zx-calendar__month-day:hover:not(:disabled) { background: var(--zx-color-bg-hover); }
|
|
470
|
-
.zx-calendar__month-events {
|
|
507
|
+
.zx-calendar__month-events {
|
|
508
|
+
grid-row: 1;
|
|
509
|
+
z-index: 2;
|
|
510
|
+
grid-auto-rows: calc(var(--zx-control-height) - var(--zx-space-2));
|
|
511
|
+
align-content: start;
|
|
512
|
+
gap: 2px 0;
|
|
513
|
+
padding-block: calc(var(--zx-control-height) + var(--zx-space-1)) var(--zx-space-1);
|
|
514
|
+
pointer-events: none;
|
|
515
|
+
}
|
|
516
|
+
.zx-calendar__month-events > .zx-calendar__event,
|
|
517
|
+
.zx-calendar__month-events > .zx-calendar__more { pointer-events: auto; }
|
|
518
|
+
.zx-calendar__event[data-variant="month"] .zx-calendar__event-action {
|
|
519
|
+
border-color: color-mix(in srgb, var(--zx-calendar-event-color) 55%, var(--zx-color-border));
|
|
520
|
+
border-inline-start-width: 1px;
|
|
521
|
+
border-radius: 0;
|
|
522
|
+
background: color-mix(in srgb, var(--zx-calendar-event-color) 13%, var(--zx-color-bg-surface));
|
|
523
|
+
}
|
|
524
|
+
.zx-calendar__event[data-variant="month"][data-all-day="true"] .zx-calendar__event-time { display: none; }
|
|
471
525
|
.zx-calendar__more {
|
|
472
526
|
align-self: center;
|
|
527
|
+
min-block-size: 0;
|
|
473
528
|
overflow: hidden;
|
|
474
529
|
border: 0;
|
|
475
530
|
background: transparent;
|
|
@@ -546,7 +601,14 @@
|
|
|
546
601
|
.zx-calendar__view-button { flex: 1 0 auto; }
|
|
547
602
|
.zx-calendar__new span { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip-path: inset(50%); }
|
|
548
603
|
.zx-calendar__agenda-day { grid-template-columns: 1fr; }
|
|
549
|
-
.zx-calendar__agenda-heading {
|
|
604
|
+
.zx-calendar__agenda-heading {
|
|
605
|
+
position: sticky;
|
|
606
|
+
inset-block-start: 0;
|
|
607
|
+
z-index: 3;
|
|
608
|
+
border-inline-end: 0;
|
|
609
|
+
border-block-end: 1px solid var(--zx-color-border);
|
|
610
|
+
}
|
|
611
|
+
.zx-calendar__agenda-events { grid-column: 1; }
|
|
550
612
|
.zx-calendar__event[data-variant="agenda"] .zx-calendar__event-action { grid-template-columns: 5.5rem minmax(0, 1fr); }
|
|
551
613
|
.zx-calendar__year { grid-template-columns: 1fr; padding: var(--zx-space-3); }
|
|
552
614
|
}
|
|
@@ -3,8 +3,8 @@ import { h } from '../../core/dom.js';
|
|
|
3
3
|
import { icon } from '../../core/icons.js';
|
|
4
4
|
import {
|
|
5
5
|
CALENDAR_VIEWS, addCalendarDays, calendarDayDifference, calendarDayKey,
|
|
6
|
-
calendarEventIntersects, calendarEventSpansDays,
|
|
7
|
-
layoutCalendarSpans, layoutTimedCalendarEvents, normalizeCalendarEvents,
|
|
6
|
+
calendarEventIntersects, calendarEventSpansDays, calendarPageDate, calendarRange,
|
|
7
|
+
cloneCalendarEvent, layoutCalendarSpans, layoutTimedCalendarEvents, normalizeCalendarEvents,
|
|
8
8
|
shiftCalendarEvent, startOfCalendarDay
|
|
9
9
|
} from './calendar-model.js';
|
|
10
10
|
|
|
@@ -289,7 +289,9 @@ export class Calendar extends Component {
|
|
|
289
289
|
? changes.durationEditable : current.durationEditable,
|
|
290
290
|
data: changes.data ?? current.data
|
|
291
291
|
};
|
|
292
|
-
|
|
292
|
+
// `source` is assembled from canonical keys, so the configured readers must not be reapplied
|
|
293
|
+
// here — but `dateUnit` must, or a numeric change is read in the wrong unit.
|
|
294
|
+
const [next] = normalizeCalendarEvents([source], { dateUnit: this.options.dateUnit });
|
|
293
295
|
next.data = source.data;
|
|
294
296
|
this._events = this._events.map((event, eventIndex) => eventIndex === index ? next : event);
|
|
295
297
|
this._mutationVersions.delete(String(id));
|
|
@@ -452,7 +454,11 @@ export class Calendar extends Component {
|
|
|
452
454
|
if (!calendarEventIntersects(event, day, next)) return false;
|
|
453
455
|
return event.start >= day || index === 0;
|
|
454
456
|
}).sort(compareEvents);
|
|
455
|
-
const row = h('section', {
|
|
457
|
+
const row = h('section', {
|
|
458
|
+
class: 'zx-calendar__agenda-day',
|
|
459
|
+
role: 'row',
|
|
460
|
+
dataset: { date: calendarDayKey(day) }
|
|
461
|
+
},
|
|
456
462
|
h('button', {
|
|
457
463
|
type: 'button',
|
|
458
464
|
class: 'zx-calendar__agenda-heading',
|
|
@@ -461,11 +467,13 @@ export class Calendar extends Component {
|
|
|
461
467
|
ariaCurrent: sameCalendarDay(day, this._now()) ? 'date' : null,
|
|
462
468
|
disabled: this.options.disabled
|
|
463
469
|
}, this._format(day, { weekday: 'long', day: 'numeric', month: 'long' })));
|
|
470
|
+
const entries = h('div', { class: 'zx-calendar__agenda-events', role: 'gridcell' });
|
|
464
471
|
if (!events.length) {
|
|
465
|
-
|
|
472
|
+
entries.append(h('p', { class: 'zx-calendar__agenda-empty' }, this.msg('noEvents')));
|
|
466
473
|
} else {
|
|
467
|
-
for (const event of events)
|
|
474
|
+
for (const event of events) entries.append(this._eventElement(event, { variant: 'agenda', day }));
|
|
468
475
|
}
|
|
476
|
+
row.append(entries);
|
|
469
477
|
grid.append(row);
|
|
470
478
|
}
|
|
471
479
|
return grid;
|
|
@@ -721,6 +729,7 @@ export class Calendar extends Component {
|
|
|
721
729
|
dataset: {
|
|
722
730
|
eventId: key,
|
|
723
731
|
variant: context.variant,
|
|
732
|
+
allDay: event.allDay ? 'true' : null,
|
|
724
733
|
draggable: this._canMove(event) ? 'true' : null
|
|
725
734
|
},
|
|
726
735
|
style: event.color ? { '--zx-calendar-event-color': event.color } : null
|
|
@@ -989,7 +998,9 @@ export class Calendar extends Component {
|
|
|
989
998
|
const item = this._eventById(control.dataset.eventId);
|
|
990
999
|
const action = control.dataset.eventAction === 'resize' ? 'resize' : 'move';
|
|
991
1000
|
if (!item || (action === 'move' ? !this._canMove(item) : !this._canResize(item))) return;
|
|
992
|
-
|
|
1001
|
+
event.preventDefault();
|
|
1002
|
+
this._capturePointer(event.pointerId);
|
|
1003
|
+
const offset = action === 'move' ? this._dragOffsetForEvent(item, event.clientX, event.clientY) : null;
|
|
993
1004
|
this._pointer = {
|
|
994
1005
|
type: 'event',
|
|
995
1006
|
pointerId: event.pointerId,
|
|
@@ -999,6 +1010,8 @@ export class Calendar extends Component {
|
|
|
999
1010
|
event: cloneCalendarEvent(item),
|
|
1000
1011
|
action,
|
|
1001
1012
|
control,
|
|
1013
|
+
timeOffsetMinutes: offset?.minutes ?? 0,
|
|
1014
|
+
dayOffset: offset?.days ?? 0,
|
|
1002
1015
|
moved: false
|
|
1003
1016
|
};
|
|
1004
1017
|
return;
|
|
@@ -1007,7 +1020,8 @@ export class Calendar extends Component {
|
|
|
1007
1020
|
if (!slot || !this.options.selectable) return;
|
|
1008
1021
|
const date = this._dateFromTarget(slot);
|
|
1009
1022
|
if (!date) return;
|
|
1010
|
-
|
|
1023
|
+
event.preventDefault();
|
|
1024
|
+
this._capturePointer(event.pointerId);
|
|
1011
1025
|
this._pointer = {
|
|
1012
1026
|
type: 'selection',
|
|
1013
1027
|
pointerId: event.pointerId,
|
|
@@ -1041,7 +1055,13 @@ export class Calendar extends Component {
|
|
|
1041
1055
|
const wrapper = pointer.control.closest('.zx-calendar__event');
|
|
1042
1056
|
if (wrapper) {
|
|
1043
1057
|
wrapper.dataset.state = 'dragging';
|
|
1044
|
-
|
|
1058
|
+
if (pointer.action === 'resize') {
|
|
1059
|
+
wrapper.style.transform = '';
|
|
1060
|
+
const height = this._timedPreviewHeight(proposed);
|
|
1061
|
+
if (height) wrapper.style.blockSize = height;
|
|
1062
|
+
} else {
|
|
1063
|
+
wrapper.style.transform = `translate(${event.clientX - pointer.startX}px, ${event.clientY - pointer.startY}px)`;
|
|
1064
|
+
}
|
|
1045
1065
|
wrapper.dataset.preview = this._eventTime(proposed, proposed.start);
|
|
1046
1066
|
}
|
|
1047
1067
|
this._announce(this.msg('editPreview', proposed.title,
|
|
@@ -1053,6 +1073,7 @@ export class Calendar extends Component {
|
|
|
1053
1073
|
const pointer = this._pointer;
|
|
1054
1074
|
if (!pointer || pointer.pointerId !== event.pointerId) return;
|
|
1055
1075
|
this._pointer = null;
|
|
1076
|
+
this._releasePointerCapture(pointer.pointerId);
|
|
1056
1077
|
this._clearSelectionPreview();
|
|
1057
1078
|
if (!pointer.moved) return;
|
|
1058
1079
|
event.preventDefault();
|
|
@@ -1071,6 +1092,7 @@ export class Calendar extends Component {
|
|
|
1071
1092
|
_cancelPointer(event) {
|
|
1072
1093
|
if (!this._pointer || this._pointer.pointerId !== event.pointerId) return;
|
|
1073
1094
|
const control = this._pointer.control;
|
|
1095
|
+
this._releasePointerCapture(this._pointer.pointerId);
|
|
1074
1096
|
this._pointer = null;
|
|
1075
1097
|
this._clearSelectionPreview();
|
|
1076
1098
|
control.closest?.('.zx-calendar__event')?.removeAttribute('data-state');
|
|
@@ -1079,13 +1101,14 @@ export class Calendar extends Component {
|
|
|
1079
1101
|
|
|
1080
1102
|
/** @param {CalendarEvent} event @param {'move'|'resize'} action @param {number} x @param {number} y @returns {CalendarEvent|null} */
|
|
1081
1103
|
_proposalAtPoint(event, action, x, y) {
|
|
1082
|
-
const
|
|
1104
|
+
const resizeTimed = action === 'resize' && !calendarEventSpansDays(event);
|
|
1105
|
+
const timed = this._timeAtPoint(x, y, resizeTimed ? event.start : null);
|
|
1083
1106
|
if (timed && !calendarEventSpansDays(event)) {
|
|
1084
1107
|
if (action === 'move') {
|
|
1085
1108
|
const duration = event.end.getTime() - event.start.getTime();
|
|
1086
1109
|
const next = cloneCalendarEvent(event);
|
|
1087
|
-
next.start = timed;
|
|
1088
|
-
next.end = new Date(
|
|
1110
|
+
next.start = addMinutes(timed, -Number(this._pointer?.timeOffsetMinutes ?? 0));
|
|
1111
|
+
next.end = new Date(next.start.getTime() + duration);
|
|
1089
1112
|
return next;
|
|
1090
1113
|
}
|
|
1091
1114
|
const next = cloneCalendarEvent(event);
|
|
@@ -1094,7 +1117,8 @@ export class Calendar extends Component {
|
|
|
1094
1117
|
}
|
|
1095
1118
|
const day = this._dayAtPoint(x, y);
|
|
1096
1119
|
if (!day) return null;
|
|
1097
|
-
const
|
|
1120
|
+
const targetDay = addCalendarDays(day, -Number(this._pointer?.dayOffset ?? 0));
|
|
1121
|
+
const difference = calendarDayDifference(startOfCalendarDay(event.start), targetDay);
|
|
1098
1122
|
if (action === 'move') return shiftCalendarEvent(event, { days: difference, minutes: 0 });
|
|
1099
1123
|
const next = cloneCalendarEvent(event);
|
|
1100
1124
|
const endDay = addCalendarDays(day, 1);
|
|
@@ -1102,15 +1126,23 @@ export class Calendar extends Component {
|
|
|
1102
1126
|
return next.end > next.start ? next : null;
|
|
1103
1127
|
}
|
|
1104
1128
|
|
|
1105
|
-
/** @param {number} x @param {number} y @returns {Date|null} */
|
|
1106
|
-
_timeAtPoint(x, y) {
|
|
1129
|
+
/** @param {number} x @param {number} y @param {Date|null} [fixedDay=null] @returns {Date|null} */
|
|
1130
|
+
_timeAtPoint(x, y, fixedDay = null) {
|
|
1107
1131
|
const slots = this.refs.body.querySelector('.zx-calendar__slots');
|
|
1108
1132
|
if (!slots) return null;
|
|
1109
1133
|
const rect = slots.getBoundingClientRect();
|
|
1110
|
-
|
|
1134
|
+
const scroller = slots.closest('.zx-calendar__time-scroller');
|
|
1135
|
+
const viewport = scroller?.getBoundingClientRect();
|
|
1136
|
+
const left = viewport ? Math.max(rect.left, viewport.left) : rect.left;
|
|
1137
|
+
const right = viewport ? Math.min(rect.right, viewport.right) : rect.right;
|
|
1138
|
+
const top = viewport ? Math.max(rect.top, viewport.top) : rect.top;
|
|
1139
|
+
const bottom = viewport ? Math.min(rect.bottom, viewport.bottom) : rect.bottom;
|
|
1140
|
+
if (x < left || x > right || y < top || y > bottom || rect.width <= 0 || rect.height <= 0) return null;
|
|
1111
1141
|
const columns = Number(slots.closest('[role="grid"]')?.getAttribute('aria-colcount')) || 1;
|
|
1112
1142
|
const rows = Number(slots.closest('[role="grid"]')?.getAttribute('aria-rowcount')) || 1;
|
|
1113
|
-
const
|
|
1143
|
+
const fixedColumn = fixedDay ? this._timeGridDayIndex(fixedDay) : -1;
|
|
1144
|
+
const column = fixedColumn >= 0 ? fixedColumn
|
|
1145
|
+
: Math.max(0, Math.min(columns - 1, Math.floor((x - rect.left) / rect.width * columns)));
|
|
1114
1146
|
const row = Math.max(0, Math.min(rows - 1, Math.floor((y - rect.top) / rect.height * rows)));
|
|
1115
1147
|
const target = slots.querySelector(`[data-row-index="${row}"][data-day-index="${column}"]`);
|
|
1116
1148
|
return target ? this._dateFromTarget(target) : null;
|
|
@@ -1118,11 +1150,6 @@ export class Calendar extends Component {
|
|
|
1118
1150
|
|
|
1119
1151
|
/** @param {number} x @param {number} y @returns {Date|null} */
|
|
1120
1152
|
_dayAtPoint(x, y) {
|
|
1121
|
-
const elements = typeof document.elementsFromPoint === 'function' ? document.elementsFromPoint(x, y) : [];
|
|
1122
|
-
for (const element of elements) {
|
|
1123
|
-
const date = element instanceof HTMLElement ? element.dataset.date : null;
|
|
1124
|
-
if (date) return parseDayKey(date);
|
|
1125
|
-
}
|
|
1126
1153
|
const allDay = this.refs.body.querySelector('.zx-calendar__all-day-grid');
|
|
1127
1154
|
if (allDay) {
|
|
1128
1155
|
const rect = allDay.getBoundingClientRect();
|
|
@@ -1132,9 +1159,79 @@ export class Calendar extends Component {
|
|
|
1132
1159
|
return parseDayKey(cells[index]?.dataset.date);
|
|
1133
1160
|
}
|
|
1134
1161
|
}
|
|
1162
|
+
for (const monthEvents of this.refs.body.querySelectorAll('.zx-calendar__month-events')) {
|
|
1163
|
+
const rect = monthEvents.getBoundingClientRect();
|
|
1164
|
+
if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom || rect.width <= 0) continue;
|
|
1165
|
+
const start = parseDayKey(monthEvents.closest('.zx-calendar__month-week')?.dataset.weekStart);
|
|
1166
|
+
if (!start) continue;
|
|
1167
|
+
const index = Math.max(0, Math.min(6, Math.floor((x - rect.left) / rect.width * 7)));
|
|
1168
|
+
return addCalendarDays(start, index);
|
|
1169
|
+
}
|
|
1170
|
+
for (const agendaDay of this.refs.body.querySelectorAll('.zx-calendar__agenda-day')) {
|
|
1171
|
+
const rect = agendaDay.getBoundingClientRect();
|
|
1172
|
+
if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) continue;
|
|
1173
|
+
return parseDayKey(agendaDay.dataset.date);
|
|
1174
|
+
}
|
|
1175
|
+
const elements = typeof document.elementsFromPoint === 'function' ? document.elementsFromPoint(x, y) : [];
|
|
1176
|
+
for (const element of elements) {
|
|
1177
|
+
const dateTarget = element instanceof HTMLElement ? element.closest('[data-date]') : null;
|
|
1178
|
+
const date = dateTarget instanceof HTMLElement ? dateTarget.dataset.date : null;
|
|
1179
|
+
if (date) return parseDayKey(date);
|
|
1180
|
+
}
|
|
1135
1181
|
return null;
|
|
1136
1182
|
}
|
|
1137
1183
|
|
|
1184
|
+
/** @param {CalendarEvent} event @param {number} x @param {number} y @returns {{minutes: number, days: number}} */
|
|
1185
|
+
_dragOffsetForEvent(event, x, y) {
|
|
1186
|
+
if (!calendarEventSpansDays(event)) {
|
|
1187
|
+
const timed = this._timeAtPoint(x, y);
|
|
1188
|
+
if (timed) {
|
|
1189
|
+
const slot = normalizeSlot(this.options.slotDuration);
|
|
1190
|
+
const duration = Math.max(0, Math.round((event.end.getTime() - event.start.getTime()) / 60_000));
|
|
1191
|
+
const maxOffset = Math.max(0, duration - slot);
|
|
1192
|
+
const minutes = Math.round((timed.getTime() - event.start.getTime()) / 60_000);
|
|
1193
|
+
return { minutes: Math.max(0, Math.min(maxOffset, minutes)), days: 0 };
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
const day = this._dayAtPoint(x, y);
|
|
1197
|
+
const days = day ? calendarDayDifference(startOfCalendarDay(event.start), day) : 0;
|
|
1198
|
+
return { minutes: 0, days: Math.max(0, days) };
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
/** @param {Date} day @returns {number} */
|
|
1202
|
+
_timeGridDayIndex(day) {
|
|
1203
|
+
const key = calendarDayKey(day);
|
|
1204
|
+
const headings = [...this.refs.body.querySelectorAll('.zx-calendar__day-heading')];
|
|
1205
|
+
return headings.findIndex((heading) => heading.dataset.date === key);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
/** @param {CalendarEvent} event @returns {string|null} */
|
|
1209
|
+
_timedPreviewHeight(event) {
|
|
1210
|
+
const grid = this.refs.body.querySelector('.zx-calendar__time-grid');
|
|
1211
|
+
if (!grid || calendarEventSpansDays(event)) return null;
|
|
1212
|
+
const min = normalizeMinute(this.options.slotMinTime, 0);
|
|
1213
|
+
const max = normalizeMinute(this.options.slotMaxTime, 1440, true);
|
|
1214
|
+
const slot = normalizeSlot(this.options.slotDuration);
|
|
1215
|
+
const start = event.start.getHours() * 60 + event.start.getMinutes();
|
|
1216
|
+
const end = event.end.getHours() * 60 + event.end.getMinutes();
|
|
1217
|
+
const minutes = Math.max(slot, Math.min(max, end) - Math.max(min, start));
|
|
1218
|
+
return `${(minutes / (max - min)) * 100}%`;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
/** @param {number} pointerId @returns {void} */
|
|
1222
|
+
_capturePointer(pointerId) {
|
|
1223
|
+
try {
|
|
1224
|
+
this.el.setPointerCapture?.(pointerId);
|
|
1225
|
+
} catch {
|
|
1226
|
+
// Synthetic PointerEvents used by smoke tests do not create an active pointer capture target.
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
/** @param {number} pointerId @returns {void} */
|
|
1231
|
+
_releasePointerCapture(pointerId) {
|
|
1232
|
+
if (this.el.hasPointerCapture?.(pointerId)) this.el.releasePointerCapture?.(pointerId);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1138
1235
|
/** @param {Date} start @param {Date} end @returns {void} */
|
|
1139
1236
|
_previewSelection(start, end) {
|
|
1140
1237
|
const low = Math.min(start.getTime(), end.getTime());
|
|
@@ -1240,13 +1337,9 @@ export class Calendar extends Component {
|
|
|
1240
1337
|
|
|
1241
1338
|
/** @param {number} direction @returns {this} */
|
|
1242
1339
|
_page(direction) {
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
else if (this._view === 'week') date.setDate(date.getDate() + direction * 7);
|
|
1247
|
-
else if (this._view === 'month') date.setMonth(date.getMonth() + direction);
|
|
1248
|
-
else date.setFullYear(date.getFullYear() + direction);
|
|
1249
|
-
return this.setDate(date);
|
|
1340
|
+
return this.setDate(calendarPageDate(this._view, this._date, direction, {
|
|
1341
|
+
agendaDays: this.options.agendaDays
|
|
1342
|
+
}));
|
|
1250
1343
|
}
|
|
1251
1344
|
|
|
1252
1345
|
/** @param {{start: Date, end: Date}} range @returns {string} */
|