@kne/react-calendar 1.0.5 → 1.1.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/README.md +210 -49
- package/dist/en-US.d.ts +6 -3
- package/dist/index.css +919 -100
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +48 -4
- package/dist/index.js +2387 -831
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +2214 -681
- package/dist/index.modern.js.map +1 -1
- package/dist/locale/en-US.js +7 -4
- package/dist/locale/en-US.js.map +1 -1
- package/dist/locale/en-US.modern.js +7 -4
- package/dist/locale/en-US.modern.js.map +1 -1
- package/dist/locale/zh-CN.js +7 -4
- package/dist/locale/zh-CN.js.map +1 -1
- package/dist/locale/zh-CN.modern.js +7 -4
- package/dist/locale/zh-CN.modern.js.map +1 -1
- package/dist/zh-CN.d.ts +6 -3
- package/package.json +2 -2
package/dist/index.modern.js
CHANGED
|
@@ -2,10 +2,11 @@ import { useState, useLayoutEffect, useMemo, useEffect, useCallback, useRef } fr
|
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
3
|
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
|
4
4
|
import useControlValue from '@kne/use-control-value';
|
|
5
|
-
import { Popover, Space, Button, Badge, List, Tag, Card, Spin, Empty, Tooltip, Calendar } from 'antd';
|
|
5
|
+
import { Popover, Space, Button, Badge, List, Tag, Card, Spin, Empty, Tooltip, Alert, Drawer, Calendar } from 'antd';
|
|
6
6
|
import { createWithIntlProvider, useIntl } from '@kne/react-intl';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
import FormInfo, { Input, TextArea, Form, SubmitButton } from '@kne/form-info';
|
|
9
|
+
import { useIsMobile } from '@kne/responsive-utils';
|
|
9
10
|
|
|
10
11
|
function _extends() {
|
|
11
12
|
return _extends = Object.assign ? Object.assign.bind() : function (n) {
|
|
@@ -29,16 +30,19 @@ const locale$1 = {
|
|
|
29
30
|
'PanelTitle.dateLabel': '{year}年{monthName}{day}日',
|
|
30
31
|
'PanelTitle.eventCount': '共 {count} 个日程',
|
|
31
32
|
'ViewPanel.empty': '暂无日程',
|
|
32
|
-
'SchedulePanel.selectFreeSlot': '
|
|
33
|
-
'SchedulePanel.newSchedule': '新增日程:{range}',
|
|
33
|
+
'SchedulePanel.selectFreeSlot': '点击或拖拽空白处选择时间',
|
|
34
34
|
'SchedulePanel.cancel': '取消',
|
|
35
35
|
'SchedulePanel.save': '保存',
|
|
36
|
-
'
|
|
37
|
-
'
|
|
36
|
+
'SchedulePanel.newScheduleTitle': '新增日程',
|
|
37
|
+
'SchedulePanel.conflict': '与 {count} 个日程时间冲突',
|
|
38
38
|
'DefaultFormInner.title': '标题',
|
|
39
39
|
'DefaultFormInner.titlePlaceholder': '请输入日程标题',
|
|
40
40
|
'DefaultFormInner.detail': '详情',
|
|
41
41
|
'DefaultFormInner.detailPlaceholder': '请输入日程详情',
|
|
42
|
+
'TimeWindow.hours': '{count} 小时',
|
|
43
|
+
'TimeWindow.minutes': '{count} 分钟',
|
|
44
|
+
'TimeWindow.editable': '允许编辑',
|
|
45
|
+
'TimeWindow.locked': '已锁定',
|
|
42
46
|
'Calendar.month.1': '1月',
|
|
43
47
|
'Calendar.month.2': '2月',
|
|
44
48
|
'Calendar.month.3': '3月',
|
|
@@ -65,16 +69,19 @@ const locale = {
|
|
|
65
69
|
'PanelTitle.dateLabel': '{monthName} {day}, {year}',
|
|
66
70
|
'PanelTitle.eventCount': '{count} events',
|
|
67
71
|
'ViewPanel.empty': 'No events',
|
|
68
|
-
'SchedulePanel.selectFreeSlot': '
|
|
69
|
-
'SchedulePanel.newSchedule': 'New event: {range}',
|
|
72
|
+
'SchedulePanel.selectFreeSlot': 'Click or drag on free time',
|
|
70
73
|
'SchedulePanel.cancel': 'Cancel',
|
|
71
74
|
'SchedulePanel.save': 'Save',
|
|
72
|
-
'
|
|
73
|
-
'
|
|
75
|
+
'SchedulePanel.newScheduleTitle': 'New event',
|
|
76
|
+
'SchedulePanel.conflict': 'Overlaps {count} event(s)',
|
|
74
77
|
'DefaultFormInner.title': 'Title',
|
|
75
78
|
'DefaultFormInner.titlePlaceholder': 'Enter event title',
|
|
76
79
|
'DefaultFormInner.detail': 'Details',
|
|
77
80
|
'DefaultFormInner.detailPlaceholder': 'Enter event details',
|
|
81
|
+
'TimeWindow.hours': '{count} h',
|
|
82
|
+
'TimeWindow.minutes': '{count} min',
|
|
83
|
+
'TimeWindow.editable': 'Editable',
|
|
84
|
+
'TimeWindow.locked': 'Locked',
|
|
78
85
|
'Calendar.month.1': 'Jan',
|
|
79
86
|
'Calendar.month.2': 'Feb',
|
|
80
87
|
'Calendar.month.3': 'Mar',
|
|
@@ -382,13 +389,13 @@ const getMonthPickerItemLabel = (formatMessage, value, monthIndex) => formatMess
|
|
|
382
389
|
monthName: getMonthName(formatMessage, monthIndex)
|
|
383
390
|
});
|
|
384
391
|
|
|
385
|
-
var style$
|
|
392
|
+
var style$5 = {"calendar-header":"kne-react-calendar_X1TYG","calendar-title":"kne-react-calendar_FcQis","calendar-title-part":"kne-react-calendar_LcyYv","header-picker-popover":"kne-react-calendar_T4oWB","picker-panel":"kne-react-calendar_PMS-M","picker-panel-header":"kne-react-calendar_IXQKl","picker-panel-range":"kne-react-calendar_J-Pnh","picker-panel-nav":"kne-react-calendar_Wo74w","picker-panel-nav-icon":"kne-react-calendar_Rwzuj","picker-panel-grid":"kne-react-calendar_rxTOr","picker-cell":"kne-react-calendar_bHuh6","picker-cell-active":"kne-react-calendar_xOfrO","header-actions":"kne-react-calendar_OCJaK","header-today":"kne-react-calendar_SWVUG","header-nav":"kne-react-calendar_BOheC","header-nav-icon":"kne-react-calendar_8IEwS"};
|
|
386
393
|
|
|
387
394
|
const HeaderNavIcon = ({
|
|
388
395
|
direction,
|
|
389
396
|
className
|
|
390
397
|
}) => /*#__PURE__*/jsx("svg", {
|
|
391
|
-
className: className || style$
|
|
398
|
+
className: className || style$5['header-nav-icon'],
|
|
392
399
|
viewBox: "0 0 16 16",
|
|
393
400
|
"aria-hidden": true,
|
|
394
401
|
focusable: "false",
|
|
@@ -411,7 +418,7 @@ const PickerPanelNav = ({
|
|
|
411
418
|
children
|
|
412
419
|
}) => /*#__PURE__*/jsx("button", {
|
|
413
420
|
type: "button",
|
|
414
|
-
className: style$
|
|
421
|
+
className: style$5['picker-panel-nav'],
|
|
415
422
|
"aria-label": label,
|
|
416
423
|
onClick: onClick,
|
|
417
424
|
children: children
|
|
@@ -429,9 +436,9 @@ const YearPickerPanel = ({
|
|
|
429
436
|
length: YEAR_PAGE_SIZE
|
|
430
437
|
}, (_, index) => pageStart + index), [pageStart]);
|
|
431
438
|
return /*#__PURE__*/jsxs("div", {
|
|
432
|
-
className: style$
|
|
439
|
+
className: style$5['picker-panel'],
|
|
433
440
|
children: [/*#__PURE__*/jsxs("div", {
|
|
434
|
-
className: style$
|
|
441
|
+
className: style$5['picker-panel-header'],
|
|
435
442
|
children: [/*#__PURE__*/jsx(PickerPanelNav, {
|
|
436
443
|
label: formatMessage({
|
|
437
444
|
id: 'Header.prevYearPage'
|
|
@@ -439,10 +446,10 @@ const YearPickerPanel = ({
|
|
|
439
446
|
onClick: () => onPageStartChange(pageStart - YEAR_PAGE_SIZE),
|
|
440
447
|
children: /*#__PURE__*/jsx(HeaderNavIcon, {
|
|
441
448
|
direction: "prev",
|
|
442
|
-
className: style$
|
|
449
|
+
className: style$5['picker-panel-nav-icon']
|
|
443
450
|
})
|
|
444
451
|
}), /*#__PURE__*/jsxs("div", {
|
|
445
|
-
className: style$
|
|
452
|
+
className: style$5['picker-panel-range'],
|
|
446
453
|
children: [pageStart, "-", pageStart + YEAR_PAGE_SIZE - 1]
|
|
447
454
|
}), /*#__PURE__*/jsx(PickerPanelNav, {
|
|
448
455
|
label: formatMessage({
|
|
@@ -451,15 +458,15 @@ const YearPickerPanel = ({
|
|
|
451
458
|
onClick: () => onPageStartChange(pageStart + YEAR_PAGE_SIZE),
|
|
452
459
|
children: /*#__PURE__*/jsx(HeaderNavIcon, {
|
|
453
460
|
direction: "next",
|
|
454
|
-
className: style$
|
|
461
|
+
className: style$5['picker-panel-nav-icon']
|
|
455
462
|
})
|
|
456
463
|
})]
|
|
457
464
|
}), /*#__PURE__*/jsx("div", {
|
|
458
|
-
className: style$
|
|
465
|
+
className: style$5['picker-panel-grid'],
|
|
459
466
|
children: years.map(year => /*#__PURE__*/jsx("button", {
|
|
460
467
|
type: "button",
|
|
461
|
-
className: classNames(style$
|
|
462
|
-
[style$
|
|
468
|
+
className: classNames(style$5['picker-cell'], {
|
|
469
|
+
[style$5['picker-cell-active']]: year === value.year()
|
|
463
470
|
}),
|
|
464
471
|
onClick: () => onSelect(year),
|
|
465
472
|
children: year
|
|
@@ -479,13 +486,13 @@ const MonthPickerPanel = ({
|
|
|
479
486
|
length: 12
|
|
480
487
|
}, (_, index) => index), []);
|
|
481
488
|
return /*#__PURE__*/jsx("div", {
|
|
482
|
-
className: style$
|
|
489
|
+
className: style$5['picker-panel'],
|
|
483
490
|
children: /*#__PURE__*/jsx("div", {
|
|
484
|
-
className: style$
|
|
491
|
+
className: style$5['picker-panel-grid'],
|
|
485
492
|
children: months.map(month => /*#__PURE__*/jsx("button", {
|
|
486
493
|
type: "button",
|
|
487
|
-
className: classNames(style$
|
|
488
|
-
[style$
|
|
494
|
+
className: classNames(style$5['picker-cell'], {
|
|
495
|
+
[style$5['picker-cell-active']]: month === value.month()
|
|
489
496
|
}),
|
|
490
497
|
onClick: () => onSelect(month),
|
|
491
498
|
children: getMonthPickerItemLabel(formatMessage, value, month)
|
|
@@ -500,7 +507,7 @@ const HeaderNavButton = ({
|
|
|
500
507
|
children
|
|
501
508
|
}) => /*#__PURE__*/jsx("button", {
|
|
502
509
|
type: "button",
|
|
503
|
-
className: style$
|
|
510
|
+
className: style$5['header-nav'],
|
|
504
511
|
"aria-label": label,
|
|
505
512
|
onClick: onClick,
|
|
506
513
|
children: children
|
|
@@ -543,9 +550,9 @@ const Header = ({
|
|
|
543
550
|
setMonthOpen(false);
|
|
544
551
|
};
|
|
545
552
|
return /*#__PURE__*/jsxs("div", {
|
|
546
|
-
className: style$
|
|
553
|
+
className: style$5['calendar-header'],
|
|
547
554
|
children: [/*#__PURE__*/jsxs("div", {
|
|
548
|
-
className: style$
|
|
555
|
+
className: style$5['calendar-title'],
|
|
549
556
|
children: [/*#__PURE__*/jsx(Popover, {
|
|
550
557
|
open: yearOpen,
|
|
551
558
|
onOpenChange: open => {
|
|
@@ -556,7 +563,7 @@ const Header = ({
|
|
|
556
563
|
},
|
|
557
564
|
trigger: "click",
|
|
558
565
|
placement: "bottomLeft",
|
|
559
|
-
rootClassName: style$
|
|
566
|
+
rootClassName: style$5['header-picker-popover'],
|
|
560
567
|
content: /*#__PURE__*/jsx(YearPickerPanel, {
|
|
561
568
|
value: date,
|
|
562
569
|
pageStart: yearPageStart,
|
|
@@ -565,7 +572,7 @@ const Header = ({
|
|
|
565
572
|
}),
|
|
566
573
|
children: /*#__PURE__*/jsx("button", {
|
|
567
574
|
type: "button",
|
|
568
|
-
className: style$
|
|
575
|
+
className: style$5['calendar-title-part'],
|
|
569
576
|
children: yearText
|
|
570
577
|
})
|
|
571
578
|
}), /*#__PURE__*/jsx(Popover, {
|
|
@@ -578,22 +585,22 @@ const Header = ({
|
|
|
578
585
|
},
|
|
579
586
|
trigger: "click",
|
|
580
587
|
placement: "bottomLeft",
|
|
581
|
-
rootClassName: style$
|
|
588
|
+
rootClassName: style$5['header-picker-popover'],
|
|
582
589
|
content: /*#__PURE__*/jsx(MonthPickerPanel, {
|
|
583
590
|
value: date,
|
|
584
591
|
onSelect: handleMonthSelect
|
|
585
592
|
}),
|
|
586
593
|
children: /*#__PURE__*/jsx("button", {
|
|
587
594
|
type: "button",
|
|
588
|
-
className: style$
|
|
595
|
+
className: style$5['calendar-title-part'],
|
|
589
596
|
children: monthText
|
|
590
597
|
})
|
|
591
598
|
})]
|
|
592
599
|
}), /*#__PURE__*/jsxs(Space, {
|
|
593
600
|
size: 4,
|
|
594
|
-
className: style$
|
|
601
|
+
className: style$5['header-actions'],
|
|
595
602
|
children: [showTodayButton ? /*#__PURE__*/jsx(Button, {
|
|
596
|
-
className: style$
|
|
603
|
+
className: style$5['header-today'],
|
|
597
604
|
type: "text",
|
|
598
605
|
size: "small",
|
|
599
606
|
onClick: onSelectToday,
|
|
@@ -660,7 +667,7 @@ const useStackedLayout = (ref, stackAt) => {
|
|
|
660
667
|
return stacked;
|
|
661
668
|
};
|
|
662
669
|
|
|
663
|
-
var style$
|
|
670
|
+
var style$4 = {"date-cell":"kne-react-calendar_-a9AL","date-number":"kne-react-calendar_9s0kZ","date-has-events":"kne-react-calendar_-Bh5f","date-selected":"kne-react-calendar_Lpltl","date-muted":"kne-react-calendar_1xgRD","date-disabled":"kne-react-calendar_YTv16"};
|
|
664
671
|
|
|
665
672
|
const DateCell = ({
|
|
666
673
|
date,
|
|
@@ -681,14 +688,14 @@ const DateCell = ({
|
|
|
681
688
|
return customCell;
|
|
682
689
|
}
|
|
683
690
|
return /*#__PURE__*/jsxs("div", {
|
|
684
|
-
className: classNames(style$
|
|
685
|
-
[style$
|
|
686
|
-
[style$
|
|
687
|
-
[style$
|
|
688
|
-
[style$
|
|
691
|
+
className: classNames(style$4['date-cell'], {
|
|
692
|
+
[style$4['date-selected']]: selected,
|
|
693
|
+
[style$4['date-muted']]: !currentMonth && !selected,
|
|
694
|
+
[style$4['date-disabled']]: disabled && !selected,
|
|
695
|
+
[style$4['date-has-events']]: count > 0 && !selected
|
|
689
696
|
}),
|
|
690
697
|
children: [/*#__PURE__*/jsx("div", {
|
|
691
|
-
className: style$
|
|
698
|
+
className: style$4['date-number'],
|
|
692
699
|
children: date.date()
|
|
693
700
|
}), count ? /*#__PURE__*/jsx(Badge, {
|
|
694
701
|
count: count > badgeMaxCount ? `${badgeMaxCount}+` : count,
|
|
@@ -714,7 +721,7 @@ const getEventTagLabel = event => {
|
|
|
714
721
|
return null;
|
|
715
722
|
};
|
|
716
723
|
|
|
717
|
-
var style$
|
|
724
|
+
var style$3 = {"event-list-item":"kne-react-calendar_cGTpY","event-item":"kne-react-calendar_3gmHY","event-item-head":"kne-react-calendar_fa3Mn","event-item-tag":"kne-react-calendar_yALGf","event-item-time":"kne-react-calendar_1L-dA","event-item-title":"kne-react-calendar_ISKyA","event-item-detail":"kne-react-calendar_HzTU2","event-item-clickable":"kne-react-calendar_iiYOC","event-item-disabled":"kne-react-calendar_3sXqE"};
|
|
718
725
|
|
|
719
726
|
const EventItem = ({
|
|
720
727
|
event,
|
|
@@ -746,11 +753,11 @@ const EventItem = ({
|
|
|
746
753
|
};
|
|
747
754
|
const tagLabel = getEventTagLabel(event);
|
|
748
755
|
return /*#__PURE__*/jsx(List.Item, {
|
|
749
|
-
className: style$
|
|
756
|
+
className: style$3['event-list-item'],
|
|
750
757
|
children: /*#__PURE__*/jsxs("div", {
|
|
751
|
-
className: classNames(style$
|
|
752
|
-
[style$
|
|
753
|
-
[style$
|
|
758
|
+
className: classNames(style$3['event-item'], {
|
|
759
|
+
[style$3['event-item-clickable']]: clickable,
|
|
760
|
+
[style$3['event-item-disabled']]: event.disabled
|
|
754
761
|
}),
|
|
755
762
|
role: clickable ? 'button' : undefined,
|
|
756
763
|
tabIndex: clickable ? 0 : undefined,
|
|
@@ -762,27 +769,27 @@ const EventItem = ({
|
|
|
762
769
|
}
|
|
763
770
|
} : undefined,
|
|
764
771
|
children: [/*#__PURE__*/jsxs("div", {
|
|
765
|
-
className: style$
|
|
772
|
+
className: style$3['event-item-head'],
|
|
766
773
|
children: [/*#__PURE__*/jsx("div", {
|
|
767
|
-
className: style$
|
|
774
|
+
className: style$3['event-item-title'],
|
|
768
775
|
children: event.title
|
|
769
776
|
}), tagLabel ? /*#__PURE__*/jsx(Tag, {
|
|
770
|
-
className: style$
|
|
777
|
+
className: style$3['event-item-tag'],
|
|
771
778
|
color: event.color,
|
|
772
779
|
children: tagLabel
|
|
773
780
|
}) : null]
|
|
774
781
|
}), /*#__PURE__*/jsx("div", {
|
|
775
|
-
className: style$
|
|
782
|
+
className: style$3['event-item-time'],
|
|
776
783
|
children: formatRange(event.start, event.end)
|
|
777
784
|
}), event.detail ? /*#__PURE__*/jsx("div", {
|
|
778
|
-
className: style$
|
|
785
|
+
className: style$3['event-item-detail'],
|
|
779
786
|
children: event.detail
|
|
780
787
|
}) : null]
|
|
781
788
|
})
|
|
782
789
|
});
|
|
783
790
|
};
|
|
784
791
|
|
|
785
|
-
var panelStyle = {"panel-card":"kne-react-calendar_uQsUE","panel-scroll-body":"kne-react-calendar_ujAl5","panel-scroll-content":"kne-react-calendar_t2e0j","panel-spin":"kne-react-calendar_tLux2","panel-spin-container":"kne-react-calendar_iW4mn","panel-title":"kne-react-calendar_C6NB3","panel-scroll-shadow":"kne-react-calendar_3ecxf","panel-scroll-shadow-visible":"kne-react-calendar_TvxhV","detail-card":"kne-react-calendar_3sEou"};
|
|
792
|
+
var panelStyle = {"panel-card":"kne-react-calendar_uQsUE","panel-scroll-body":"kne-react-calendar_ujAl5","panel-scroll-content":"kne-react-calendar_t2e0j","panel-spin":"kne-react-calendar_tLux2","panel-spin-container":"kne-react-calendar_iW4mn","panel-card-expanded":"kne-react-calendar_OzKbW","panel-scroll-body-expanded":"kne-react-calendar_jmktP","panel-title":"kne-react-calendar_C6NB3","panel-scroll-shadow":"kne-react-calendar_3ecxf","panel-scroll-shadow-visible":"kne-react-calendar_TvxhV","detail-card":"kne-react-calendar_3sEou"};
|
|
786
793
|
|
|
787
794
|
const PanelTitle = ({
|
|
788
795
|
date,
|
|
@@ -840,7 +847,8 @@ const ViewPanel = ({
|
|
|
840
847
|
loading,
|
|
841
848
|
renderEventItem,
|
|
842
849
|
renderPanelHeader,
|
|
843
|
-
onEventClick
|
|
850
|
+
onEventClick,
|
|
851
|
+
scrollable: _scrollable = true
|
|
844
852
|
}) => {
|
|
845
853
|
const {
|
|
846
854
|
formatMessage
|
|
@@ -862,7 +870,7 @@ const ViewPanel = ({
|
|
|
862
870
|
date: date,
|
|
863
871
|
count: sortedEvents.length
|
|
864
872
|
}),
|
|
865
|
-
className: panelStyle['panel-card'],
|
|
873
|
+
className: classNames(panelStyle['panel-card'], !_scrollable && panelStyle['panel-card-expanded']),
|
|
866
874
|
children: /*#__PURE__*/jsx(Spin, {
|
|
867
875
|
spinning: !!loading,
|
|
868
876
|
classNames: {
|
|
@@ -871,11 +879,11 @@ const ViewPanel = ({
|
|
|
871
879
|
},
|
|
872
880
|
children: /*#__PURE__*/jsxs("div", {
|
|
873
881
|
ref: scrollRef,
|
|
874
|
-
className: panelStyle['panel-scroll-body'],
|
|
875
|
-
onScroll: onScroll,
|
|
876
|
-
children: [/*#__PURE__*/jsx(PanelScrollShadow, {
|
|
882
|
+
className: classNames(panelStyle['panel-scroll-body'], !_scrollable && panelStyle['panel-scroll-body-expanded']),
|
|
883
|
+
onScroll: _scrollable ? onScroll : undefined,
|
|
884
|
+
children: [_scrollable ? /*#__PURE__*/jsx(PanelScrollShadow, {
|
|
877
885
|
visible: scrolled
|
|
878
|
-
}), /*#__PURE__*/jsx("div", {
|
|
886
|
+
}) : null, /*#__PURE__*/jsx("div", {
|
|
879
887
|
className: panelStyle['panel-scroll-content'],
|
|
880
888
|
children: sortedEvents.length ? /*#__PURE__*/jsx(List, {
|
|
881
889
|
dataSource: sortedEvents,
|
|
@@ -897,456 +905,1443 @@ const ViewPanel = ({
|
|
|
897
905
|
});
|
|
898
906
|
};
|
|
899
907
|
|
|
900
|
-
const
|
|
901
|
-
const
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
const getSlotIndexFromPoint = (container, clientX, clientY) => {
|
|
905
|
-
const slotElements = container == null || container.querySelectorAll == null ? void 0 : container.querySelectorAll('[data-slot-index]');
|
|
906
|
-
if (!(slotElements != null && slotElements.length)) {
|
|
907
|
-
return null;
|
|
908
|
+
const MINUTE_MS$1 = 60 * 1000;
|
|
909
|
+
const toMs = value => {
|
|
910
|
+
if (value === null || typeof value === 'undefined') {
|
|
911
|
+
return NaN;
|
|
908
912
|
}
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
913
|
+
if (typeof value === 'number') {
|
|
914
|
+
return value;
|
|
915
|
+
}
|
|
916
|
+
const date = dayjs(value);
|
|
917
|
+
return date.isValid() ? date.valueOf() : NaN;
|
|
918
|
+
};
|
|
919
|
+
const snapToStep = (ms, {
|
|
920
|
+
originMs,
|
|
921
|
+
stepMs,
|
|
922
|
+
mode: _mode = 'round'
|
|
923
|
+
}) => {
|
|
924
|
+
const fn = _mode === 'floor' ? Math.floor : _mode === 'ceil' ? Math.ceil : Math.round;
|
|
925
|
+
return originMs + fn((ms - originMs) / stepMs) * stepMs;
|
|
926
|
+
};
|
|
927
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
928
|
+
|
|
929
|
+
/**
|
|
930
|
+
* Clip events to the day window and lay overlapping ones out side by side.
|
|
931
|
+
* Returns `{ event, startMs, endMs, column, columns }` sorted by start.
|
|
932
|
+
*/
|
|
933
|
+
const layoutTimelineEvents = (events, {
|
|
934
|
+
startMs,
|
|
935
|
+
endMs
|
|
936
|
+
}) => {
|
|
937
|
+
const items = (events || []).map(event => {
|
|
938
|
+
const s = Math.max(toMs(event.start), startMs);
|
|
939
|
+
const e = Math.min(toMs(event.end), endMs);
|
|
940
|
+
return {
|
|
941
|
+
event,
|
|
942
|
+
startMs: s,
|
|
943
|
+
endMs: e
|
|
944
|
+
};
|
|
945
|
+
}).filter(item => Number.isFinite(item.startMs) && Number.isFinite(item.endMs) && item.endMs > item.startMs).sort((a, b) => a.startMs - b.startMs || b.endMs - a.endMs);
|
|
946
|
+
let cluster = [];
|
|
947
|
+
let clusterEnd = -Infinity;
|
|
948
|
+
let columnEnds = [];
|
|
949
|
+
const flush = () => {
|
|
950
|
+
cluster.forEach(item => {
|
|
951
|
+
item.columns = columnEnds.length;
|
|
952
|
+
});
|
|
953
|
+
cluster = [];
|
|
954
|
+
columnEnds = [];
|
|
955
|
+
};
|
|
956
|
+
items.forEach(item => {
|
|
957
|
+
if (item.startMs >= clusterEnd) {
|
|
958
|
+
flush();
|
|
959
|
+
clusterEnd = -Infinity;
|
|
916
960
|
}
|
|
917
|
-
|
|
918
|
-
|
|
961
|
+
let column = columnEnds.findIndex(end => end <= item.startMs);
|
|
962
|
+
if (column === -1) {
|
|
963
|
+
column = columnEnds.length;
|
|
964
|
+
columnEnds.push(item.endMs);
|
|
965
|
+
} else {
|
|
966
|
+
columnEnds[column] = item.endMs;
|
|
919
967
|
}
|
|
920
|
-
|
|
921
|
-
|
|
968
|
+
item.column = column;
|
|
969
|
+
cluster.push(item);
|
|
970
|
+
clusterEnd = Math.max(clusterEnd, item.endMs);
|
|
971
|
+
});
|
|
972
|
+
flush();
|
|
973
|
+
return items;
|
|
974
|
+
};
|
|
975
|
+
|
|
976
|
+
/** Merge consecutive disabled slots (same reason) into ranges. */
|
|
977
|
+
const mergeDisabledSlots = slots => {
|
|
978
|
+
const ranges = [];
|
|
979
|
+
(slots || []).forEach(slot => {
|
|
980
|
+
if (slot.status !== 'disabled') {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
const startMs = toMs(slot.start);
|
|
984
|
+
const endMs = toMs(slot.end);
|
|
985
|
+
const last = ranges[ranges.length - 1];
|
|
986
|
+
if (last && last.endMs === startMs && last.reason === slot.disabledReason) {
|
|
987
|
+
last.endMs = endMs;
|
|
988
|
+
return;
|
|
989
|
+
}
|
|
990
|
+
ranges.push({
|
|
991
|
+
startMs,
|
|
992
|
+
endMs,
|
|
993
|
+
reason: slot.disabledReason
|
|
994
|
+
});
|
|
995
|
+
});
|
|
996
|
+
return ranges;
|
|
997
|
+
};
|
|
998
|
+
const toBlockers = ranges => (ranges || []).map(range => {
|
|
999
|
+
var _range$startMs, _range$endMs;
|
|
1000
|
+
return {
|
|
1001
|
+
startMs: toMs((_range$startMs = range.startMs) != null ? _range$startMs : range.start),
|
|
1002
|
+
endMs: toMs((_range$endMs = range.endMs) != null ? _range$endMs : range.end)
|
|
1003
|
+
};
|
|
1004
|
+
}).filter(range => Number.isFinite(range.startMs) && Number.isFinite(range.endMs) && range.endMs > range.startMs).sort((a, b) => a.startMs - b.startMs);
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* The free gap around `ms` between blockers, bounded by the day window.
|
|
1008
|
+
* Returns null when `ms` falls inside a blocker.
|
|
1009
|
+
*/
|
|
1010
|
+
const getFreeGap = (ms, blockers, {
|
|
1011
|
+
startMs,
|
|
1012
|
+
endMs
|
|
1013
|
+
}) => {
|
|
1014
|
+
let gapStart = startMs;
|
|
1015
|
+
let gapEnd = endMs;
|
|
1016
|
+
for (const blocker of blockers || []) {
|
|
1017
|
+
if (blocker.startMs <= ms && ms < blocker.endMs) {
|
|
1018
|
+
return null;
|
|
922
1019
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
if (
|
|
926
|
-
|
|
927
|
-
closestIndex = index;
|
|
1020
|
+
if (blocker.endMs <= ms) {
|
|
1021
|
+
gapStart = Math.max(gapStart, blocker.endMs);
|
|
1022
|
+
} else if (blocker.startMs > ms) {
|
|
1023
|
+
gapEnd = Math.min(gapEnd, blocker.startMs);
|
|
928
1024
|
}
|
|
929
1025
|
}
|
|
930
|
-
|
|
1026
|
+
if (ms < startMs || ms >= endMs || gapEnd <= gapStart) {
|
|
1027
|
+
return null;
|
|
1028
|
+
}
|
|
1029
|
+
return {
|
|
1030
|
+
startMs: gapStart,
|
|
1031
|
+
endMs: gapEnd
|
|
1032
|
+
};
|
|
931
1033
|
};
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1034
|
+
|
|
1035
|
+
/** Range created by pressing at `anchorMs` and dragging to `pointerMs`, kept inside `gap`. */
|
|
1036
|
+
const createSelectionRange = ({
|
|
1037
|
+
anchorMs,
|
|
1038
|
+
pointerMs,
|
|
1039
|
+
gap,
|
|
1040
|
+
originMs,
|
|
1041
|
+
stepMs
|
|
1042
|
+
}) => {
|
|
1043
|
+
const anchorStart = clamp(snapToStep(anchorMs, {
|
|
1044
|
+
originMs,
|
|
1045
|
+
stepMs,
|
|
1046
|
+
mode: 'floor'
|
|
1047
|
+
}), gap.startMs, gap.endMs);
|
|
1048
|
+
const anchorEnd = Math.min(anchorStart + stepMs, gap.endMs);
|
|
1049
|
+
const pointer = clamp(pointerMs, gap.startMs, gap.endMs);
|
|
1050
|
+
if (pointer >= anchorStart) {
|
|
1051
|
+
const end = clamp(snapToStep(pointer, {
|
|
1052
|
+
originMs,
|
|
1053
|
+
stepMs,
|
|
1054
|
+
mode: 'ceil'
|
|
1055
|
+
}), anchorEnd, gap.endMs);
|
|
1056
|
+
return {
|
|
1057
|
+
startMs: anchorStart,
|
|
1058
|
+
endMs: end
|
|
1059
|
+
};
|
|
936
1060
|
}
|
|
1061
|
+
const start = clamp(snapToStep(pointer, {
|
|
1062
|
+
originMs,
|
|
1063
|
+
stepMs,
|
|
1064
|
+
mode: 'floor'
|
|
1065
|
+
}), gap.startMs, anchorStart);
|
|
1066
|
+
return {
|
|
1067
|
+
startMs: start,
|
|
1068
|
+
endMs: anchorEnd
|
|
1069
|
+
};
|
|
937
1070
|
};
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1071
|
+
|
|
1072
|
+
/** Default range for a single click: one `unitMs` block that contains the pointer, fitted into `gap`. */
|
|
1073
|
+
const clickSelectionRange = ({
|
|
1074
|
+
pointerMs,
|
|
1075
|
+
gap,
|
|
1076
|
+
originMs,
|
|
1077
|
+
unitMs
|
|
1078
|
+
}) => {
|
|
1079
|
+
const duration = Math.min(unitMs, gap.endMs - gap.startMs);
|
|
1080
|
+
const start = clamp(snapToStep(pointerMs, {
|
|
1081
|
+
originMs,
|
|
1082
|
+
stepMs: unitMs,
|
|
1083
|
+
mode: 'floor'
|
|
1084
|
+
}), gap.startMs, gap.endMs - duration);
|
|
1085
|
+
return {
|
|
1086
|
+
startMs: start,
|
|
1087
|
+
endMs: start + duration
|
|
1088
|
+
};
|
|
1089
|
+
};
|
|
1090
|
+
const moveSelectionRange = ({
|
|
1091
|
+
range,
|
|
1092
|
+
deltaMs,
|
|
1093
|
+
gap,
|
|
1094
|
+
originMs,
|
|
1095
|
+
stepMs
|
|
1096
|
+
}) => {
|
|
1097
|
+
const duration = range.endMs - range.startMs;
|
|
1098
|
+
const start = clamp(snapToStep(range.startMs + deltaMs, {
|
|
1099
|
+
originMs,
|
|
1100
|
+
stepMs
|
|
1101
|
+
}), gap.startMs, gap.endMs - duration);
|
|
1102
|
+
return {
|
|
1103
|
+
startMs: start,
|
|
1104
|
+
endMs: start + duration
|
|
1105
|
+
};
|
|
1106
|
+
};
|
|
1107
|
+
const resizeSelectionRange = ({
|
|
1108
|
+
range,
|
|
1109
|
+
edge,
|
|
1110
|
+
pointerMs,
|
|
1111
|
+
gap,
|
|
1112
|
+
originMs,
|
|
1113
|
+
stepMs,
|
|
1114
|
+
minMs: _minMs = stepMs
|
|
945
1115
|
}) => {
|
|
946
|
-
|
|
1116
|
+
const snapped = snapToStep(pointerMs, {
|
|
1117
|
+
originMs,
|
|
1118
|
+
stepMs
|
|
1119
|
+
});
|
|
1120
|
+
if (edge === 'start') {
|
|
1121
|
+
const start = clamp(snapped, gap.startMs, range.endMs - _minMs);
|
|
1122
|
+
return {
|
|
1123
|
+
startMs: start,
|
|
1124
|
+
endMs: range.endMs
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
const end = clamp(snapped, range.startMs + _minMs, gap.endMs);
|
|
1128
|
+
return {
|
|
1129
|
+
startMs: range.startMs,
|
|
1130
|
+
endMs: end
|
|
1131
|
+
};
|
|
1132
|
+
};
|
|
1133
|
+
const findOverlappingEvents = (range, events) => {
|
|
1134
|
+
if (!range) {
|
|
1135
|
+
return [];
|
|
1136
|
+
}
|
|
1137
|
+
return (events || []).filter(event => toMs(event.start) < range.endMs && toMs(event.end) > range.startMs);
|
|
1138
|
+
};
|
|
1139
|
+
const findOverlappingSlots = (range, slots) => {
|
|
1140
|
+
if (!range) {
|
|
1141
|
+
return [];
|
|
1142
|
+
}
|
|
1143
|
+
return (slots || []).filter(slot => toMs(slot.start) < range.endMs && toMs(slot.end) > range.startMs);
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
dayjs.extend(customParseFormat);
|
|
1147
|
+
const DEFAULT_DURATION = 60;
|
|
1148
|
+
const DEFAULT_STEP = 1;
|
|
1149
|
+
const DEFAULT_DEBOUNCE_WAIT = 300;
|
|
1150
|
+
const DEFAULT_SEPARATOR = ' ~ ';
|
|
1151
|
+
const DEFAULT_FORMAT = ['YYYY-MM-DD HH:mm', 'HH:mm'];
|
|
1152
|
+
const DEFAULT_MIN_VISIBLE_SPAN = 360;
|
|
1153
|
+
const MINUTE_MS = 60 * 1000;
|
|
1154
|
+
const HOUR_MS = 60 * MINUTE_MS;
|
|
1155
|
+
const PARSE_FORMATS = ['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm', 'YYYY-MM-DD'];
|
|
1156
|
+
const parseDateLike = value => {
|
|
1157
|
+
if (value == null || value === '') {
|
|
1158
|
+
return null;
|
|
1159
|
+
}
|
|
1160
|
+
if (dayjs.isDayjs(value)) {
|
|
1161
|
+
return value.isValid() ? value : null;
|
|
1162
|
+
}
|
|
1163
|
+
const direct = dayjs(value);
|
|
1164
|
+
if (direct.isValid()) {
|
|
1165
|
+
return direct;
|
|
1166
|
+
}
|
|
1167
|
+
if (typeof value === 'string') {
|
|
1168
|
+
const parsed = dayjs(value, PARSE_FORMATS, true);
|
|
1169
|
+
return parsed.isValid() ? parsed : null;
|
|
1170
|
+
}
|
|
1171
|
+
return null;
|
|
1172
|
+
};
|
|
1173
|
+
const isEmptyBound = value => value == null || value === '';
|
|
1174
|
+
const defaultDayRange = now => {
|
|
1175
|
+
const day = (parseDateLike(now) || dayjs()).startOf('day');
|
|
1176
|
+
return [day, day.add(1, 'day')];
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1179
|
+
/**
|
|
1180
|
+
* `null` 或 `[null, null]` 表示不限制;单侧为空表示该侧不限制;`undefined` 为当天全天。
|
|
1181
|
+
*/
|
|
1182
|
+
const resolveRange = (range, now = dayjs()) => {
|
|
1183
|
+
if (range === null) {
|
|
1184
|
+
return [null, null];
|
|
1185
|
+
}
|
|
1186
|
+
if (!Array.isArray(range)) {
|
|
1187
|
+
return defaultDayRange(now);
|
|
1188
|
+
}
|
|
1189
|
+
const start = isEmptyBound(range[0]) ? null : parseDateLike(range[0]);
|
|
1190
|
+
const end = isEmptyBound(range[1]) ? null : parseDateLike(range[1]);
|
|
1191
|
+
const startInvalid = !isEmptyBound(range[0]) && !start;
|
|
1192
|
+
const endInvalid = !isEmptyBound(range[1]) && !end;
|
|
1193
|
+
if (startInvalid || endInvalid || start && end && !end.isAfter(start)) {
|
|
1194
|
+
return defaultDayRange(now);
|
|
1195
|
+
}
|
|
1196
|
+
return [start, end];
|
|
1197
|
+
};
|
|
1198
|
+
const resolveDuration = (duration, rangeStart, rangeEnd) => {
|
|
1199
|
+
const requested = Number(duration) > 0 ? Number(duration) : DEFAULT_DURATION;
|
|
1200
|
+
if (!rangeStart || !rangeEnd) {
|
|
1201
|
+
return requested;
|
|
1202
|
+
}
|
|
1203
|
+
const rangeMinutes = rangeEnd.diff(rangeStart, 'minute');
|
|
1204
|
+
if (rangeMinutes > 0 && requested > rangeMinutes) {
|
|
1205
|
+
return rangeMinutes;
|
|
1206
|
+
}
|
|
1207
|
+
return requested;
|
|
1208
|
+
};
|
|
1209
|
+
const resolveStep = step => Number(step) > 0 ? Number(step) : DEFAULT_STEP;
|
|
1210
|
+
const resolveVisibleSpan = ({
|
|
1211
|
+
visibleSpan,
|
|
1212
|
+
duration,
|
|
1213
|
+
baseDuration: _baseDuration = duration,
|
|
1214
|
+
rangeStart,
|
|
1215
|
+
rangeEnd
|
|
1216
|
+
}) => {
|
|
1217
|
+
const minimum = duration * 1.5;
|
|
1218
|
+
let span = Number(visibleSpan) > 0 ? Number(visibleSpan) : Math.max(_baseDuration * 6, DEFAULT_MIN_VISIBLE_SPAN);
|
|
1219
|
+
if (rangeStart && rangeEnd && !(Number(visibleSpan) > 0)) {
|
|
1220
|
+
span = Math.min(span, rangeEnd.diff(rangeStart, 'minute'));
|
|
1221
|
+
}
|
|
1222
|
+
return Math.max(span, minimum);
|
|
1223
|
+
};
|
|
1224
|
+
const snapMs = (ms, stepMs, mode = 'round') => {
|
|
1225
|
+
const anchor = dayjs(ms).startOf('day').valueOf();
|
|
1226
|
+
const units = (ms - anchor) / stepMs;
|
|
1227
|
+
const snapped = mode === 'floor' ? Math.floor(units) : mode === 'ceil' ? Math.ceil(units) : Math.round(units);
|
|
1228
|
+
return anchor + snapped * stepMs;
|
|
1229
|
+
};
|
|
1230
|
+
const clampWindowStart = (startInput, {
|
|
1231
|
+
rangeStart,
|
|
1232
|
+
rangeEnd,
|
|
1233
|
+
duration,
|
|
1234
|
+
step,
|
|
1235
|
+
snap: _snap = true,
|
|
1236
|
+
now
|
|
1237
|
+
}) => {
|
|
1238
|
+
const durationMs = duration * MINUTE_MS;
|
|
1239
|
+
const minMs = rangeStart ? rangeStart.valueOf() : -Infinity;
|
|
1240
|
+
const maxMs = rangeEnd ? rangeEnd.valueOf() - durationMs : Infinity;
|
|
1241
|
+
if (maxMs <= minMs) {
|
|
1242
|
+
return dayjs(minMs);
|
|
1243
|
+
}
|
|
1244
|
+
const fallback = rangeStart || parseDateLike(now) || dayjs();
|
|
1245
|
+
const start = parseDateLike(startInput) || fallback;
|
|
1246
|
+
const stepMs = step * MINUTE_MS;
|
|
1247
|
+
let target = _snap ? snapMs(start.valueOf(), stepMs) : start.valueOf();
|
|
1248
|
+
if (target > maxMs) {
|
|
1249
|
+
target = _snap ? snapMs(maxMs, stepMs, 'floor') : maxMs;
|
|
1250
|
+
}
|
|
1251
|
+
if (target < minMs) {
|
|
1252
|
+
target = _snap ? snapMs(minMs, stepMs, 'ceil') : minMs;
|
|
1253
|
+
}
|
|
1254
|
+
return dayjs(Math.min(Math.max(target, minMs), maxMs));
|
|
1255
|
+
};
|
|
1256
|
+
const createTimeWindow = ({
|
|
1257
|
+
start,
|
|
1258
|
+
duration,
|
|
1259
|
+
range,
|
|
1260
|
+
step,
|
|
1261
|
+
now,
|
|
1262
|
+
snap: _snap2 = true
|
|
1263
|
+
} = {}) => {
|
|
1264
|
+
const [rangeStart, rangeEnd] = resolveRange(range, now);
|
|
1265
|
+
const resolvedDuration = resolveDuration(duration, rangeStart, rangeEnd);
|
|
1266
|
+
const resolvedStep = resolveStep(step);
|
|
1267
|
+
const windowStart = clampWindowStart(start == null ? null : start, {
|
|
1268
|
+
rangeStart,
|
|
1269
|
+
rangeEnd,
|
|
1270
|
+
duration: resolvedDuration,
|
|
1271
|
+
step: resolvedStep,
|
|
1272
|
+
snap: _snap2,
|
|
1273
|
+
now
|
|
1274
|
+
});
|
|
1275
|
+
return {
|
|
1276
|
+
start: windowStart,
|
|
1277
|
+
end: windowStart.add(resolvedDuration, 'minute'),
|
|
1278
|
+
rangeStart,
|
|
1279
|
+
rangeEnd,
|
|
1280
|
+
duration: resolvedDuration,
|
|
1281
|
+
step: resolvedStep
|
|
1282
|
+
};
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* 拖动窗口一侧边界:另一侧保持不动,拖动侧按 step 吸附,并受最短 / 最长时长与范围约束。
|
|
1287
|
+
*/
|
|
1288
|
+
const resizeTimeWindow = ({
|
|
1289
|
+
edge,
|
|
1290
|
+
pointerMs,
|
|
1291
|
+
startMs,
|
|
1292
|
+
endMs,
|
|
1293
|
+
step,
|
|
1294
|
+
rangeStart,
|
|
1295
|
+
rangeEnd,
|
|
1296
|
+
minDuration,
|
|
1297
|
+
maxDuration
|
|
1298
|
+
}) => {
|
|
1299
|
+
const stepMs = resolveStep(step) * MINUTE_MS;
|
|
1300
|
+
const minMs = (Number(minDuration) > 0 ? Number(minDuration) : resolveStep(step)) * MINUTE_MS;
|
|
1301
|
+
const maxMs = Number(maxDuration) > 0 ? Number(maxDuration) * MINUTE_MS : Infinity;
|
|
1302
|
+
const rangeStartMs = rangeStart ? dayjs(rangeStart).valueOf() : -Infinity;
|
|
1303
|
+
const rangeEndMs = rangeEnd ? dayjs(rangeEnd).valueOf() : Infinity;
|
|
1304
|
+
if (edge === 'start') {
|
|
1305
|
+
let _next = snapMs(pointerMs, stepMs);
|
|
1306
|
+
_next = Math.min(_next, endMs - minMs);
|
|
1307
|
+
_next = Math.max(_next, endMs - maxMs, rangeStartMs);
|
|
1308
|
+
return {
|
|
1309
|
+
startMs: _next,
|
|
1310
|
+
duration: (endMs - _next) / MINUTE_MS
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
let next = snapMs(pointerMs, stepMs);
|
|
1314
|
+
next = Math.max(next, startMs + minMs);
|
|
1315
|
+
next = Math.min(next, startMs + maxMs, rangeEndMs);
|
|
1316
|
+
return {
|
|
1317
|
+
startMs,
|
|
1318
|
+
duration: (next - startMs) / MINUTE_MS
|
|
1319
|
+
};
|
|
1320
|
+
};
|
|
1321
|
+
const toWindowValue = window => [window.start.toDate(), window.end.toDate()];
|
|
1322
|
+
const getViewport = ({
|
|
1323
|
+
startMs,
|
|
1324
|
+
duration
|
|
1325
|
+
}, visibleSpanMinutes) => {
|
|
1326
|
+
const spanMs = visibleSpanMinutes * MINUTE_MS;
|
|
1327
|
+
const centerMs = startMs + duration * MINUTE_MS / 2;
|
|
1328
|
+
return {
|
|
1329
|
+
startMs: centerMs - spanMs / 2,
|
|
1330
|
+
endMs: centerMs + spanMs / 2,
|
|
1331
|
+
spanMs
|
|
1332
|
+
};
|
|
1333
|
+
};
|
|
1334
|
+
const wheelDeltaToPx = ({
|
|
1335
|
+
delta,
|
|
1336
|
+
deltaMode: _deltaMode = 0,
|
|
1337
|
+
trackWidth
|
|
1338
|
+
}) => {
|
|
1339
|
+
if (_deltaMode === 1) {
|
|
1340
|
+
return delta * 16;
|
|
1341
|
+
}
|
|
1342
|
+
if (_deltaMode === 2) {
|
|
1343
|
+
return delta * (trackWidth > 0 ? trackWidth : 1);
|
|
1344
|
+
}
|
|
1345
|
+
return delta;
|
|
1346
|
+
};
|
|
1347
|
+
|
|
1348
|
+
/**
|
|
1349
|
+
* 把滚轮像素累加为整步数:步数与刻度尺比例一致,单次离散滚动至少移动一步。
|
|
1350
|
+
*/
|
|
1351
|
+
const consumeWheelSteps = ({
|
|
1352
|
+
accumulatedPx,
|
|
1353
|
+
deltaPx,
|
|
1354
|
+
pxPerStep,
|
|
1355
|
+
discrete
|
|
1356
|
+
}) => {
|
|
1357
|
+
const sameDirection = accumulatedPx === 0 || Math.sign(accumulatedPx) === Math.sign(deltaPx);
|
|
1358
|
+
const total = (sameDirection ? accumulatedPx : 0) + deltaPx;
|
|
1359
|
+
const perStep = pxPerStep > 0 ? pxPerStep : 1;
|
|
1360
|
+
let steps = Math.trunc(total / perStep) || 0;
|
|
1361
|
+
let rest = total - steps * perStep;
|
|
1362
|
+
if (steps === 0 && discrete && deltaPx !== 0) {
|
|
1363
|
+
steps = Math.sign(deltaPx);
|
|
1364
|
+
rest = 0;
|
|
1365
|
+
}
|
|
1366
|
+
return {
|
|
1367
|
+
steps,
|
|
1368
|
+
accumulatedPx: rest
|
|
1369
|
+
};
|
|
1370
|
+
};
|
|
1371
|
+
const DEFAULT_INERTIA_TIME_CONSTANT = 325;
|
|
1372
|
+
const VELOCITY_WINDOW_MS = 120;
|
|
1373
|
+
|
|
1374
|
+
/**
|
|
1375
|
+
* `true` 为默认强度 1,数字为强度倍数,`false` / `0` 关闭。返回滑行衰减时间常数(ms),0 表示关闭。
|
|
1376
|
+
*/
|
|
1377
|
+
const resolveInertia = inertia => {
|
|
1378
|
+
if (inertia === false || inertia == null) {
|
|
1379
|
+
return inertia == null ? DEFAULT_INERTIA_TIME_CONSTANT : 0;
|
|
1380
|
+
}
|
|
1381
|
+
if (inertia === true) {
|
|
1382
|
+
return DEFAULT_INERTIA_TIME_CONSTANT;
|
|
1383
|
+
}
|
|
1384
|
+
const strength = Number(inertia);
|
|
1385
|
+
return strength > 0 ? DEFAULT_INERTIA_TIME_CONSTANT * strength : 0;
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
/**
|
|
1389
|
+
* samples: [{ t, ms }],按时间顺序;返回最近一段内每毫秒移动的时间量。
|
|
1390
|
+
*/
|
|
1391
|
+
const estimateVelocity = (samples, now) => {
|
|
1392
|
+
if (!samples || samples.length < 2) {
|
|
1393
|
+
return 0;
|
|
1394
|
+
}
|
|
1395
|
+
const latest = samples[samples.length - 1];
|
|
1396
|
+
const reference = now == null ? latest.t : now;
|
|
1397
|
+
if (reference - latest.t > VELOCITY_WINDOW_MS / 2) {
|
|
1398
|
+
return 0;
|
|
1399
|
+
}
|
|
1400
|
+
const recent = samples.filter(sample => latest.t - sample.t <= VELOCITY_WINDOW_MS);
|
|
1401
|
+
if (recent.length < 2) {
|
|
1402
|
+
return 0;
|
|
1403
|
+
}
|
|
1404
|
+
const first = recent[0];
|
|
1405
|
+
const elapsed = latest.t - first.t;
|
|
1406
|
+
return elapsed > 0 ? (latest.ms - first.ms) / elapsed : 0;
|
|
1407
|
+
};
|
|
1408
|
+
const timeAtClientX = ({
|
|
1409
|
+
clientX,
|
|
1410
|
+
rectLeft,
|
|
1411
|
+
rectWidth,
|
|
1412
|
+
rangeStartMs,
|
|
1413
|
+
rangeEndMs,
|
|
1414
|
+
grabOffsetMs: _grabOffsetMs = 0
|
|
1415
|
+
}) => {
|
|
1416
|
+
const span = Math.max(rangeEndMs - rangeStartMs, 1);
|
|
1417
|
+
const width = rectWidth > 0 ? rectWidth : 1;
|
|
1418
|
+
const ratio = Math.min(Math.max((clientX - rectLeft) / width, 0), 1);
|
|
1419
|
+
return rangeStartMs + ratio * span - _grabOffsetMs;
|
|
1420
|
+
};
|
|
1421
|
+
const formatTimeWindowParts = ({
|
|
1422
|
+
start,
|
|
1423
|
+
end,
|
|
1424
|
+
format: _format = DEFAULT_FORMAT
|
|
1425
|
+
}) => {
|
|
1426
|
+
const startDate = parseDateLike(start);
|
|
1427
|
+
const endDate = parseDateLike(end);
|
|
1428
|
+
if (!startDate || !endDate || typeof _format === 'function') {
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
const formats = Array.isArray(_format) ? _format : [_format, _format];
|
|
1432
|
+
const startText = startDate.format(formats[0] || DEFAULT_FORMAT[0]);
|
|
1433
|
+
let endFormat = formats[1] || formats[0] || DEFAULT_FORMAT[1];
|
|
1434
|
+
const endAtMidnight = endDate.valueOf() === endDate.startOf('day').valueOf();
|
|
1435
|
+
if (_format === DEFAULT_FORMAT && !endAtMidnight && !endDate.isSame(startDate, 'day')) {
|
|
1436
|
+
endFormat = 'MM-DD HH:mm';
|
|
1437
|
+
}
|
|
1438
|
+
return [startText, endDate.format(endFormat)];
|
|
1439
|
+
};
|
|
1440
|
+
const formatTimeWindow = ({
|
|
1441
|
+
start,
|
|
1442
|
+
end,
|
|
1443
|
+
format: _format2 = DEFAULT_FORMAT,
|
|
1444
|
+
separator: _separator = DEFAULT_SEPARATOR
|
|
1445
|
+
}) => {
|
|
1446
|
+
if (typeof _format2 === 'function') {
|
|
1447
|
+
const startDate = parseDateLike(start);
|
|
1448
|
+
const endDate = parseDateLike(end);
|
|
1449
|
+
return startDate && endDate ? _format2(startDate.toDate(), endDate.toDate()) : '';
|
|
1450
|
+
}
|
|
1451
|
+
const parts = formatTimeWindowParts({
|
|
1452
|
+
start,
|
|
1453
|
+
end,
|
|
1454
|
+
format: _format2
|
|
1455
|
+
});
|
|
1456
|
+
return parts ? `${parts[0]}${_separator}${parts[1]}` : '';
|
|
1457
|
+
};
|
|
1458
|
+
const LABEL_INTERVALS = [1, 2, 3, 4, 6, 8, 12, 24];
|
|
1459
|
+
const pickLabelEvery = (pxPerHour, minLabelPx) => {
|
|
1460
|
+
if (!(pxPerHour > 0)) {
|
|
1461
|
+
return 3;
|
|
1462
|
+
}
|
|
1463
|
+
return LABEL_INTERVALS.find(every => pxPerHour * every >= minLabelPx) || 24;
|
|
1464
|
+
};
|
|
1465
|
+
const TICK_MINUTES = [1, 2, 5, 10, 15, 30, 60];
|
|
1466
|
+
const MIN_TICK_GAP_PX = 7;
|
|
1467
|
+
const pickTickMinutes = (pxPerHour, labelEvery) => {
|
|
1468
|
+
const fit = TICK_MINUTES.find(minutes => pxPerHour * minutes / 60 >= MIN_TICK_GAP_PX);
|
|
1469
|
+
return fit || labelEvery * 60;
|
|
1470
|
+
};
|
|
1471
|
+
|
|
1472
|
+
/**
|
|
1473
|
+
* 只生成当前视窗 [viewStart, viewEnd] 内的刻度;`outOfRange` 标出超出可选范围的刻度。
|
|
1474
|
+
*/
|
|
1475
|
+
const buildScaleTicks = (viewStart, viewEnd, {
|
|
1476
|
+
trackWidth,
|
|
1477
|
+
rangeStart,
|
|
1478
|
+
rangeEnd,
|
|
1479
|
+
minLabelPx: _minLabelPx = 52
|
|
1480
|
+
} = {}) => {
|
|
1481
|
+
const viewStartMs = dayjs(viewStart).valueOf();
|
|
1482
|
+
const viewEndMs = dayjs(viewEnd).valueOf();
|
|
1483
|
+
const span = viewEndMs - viewStartMs;
|
|
1484
|
+
if (span <= 0) {
|
|
1485
|
+
return [];
|
|
1486
|
+
}
|
|
1487
|
+
const pxPerHour = trackWidth > 0 ? trackWidth * HOUR_MS / span : 0;
|
|
1488
|
+
const labelEvery = trackWidth > 0 ? pickLabelEvery(pxPerHour, _minLabelPx) : span / HOUR_MS <= 6 ? 1 : 3;
|
|
1489
|
+
const tickMinutes = trackWidth > 0 ? pickTickMinutes(pxPerHour, labelEvery) : 60;
|
|
1490
|
+
const tickMs = tickMinutes * MINUTE_MS;
|
|
1491
|
+
const rangeStartMs = rangeStart ? dayjs(rangeStart).valueOf() : -Infinity;
|
|
1492
|
+
const rangeEndMs = rangeEnd ? dayjs(rangeEnd).valueOf() : Infinity;
|
|
1493
|
+
const ticks = [];
|
|
1494
|
+
let cursorMs = snapMs(viewStartMs, tickMs, 'ceil');
|
|
1495
|
+
while (cursorMs <= viewEndMs) {
|
|
1496
|
+
const cursor = dayjs(cursorMs);
|
|
1497
|
+
const major = cursor.minute() === 0;
|
|
1498
|
+
const midnight = major && cursor.hour() === 0;
|
|
1499
|
+
const minute = cursor.minute();
|
|
1500
|
+
const level = midnight ? 'day' : major ? 'hour' : minute === 30 ? 'half' : minute % 15 === 0 && tickMinutes < 15 ? 'quarter' : 'minute';
|
|
1501
|
+
ticks.push({
|
|
1502
|
+
key: cursorMs,
|
|
1503
|
+
ratio: (cursorMs - viewStartMs) / span,
|
|
1504
|
+
major,
|
|
1505
|
+
level,
|
|
1506
|
+
label: midnight ? cursor.format('MM-DD') : cursor.format('HH:mm'),
|
|
1507
|
+
showLabel: major && cursor.hour() % labelEvery === 0,
|
|
1508
|
+
outOfRange: cursorMs < rangeStartMs || cursorMs > rangeEndMs
|
|
1509
|
+
});
|
|
1510
|
+
cursorMs = snapMs(cursorMs + tickMs, tickMs);
|
|
1511
|
+
}
|
|
1512
|
+
return ticks;
|
|
1513
|
+
};
|
|
1514
|
+
const formatDurationParts = minutes => {
|
|
1515
|
+
const total = Math.max(Math.round(minutes), 0);
|
|
1516
|
+
return {
|
|
1517
|
+
hours: Math.floor(total / 60),
|
|
1518
|
+
minutes: total % 60
|
|
1519
|
+
};
|
|
1520
|
+
};
|
|
1521
|
+
|
|
1522
|
+
var style$2 = {"root":"kne-react-calendar_0I96A","root-large":"kne-react-calendar_eGwaj","root-small":"kne-react-calendar_h50YK","header":"kne-react-calendar_oV0dZ","label":"kne-react-calendar_ES-Jt","separator":"kne-react-calendar_TWzY7","actions":"kne-react-calendar_FiXT7","switch":"kne-react-calendar_uBaQO","switch-on":"kne-react-calendar_c1XaN","duration":"kne-react-calendar_UnlvI","scale":"kne-react-calendar_2q4r3","track":"kne-react-calendar_Psk8T","root-dragging":"kne-react-calendar_lDK6o","ticks":"kne-react-calendar_RAKOV","tick":"kne-react-calendar_0-sBN","tick-minute":"kne-react-calendar_KR68F","tick-quarter":"kne-react-calendar_KG7-G","tick-half":"kne-react-calendar_cxGhs","tick-hour":"kne-react-calendar_eLF-c","tick-day":"kne-react-calendar_bVU--","labels":"kne-react-calendar_ahayU","tick-out":"kne-react-calendar_Crt1q","track-wheel":"kne-react-calendar_6YDSw","cylinder":"kne-react-calendar_zo6Jy","shade":"kne-react-calendar_7IH3i","shade-start":"kne-react-calendar_jm61m","shade-end":"kne-react-calendar_H6JJo","window":"kne-react-calendar_FpwgD","window-resizable":"kne-react-calendar_zx86H","window-fixed":"kne-react-calendar_e3Rls","edge":"kne-react-calendar_XWaYx","edge-start":"kne-react-calendar_Lk0dI","edge-end":"kne-react-calendar_-iQoy","edge-active":"kne-react-calendar_U0aKK","root-resizing":"kne-react-calendar_59sUX","window-compact":"kne-react-calendar_kvOXQ","tick-label":"kne-react-calendar_4LHqw","tick-label-day":"kne-react-calendar_S5S1g","root-locked":"kne-react-calendar_A9Acq"};
|
|
1523
|
+
|
|
1524
|
+
const GRIP_MIN_WIDTH = 28;
|
|
1525
|
+
const CLICK_MOVE_TOLERANCE = 4;
|
|
1526
|
+
const DISCRETE_WHEEL_DELTA = 50;
|
|
1527
|
+
const VISUAL_EASE_MS = 70;
|
|
1528
|
+
const WHEEL_IDLE_MS = 140;
|
|
1529
|
+
const WHEEL_NOTCH_EASE_MS = 90;
|
|
1530
|
+
const WHEEL_INERTIA_SCALE = 0.6;
|
|
1531
|
+
const sameInstant = (left, right) => {
|
|
1532
|
+
const leftDate = parseDateLike(left);
|
|
1533
|
+
const rightDate = parseDateLike(right);
|
|
1534
|
+
if (!leftDate || !rightDate) {
|
|
1535
|
+
return false;
|
|
1536
|
+
}
|
|
1537
|
+
return Math.abs(leftDate.valueOf() - rightDate.valueOf()) < 1000;
|
|
1538
|
+
};
|
|
1539
|
+
const readWindowStart = value => {
|
|
1540
|
+
if (!Array.isArray(value)) {
|
|
1541
|
+
return null;
|
|
1542
|
+
}
|
|
1543
|
+
return parseDateLike(value[0]);
|
|
1544
|
+
};
|
|
1545
|
+
const isDiscreteWheel = (event, deltaPx) => {
|
|
1546
|
+
if (event.deltaMode !== 0) {
|
|
1547
|
+
return true;
|
|
1548
|
+
}
|
|
1549
|
+
const legacy = event.wheelDeltaY || event.wheelDeltaX;
|
|
1550
|
+
if (legacy && Math.abs(legacy) % 120 === 0) {
|
|
1551
|
+
return true;
|
|
1552
|
+
}
|
|
1553
|
+
return Math.abs(deltaPx) >= DISCRETE_WHEEL_DELTA;
|
|
1554
|
+
};
|
|
1555
|
+
const useElementWidth = ref => {
|
|
1556
|
+
const [width, setWidth] = useState(0);
|
|
1557
|
+
useEffect(() => {
|
|
1558
|
+
const node = ref.current;
|
|
1559
|
+
if (!node) {
|
|
1560
|
+
return undefined;
|
|
1561
|
+
}
|
|
1562
|
+
setWidth(node.getBoundingClientRect().width);
|
|
1563
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
1564
|
+
return undefined;
|
|
1565
|
+
}
|
|
1566
|
+
const observer = new ResizeObserver(entries => {
|
|
1567
|
+
const entry = entries[0];
|
|
1568
|
+
if (entry) {
|
|
1569
|
+
setWidth(entry.contentRect.width);
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
observer.observe(node);
|
|
1573
|
+
return () => observer.disconnect();
|
|
1574
|
+
}, [ref]);
|
|
1575
|
+
return width;
|
|
1576
|
+
};
|
|
1577
|
+
const TimeWindowInner = ({
|
|
1578
|
+
value,
|
|
1579
|
+
defaultValue,
|
|
1580
|
+
onChange,
|
|
1581
|
+
duration,
|
|
1582
|
+
range,
|
|
1583
|
+
step,
|
|
1584
|
+
visibleSpan,
|
|
1585
|
+
debounceWait: _debounceWait = DEFAULT_DEBOUNCE_WAIT,
|
|
1586
|
+
format: _format = DEFAULT_FORMAT,
|
|
1587
|
+
separator: _separator = DEFAULT_SEPARATOR,
|
|
1588
|
+
showDuration: _showDuration = true,
|
|
1589
|
+
size: _size = 'default',
|
|
1590
|
+
inertia: _inertia = true,
|
|
1591
|
+
resizable: _resizable = true,
|
|
1592
|
+
wheelEffect: _wheelEffect = true,
|
|
1593
|
+
lockable: _lockable = false,
|
|
1594
|
+
locked,
|
|
1595
|
+
defaultLocked: _defaultLocked = false,
|
|
1596
|
+
onLockedChange,
|
|
1597
|
+
minDuration,
|
|
1598
|
+
maxDuration,
|
|
1599
|
+
className,
|
|
1600
|
+
style: styleProp
|
|
1601
|
+
}) => {
|
|
1602
|
+
const {
|
|
1603
|
+
formatMessage
|
|
1604
|
+
} = useIntl();
|
|
1605
|
+
const isControlled = typeof value !== 'undefined';
|
|
1606
|
+
const [innerValue, setInnerValue] = useState(() => {
|
|
1607
|
+
if (Array.isArray(defaultValue) && readWindowStart(defaultValue)) {
|
|
1608
|
+
return defaultValue;
|
|
1609
|
+
}
|
|
1610
|
+
return toWindowValue(createTimeWindow({
|
|
1611
|
+
duration,
|
|
1612
|
+
range,
|
|
1613
|
+
step
|
|
1614
|
+
}));
|
|
1615
|
+
});
|
|
1616
|
+
const [draftStart, setDraftStart] = useState(null);
|
|
1617
|
+
const [draftDuration, setDraftDuration] = useState(null);
|
|
947
1618
|
const [dragging, setDragging] = useState(false);
|
|
948
|
-
const [
|
|
949
|
-
const
|
|
950
|
-
const
|
|
951
|
-
const
|
|
952
|
-
const
|
|
953
|
-
const
|
|
954
|
-
const
|
|
955
|
-
const
|
|
956
|
-
const
|
|
957
|
-
const
|
|
958
|
-
const
|
|
959
|
-
const
|
|
960
|
-
const
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
const
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
1619
|
+
const [innerLocked, setInnerLocked] = useState(_defaultLocked);
|
|
1620
|
+
const lockedControlled = typeof locked === 'boolean';
|
|
1621
|
+
const isLocked = _lockable && (lockedControlled ? locked : innerLocked);
|
|
1622
|
+
const [resizeView, setResizeView] = useState(null);
|
|
1623
|
+
const resizeRef = useRef(null);
|
|
1624
|
+
const resizeEdge = resizeView ? resizeView.edge : null;
|
|
1625
|
+
const rootRef = useRef(null);
|
|
1626
|
+
const trackRef = useRef(null);
|
|
1627
|
+
const dragRef = useRef(null);
|
|
1628
|
+
const wheelAccumRef = useRef(0);
|
|
1629
|
+
const interactingRef = useRef(false);
|
|
1630
|
+
const timerRef = useRef(null);
|
|
1631
|
+
const pendingRef = useRef(null);
|
|
1632
|
+
const onChangeRef = useRef(onChange);
|
|
1633
|
+
const onLockedChangeRef = useRef(onLockedChange);
|
|
1634
|
+
const isLockedRef = useRef(isLocked);
|
|
1635
|
+
onLockedChangeRef.current = onLockedChange;
|
|
1636
|
+
isLockedRef.current = isLocked;
|
|
1637
|
+
const trackWidth = useElementWidth(trackRef);
|
|
1638
|
+
const committedValue = isControlled ? value : innerValue;
|
|
1639
|
+
const committedStart = readWindowStart(committedValue);
|
|
1640
|
+
const committedMs = committedStart ? committedStart.valueOf() : null;
|
|
1641
|
+
const anchorDay = committedStart ? committedStart.startOf('day').valueOf() : undefined;
|
|
1642
|
+
const draftMs = draftStart ? draftStart.valueOf() : null;
|
|
1643
|
+
const committedEnd = Array.isArray(committedValue) ? parseDateLike(committedValue[1]) : null;
|
|
1644
|
+
const committedEndMs = committedEnd ? committedEnd.valueOf() : null;
|
|
1645
|
+
const committedDuration = committedStart && committedEnd && committedEnd.isAfter(committedStart) ? committedEnd.diff(committedStart, 'minute', true) : null;
|
|
1646
|
+
const activeDuration = draftDuration != null ? draftDuration : committedDuration != null ? committedDuration : duration;
|
|
1647
|
+
const initialDurationRef = useRef(committedDuration || DEFAULT_DURATION);
|
|
1648
|
+
const optionsRef = useRef(null);
|
|
1649
|
+
onChangeRef.current = onChange;
|
|
1650
|
+
optionsRef.current = {
|
|
1651
|
+
duration: activeDuration,
|
|
1652
|
+
range,
|
|
1653
|
+
step,
|
|
1654
|
+
now: anchorDay
|
|
1655
|
+
};
|
|
1656
|
+
const baseWindow = useMemo(() => {
|
|
1657
|
+
const start = draftMs == null ? committedMs : draftMs;
|
|
1658
|
+
return createTimeWindow({
|
|
1659
|
+
start,
|
|
1660
|
+
duration: activeDuration,
|
|
1661
|
+
range,
|
|
1662
|
+
step,
|
|
1663
|
+
now: anchorDay
|
|
1664
|
+
});
|
|
1665
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1666
|
+
}, [anchorDay, committedMs, draftMs, activeDuration, step, JSON.stringify(range === undefined ? 'default' : range)]);
|
|
1667
|
+
const visibleSpanMinutes = resolveVisibleSpan({
|
|
1668
|
+
visibleSpan,
|
|
1669
|
+
duration: baseWindow.duration,
|
|
1670
|
+
baseDuration: Number(duration) > 0 ? Number(duration) : initialDurationRef.current,
|
|
1671
|
+
rangeStart: baseWindow.rangeStart,
|
|
1672
|
+
rangeEnd: baseWindow.rangeEnd
|
|
1673
|
+
});
|
|
1674
|
+
const baseStartMs = baseWindow.start.valueOf();
|
|
1675
|
+
const rangeStartMs = baseWindow.rangeStart ? baseWindow.rangeStart.valueOf() : null;
|
|
1676
|
+
const rangeEndMs = baseWindow.rangeEnd ? baseWindow.rangeEnd.valueOf() : null;
|
|
1677
|
+
const durationMs = baseWindow.duration * 60 * 1000;
|
|
1678
|
+
const [visualMs, setVisualMs] = useState(null);
|
|
1679
|
+
const visualRef = useRef(null);
|
|
1680
|
+
const visualTargetRef = useRef(null);
|
|
1681
|
+
const frameRef = useRef(null);
|
|
1682
|
+
const lastFrameRef = useRef(0);
|
|
1683
|
+
const wheelActiveRef = useRef(false);
|
|
1684
|
+
const wheelIdleRef = useRef(null);
|
|
1685
|
+
const easeRef = useRef(VISUAL_EASE_MS);
|
|
1686
|
+
const glidingRef = useRef(false);
|
|
1687
|
+
const wheelBaseRef = useRef(null);
|
|
1688
|
+
const wheelSamplesRef = useRef([]);
|
|
1689
|
+
const wheelDirectionRef = useRef(0);
|
|
1690
|
+
const inertiaTau = resolveInertia(_inertia);
|
|
1691
|
+
const inertiaRef = useRef(inertiaTau);
|
|
1692
|
+
inertiaRef.current = inertiaTau;
|
|
1693
|
+
const clampVisual = ms => {
|
|
1694
|
+
let next = ms;
|
|
1695
|
+
if (rangeEndMs != null) {
|
|
1696
|
+
next = Math.min(next, rangeEndMs - durationMs);
|
|
971
1697
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
const container = slotsRef.current;
|
|
975
|
-
if (!container || scrollLockRef.current == null) {
|
|
976
|
-
return;
|
|
1698
|
+
if (rangeStartMs != null) {
|
|
1699
|
+
next = Math.max(next, rangeStartMs);
|
|
977
1700
|
}
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
const
|
|
986
|
-
|
|
1701
|
+
return next;
|
|
1702
|
+
};
|
|
1703
|
+
const clampVisualRef = useRef(clampVisual);
|
|
1704
|
+
clampVisualRef.current = clampVisual;
|
|
1705
|
+
const runFrame = time => {
|
|
1706
|
+
const target = visualTargetRef.current;
|
|
1707
|
+
const current = visualRef.current == null ? target : visualRef.current;
|
|
1708
|
+
const dt = lastFrameRef.current ? Math.min(time - lastFrameRef.current, 64) : 16;
|
|
1709
|
+
lastFrameRef.current = time;
|
|
1710
|
+
const diff = target - current;
|
|
1711
|
+
const settled = Math.abs(diff) < 1000;
|
|
1712
|
+
const next = settled ? target : current + diff * (1 - Math.exp(-dt / easeRef.current));
|
|
1713
|
+
visualRef.current = next;
|
|
1714
|
+
setVisualMs(next);
|
|
1715
|
+
if (glidingRef.current) {
|
|
1716
|
+
moveToRef.current(next);
|
|
1717
|
+
}
|
|
1718
|
+
if (settled) {
|
|
1719
|
+
frameRef.current = null;
|
|
1720
|
+
lastFrameRef.current = 0;
|
|
1721
|
+
easeRef.current = VISUAL_EASE_MS;
|
|
1722
|
+
glidingRef.current = false;
|
|
987
1723
|
return;
|
|
988
1724
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
clearLongPress();
|
|
995
|
-
const container = slotsRef.current;
|
|
996
|
-
const touchState = touchStateRef.current;
|
|
997
|
-
if (container && (touchState == null ? void 0 : touchState.pointerId) != null && container.hasPointerCapture != null && container.hasPointerCapture(touchState.pointerId)) {
|
|
998
|
-
try {
|
|
999
|
-
container.releasePointerCapture(touchState.pointerId);
|
|
1000
|
-
} catch (_unused) {
|
|
1001
|
-
// Ignore capture release errors on stale pointers.
|
|
1002
|
-
}
|
|
1725
|
+
frameRef.current = requestAnimationFrame(runFrame);
|
|
1726
|
+
};
|
|
1727
|
+
const stopGlide = () => {
|
|
1728
|
+
if (!glidingRef.current) {
|
|
1729
|
+
return false;
|
|
1003
1730
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
if (!nextSlots.length) {
|
|
1014
|
-
setActivePopoverKey(null);
|
|
1015
|
-
resetGestureState();
|
|
1016
|
-
}
|
|
1017
|
-
setSelectedSlots(nextSlots);
|
|
1018
|
-
onSlotRangeChange == null || onSlotRangeChange(nextSlots, {
|
|
1019
|
-
action: nextSlots.length ? 'select' : 'clear'
|
|
1020
|
-
});
|
|
1021
|
-
}, [onSlotRangeChange, resetGestureState, setSelectedSlots]);
|
|
1022
|
-
const getSelectableRange = useCallback((startIndex, endIndex) => {
|
|
1023
|
-
const [start, end] = [Math.min(startIndex, endIndex), Math.max(startIndex, endIndex)];
|
|
1024
|
-
return slotsDataRef.current.slice(start, end + 1).filter(slot => slot.status === 'free');
|
|
1025
|
-
}, []);
|
|
1026
|
-
const showFormPopoverForSlots = useCallback(slotsList => {
|
|
1027
|
-
const anchorSlot = slotsList[slotsList.length - 1];
|
|
1028
|
-
if (anchorSlot) {
|
|
1029
|
-
setActivePopoverKey(getPopoverKey(anchorSlot));
|
|
1731
|
+
glidingRef.current = false;
|
|
1732
|
+
wheelBaseRef.current = null;
|
|
1733
|
+
wheelSamplesRef.current = [];
|
|
1734
|
+
wheelDirectionRef.current = 0;
|
|
1735
|
+
easeRef.current = VISUAL_EASE_MS;
|
|
1736
|
+
if (frameRef.current) {
|
|
1737
|
+
cancelAnimationFrame(frameRef.current);
|
|
1738
|
+
frameRef.current = null;
|
|
1739
|
+
lastFrameRef.current = 0;
|
|
1030
1740
|
}
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
}
|
|
1045
|
-
setDragging(true);
|
|
1046
|
-
selectSlots([slot]);
|
|
1047
|
-
if (!_showPopover) {
|
|
1048
|
-
suppressTriggerUntilRef.current = Date.now() + 700;
|
|
1049
|
-
setActivePopoverKey(null);
|
|
1050
|
-
}
|
|
1051
|
-
onFreeSlotClick == null || onFreeSlotClick(slot);
|
|
1052
|
-
}, [lockContainerScroll, onFreeSlotClick, selectSlots]);
|
|
1053
|
-
const finishSelection = useCallback(({
|
|
1054
|
-
fromTouch: _fromTouch = false
|
|
1741
|
+
visualTargetRef.current = visualRef.current;
|
|
1742
|
+
return true;
|
|
1743
|
+
};
|
|
1744
|
+
const glideTo = (ms, easeMs = inertiaTau) => {
|
|
1745
|
+
const snapped = createTimeWindow(_extends({
|
|
1746
|
+
start: ms
|
|
1747
|
+
}, optionsRef.current)).start.valueOf();
|
|
1748
|
+
glidingRef.current = true;
|
|
1749
|
+
easeRef.current = easeMs;
|
|
1750
|
+
setVisualTarget(snapped);
|
|
1751
|
+
};
|
|
1752
|
+
const setVisualTarget = (ms, {
|
|
1753
|
+
immediate: _immediate = false
|
|
1055
1754
|
} = {}) => {
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
if (
|
|
1059
|
-
if (
|
|
1060
|
-
|
|
1755
|
+
const target = clampVisualRef.current(ms);
|
|
1756
|
+
visualTargetRef.current = target;
|
|
1757
|
+
if (_immediate || typeof requestAnimationFrame === 'undefined') {
|
|
1758
|
+
if (frameRef.current) {
|
|
1759
|
+
cancelAnimationFrame(frameRef.current);
|
|
1760
|
+
frameRef.current = null;
|
|
1761
|
+
lastFrameRef.current = 0;
|
|
1061
1762
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
}, [resetGestureState, showFormPopoverForSlots]);
|
|
1065
|
-
const updateSelectionRange = useCallback(index => {
|
|
1066
|
-
const startIndex = dragStartIndexRef.current;
|
|
1067
|
-
if (startIndex === null || !allowMultiSelectRef.current) {
|
|
1763
|
+
visualRef.current = target;
|
|
1764
|
+
setVisualMs(target);
|
|
1068
1765
|
return;
|
|
1069
1766
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1767
|
+
if (!frameRef.current) {
|
|
1768
|
+
frameRef.current = requestAnimationFrame(runFrame);
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
const setVisualTargetRef = useRef(setVisualTarget);
|
|
1772
|
+
setVisualTargetRef.current = setVisualTarget;
|
|
1773
|
+
const glideToRef = useRef(glideTo);
|
|
1774
|
+
glideToRef.current = glideTo;
|
|
1775
|
+
const stopGlideRef = useRef(stopGlide);
|
|
1776
|
+
stopGlideRef.current = stopGlide;
|
|
1777
|
+
useEffect(() => () => {
|
|
1778
|
+
if (frameRef.current) {
|
|
1779
|
+
cancelAnimationFrame(frameRef.current);
|
|
1780
|
+
}
|
|
1781
|
+
clearTimeout(wheelIdleRef.current);
|
|
1075
1782
|
}, []);
|
|
1076
|
-
|
|
1077
|
-
if (
|
|
1783
|
+
useEffect(() => {
|
|
1784
|
+
if (dragRef.current || resizeRef.current || wheelActiveRef.current || glidingRef.current) {
|
|
1078
1785
|
return;
|
|
1079
1786
|
}
|
|
1080
|
-
if (
|
|
1787
|
+
if (visualRef.current == null) {
|
|
1788
|
+
visualRef.current = baseStartMs;
|
|
1789
|
+
visualTargetRef.current = baseStartMs;
|
|
1081
1790
|
return;
|
|
1082
1791
|
}
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1088
|
-
|
|
1792
|
+
setVisualTargetRef.current(baseStartMs);
|
|
1793
|
+
}, [baseStartMs]);
|
|
1794
|
+
const displayStartMs = resizeView || visualMs == null ? baseStartMs : visualMs;
|
|
1795
|
+
const viewport = resizeView || getViewport({
|
|
1796
|
+
startMs: displayStartMs,
|
|
1797
|
+
duration: baseWindow.duration
|
|
1798
|
+
}, visibleSpanMinutes);
|
|
1799
|
+
const viewRef = useRef(null);
|
|
1800
|
+
const baseWindowRef = useRef(baseWindow);
|
|
1801
|
+
baseWindowRef.current = baseWindow;
|
|
1802
|
+
viewRef.current = viewport;
|
|
1803
|
+
const ticks = useMemo(() => buildScaleTicks(viewport.startMs, viewport.endMs, {
|
|
1804
|
+
trackWidth,
|
|
1805
|
+
rangeStart: rangeStartMs,
|
|
1806
|
+
rangeEnd: rangeEndMs
|
|
1807
|
+
}), [viewport.startMs, viewport.endMs, trackWidth, rangeStartMs, rangeEndMs]);
|
|
1808
|
+
const toPercent = ms => (ms - viewport.startMs) / viewport.spanMs * 100;
|
|
1809
|
+
const width = durationMs / viewport.spanMs * 100;
|
|
1810
|
+
const left = toPercent(displayStartMs);
|
|
1811
|
+
const windowPx = width / 100 * trackWidth;
|
|
1812
|
+
const leftShade = rangeStartMs != null ? Math.min(Math.max(toPercent(rangeStartMs), 0), 100) : 0;
|
|
1813
|
+
const rightShade = rangeEndMs != null ? Math.min(Math.max(100 - toPercent(rangeEndMs), 0), 100) : 0;
|
|
1814
|
+
const label = useMemo(() => {
|
|
1815
|
+
if (typeof _format === 'function') {
|
|
1816
|
+
return _format(baseWindow.start.toDate(), baseWindow.end.toDate());
|
|
1089
1817
|
}
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1818
|
+
const [startText, endText] = formatTimeWindowParts({
|
|
1819
|
+
start: baseWindow.start,
|
|
1820
|
+
end: baseWindow.end,
|
|
1821
|
+
format: _format
|
|
1822
|
+
});
|
|
1823
|
+
return /*#__PURE__*/jsxs(Fragment, {
|
|
1824
|
+
children: [/*#__PURE__*/jsx("span", {
|
|
1825
|
+
children: startText
|
|
1826
|
+
}), /*#__PURE__*/jsx("span", {
|
|
1827
|
+
className: style$2.separator,
|
|
1828
|
+
children: _separator
|
|
1829
|
+
}), /*#__PURE__*/jsx("span", {
|
|
1830
|
+
children: endText
|
|
1831
|
+
})]
|
|
1832
|
+
});
|
|
1833
|
+
}, [baseWindow.end, baseWindow.start, _format, _separator]);
|
|
1834
|
+
const ariaValueText = formatTimeWindow({
|
|
1835
|
+
start: baseWindow.start,
|
|
1836
|
+
end: baseWindow.end,
|
|
1837
|
+
format: typeof _format === 'function' ? DEFAULT_FORMAT : _format,
|
|
1838
|
+
separator: typeof _separator === 'string' ? _separator : DEFAULT_SEPARATOR
|
|
1839
|
+
});
|
|
1840
|
+
const durationText = useMemo(() => {
|
|
1841
|
+
const {
|
|
1842
|
+
hours,
|
|
1843
|
+
minutes
|
|
1844
|
+
} = formatDurationParts(baseWindow.duration);
|
|
1845
|
+
const parts = [];
|
|
1846
|
+
if (hours) {
|
|
1847
|
+
parts.push(formatMessage({
|
|
1848
|
+
id: 'TimeWindow.hours'
|
|
1849
|
+
}, {
|
|
1850
|
+
count: hours
|
|
1851
|
+
}));
|
|
1095
1852
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1853
|
+
if (minutes || !hours) {
|
|
1854
|
+
parts.push(formatMessage({
|
|
1855
|
+
id: 'TimeWindow.minutes'
|
|
1856
|
+
}, {
|
|
1857
|
+
count: minutes
|
|
1858
|
+
}));
|
|
1101
1859
|
}
|
|
1102
|
-
|
|
1103
|
-
}, [
|
|
1104
|
-
const
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
return;
|
|
1860
|
+
return parts.join(' ');
|
|
1861
|
+
}, [baseWindow.duration, formatMessage]);
|
|
1862
|
+
const clearTimer = () => {
|
|
1863
|
+
if (timerRef.current) {
|
|
1864
|
+
clearTimeout(timerRef.current);
|
|
1865
|
+
timerRef.current = null;
|
|
1109
1866
|
}
|
|
1110
|
-
|
|
1111
|
-
|
|
1867
|
+
};
|
|
1868
|
+
useEffect(() => clearTimer, []);
|
|
1869
|
+
useEffect(() => {
|
|
1870
|
+
if (interactingRef.current || draftMs == null) {
|
|
1112
1871
|
return;
|
|
1113
1872
|
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1873
|
+
const pending = Array.isArray(pendingRef.current) ? pendingRef.current : null;
|
|
1874
|
+
if (pending && sameInstant(committedMs, pending[0]) && sameInstant(committedEndMs, pending[1])) {
|
|
1875
|
+
pendingRef.current = null;
|
|
1876
|
+
setDraftStart(null);
|
|
1877
|
+
setDraftDuration(null);
|
|
1878
|
+
}
|
|
1879
|
+
}, [committedMs, committedEndMs, draftMs]);
|
|
1880
|
+
const publish = nextWindow => {
|
|
1881
|
+
const nextValue = toWindowValue(nextWindow);
|
|
1882
|
+
baseWindowRef.current = nextWindow;
|
|
1883
|
+
pendingRef.current = nextValue;
|
|
1884
|
+
setDraftStart(nextWindow.start);
|
|
1885
|
+
setDraftDuration(nextWindow.duration);
|
|
1886
|
+
clearTimer();
|
|
1887
|
+
timerRef.current = setTimeout(() => {
|
|
1888
|
+
timerRef.current = null;
|
|
1889
|
+
if (!isControlled) {
|
|
1890
|
+
setInnerValue(nextValue);
|
|
1117
1891
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
clearLongPress,
|
|
1123
|
-
finishSelection,
|
|
1124
|
-
onFreeSlotClick,
|
|
1125
|
-
resetGestureState,
|
|
1126
|
-
selectSlots,
|
|
1127
|
-
showFormPopoverForSlots,
|
|
1128
|
-
startSlotSelection,
|
|
1129
|
-
updateSelectionRange
|
|
1892
|
+
if (onChangeRef.current) {
|
|
1893
|
+
onChangeRef.current(nextValue);
|
|
1894
|
+
}
|
|
1895
|
+
}, _debounceWait);
|
|
1130
1896
|
};
|
|
1897
|
+
const moveTo = startInput => {
|
|
1898
|
+
const current = baseWindowRef.current;
|
|
1899
|
+
const nextWindow = createTimeWindow(_extends({
|
|
1900
|
+
start: startInput
|
|
1901
|
+
}, optionsRef.current));
|
|
1902
|
+
if (nextWindow.start.valueOf() === current.start.valueOf()) {
|
|
1903
|
+
return;
|
|
1904
|
+
}
|
|
1905
|
+
publish(nextWindow);
|
|
1906
|
+
};
|
|
1907
|
+
const moveToRef = useRef(moveTo);
|
|
1908
|
+
moveToRef.current = moveTo;
|
|
1909
|
+
const durationPropRef = useRef(duration);
|
|
1131
1910
|
useEffect(() => {
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
return undefined;
|
|
1911
|
+
if (durationPropRef.current === duration) {
|
|
1912
|
+
return;
|
|
1135
1913
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1914
|
+
durationPropRef.current = duration;
|
|
1915
|
+
publish(createTimeWindow(_extends({}, optionsRef.current, {
|
|
1916
|
+
start: baseWindowRef.current.start,
|
|
1917
|
+
duration
|
|
1918
|
+
})));
|
|
1919
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1920
|
+
}, [duration]);
|
|
1921
|
+
const pointerTimeAt = (clientX, view) => {
|
|
1922
|
+
const rect = trackRef.current ? trackRef.current.getBoundingClientRect() : {
|
|
1923
|
+
left: 0,
|
|
1924
|
+
width: 1
|
|
1145
1925
|
};
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1926
|
+
return timeAtClientX({
|
|
1927
|
+
clientX,
|
|
1928
|
+
rectLeft: rect.left,
|
|
1929
|
+
rectWidth: rect.width,
|
|
1930
|
+
rangeStartMs: view.startMs,
|
|
1931
|
+
rangeEndMs: view.endMs
|
|
1932
|
+
});
|
|
1933
|
+
};
|
|
1934
|
+
const startResize = (event, edge) => {
|
|
1935
|
+
const view = viewRef.current;
|
|
1936
|
+
const current = baseWindowRef.current;
|
|
1937
|
+
const edgeMs = edge === 'start' ? current.start.valueOf() : current.end.valueOf();
|
|
1938
|
+
resizeRef.current = {
|
|
1939
|
+
pointerId: event.pointerId,
|
|
1940
|
+
edge,
|
|
1941
|
+
view,
|
|
1942
|
+
grabOffsetMs: pointerTimeAt(event.clientX, view) - edgeMs
|
|
1149
1943
|
};
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1944
|
+
interactingRef.current = true;
|
|
1945
|
+
setResizeView(_extends({}, view, {
|
|
1946
|
+
edge
|
|
1947
|
+
}));
|
|
1948
|
+
try {
|
|
1949
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
1950
|
+
} catch (error) {
|
|
1951
|
+
// pointer may already be released
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1954
|
+
const moveResize = event => {
|
|
1955
|
+
const resize = resizeRef.current;
|
|
1956
|
+
const current = baseWindowRef.current;
|
|
1957
|
+
const next = resizeTimeWindow({
|
|
1958
|
+
edge: resize.edge,
|
|
1959
|
+
pointerMs: pointerTimeAt(event.clientX, resize.view) - resize.grabOffsetMs,
|
|
1960
|
+
startMs: current.start.valueOf(),
|
|
1961
|
+
endMs: current.end.valueOf(),
|
|
1962
|
+
step: current.step,
|
|
1963
|
+
rangeStart: current.rangeStart,
|
|
1964
|
+
rangeEnd: current.rangeEnd,
|
|
1965
|
+
minDuration,
|
|
1966
|
+
maxDuration
|
|
1967
|
+
});
|
|
1968
|
+
if (next.startMs === current.start.valueOf() && next.duration === current.duration) {
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
publish(createTimeWindow(_extends({}, optionsRef.current, {
|
|
1972
|
+
start: next.startMs,
|
|
1973
|
+
duration: next.duration
|
|
1974
|
+
})));
|
|
1975
|
+
};
|
|
1976
|
+
const endResize = () => {
|
|
1977
|
+
const resize = resizeRef.current;
|
|
1978
|
+
resizeRef.current = null;
|
|
1979
|
+
interactingRef.current = false;
|
|
1980
|
+
const current = baseWindowRef.current;
|
|
1981
|
+
const centerMs = resize.view.startMs + resize.view.spanMs / 2;
|
|
1982
|
+
const frozenStart = centerMs - current.duration * 60 * 1000 / 2;
|
|
1983
|
+
visualRef.current = frozenStart;
|
|
1984
|
+
setVisualMs(frozenStart);
|
|
1985
|
+
setResizeView(null);
|
|
1986
|
+
setVisualTargetRef.current(current.start.valueOf());
|
|
1987
|
+
};
|
|
1988
|
+
const onPointerDown = event => {
|
|
1989
|
+
if (event.button != null && event.button !== 0) {
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1992
|
+
const braked = stopGlideRef.current();
|
|
1993
|
+
const edgeNode = _resizable && event.target && event.target.closest ? event.target.closest('[data-edge]') : null;
|
|
1994
|
+
if (edgeNode) {
|
|
1995
|
+
startResize(event, edgeNode.getAttribute('data-edge'));
|
|
1996
|
+
return;
|
|
1997
|
+
}
|
|
1998
|
+
dragRef.current = {
|
|
1999
|
+
pointerId: event.pointerId,
|
|
2000
|
+
startX: event.clientX,
|
|
2001
|
+
originMs: visualRef.current == null ? baseWindowRef.current.start.valueOf() : visualRef.current,
|
|
2002
|
+
moved: false,
|
|
2003
|
+
braked,
|
|
2004
|
+
samples: []
|
|
1153
2005
|
};
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
if (
|
|
1164
|
-
|
|
2006
|
+
interactingRef.current = true;
|
|
2007
|
+
try {
|
|
2008
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2009
|
+
} catch (error) {
|
|
2010
|
+
// pointer may already be released
|
|
2011
|
+
}
|
|
2012
|
+
};
|
|
2013
|
+
const onPointerMove = event => {
|
|
2014
|
+
if (resizeRef.current) {
|
|
2015
|
+
if (resizeRef.current.pointerId === event.pointerId) {
|
|
2016
|
+
moveResize(event);
|
|
1165
2017
|
}
|
|
1166
|
-
return
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
2020
|
+
const drag = dragRef.current;
|
|
2021
|
+
if (!drag || drag.pointerId !== event.pointerId) {
|
|
2022
|
+
return;
|
|
2023
|
+
}
|
|
2024
|
+
const dx = event.clientX - drag.startX;
|
|
2025
|
+
if (!drag.moved && Math.abs(dx) < CLICK_MOVE_TOLERANCE) {
|
|
2026
|
+
return;
|
|
2027
|
+
}
|
|
2028
|
+
if (!drag.moved) {
|
|
2029
|
+
drag.moved = true;
|
|
2030
|
+
setDragging(true);
|
|
2031
|
+
}
|
|
2032
|
+
const rect = trackRef.current ? trackRef.current.getBoundingClientRect() : {
|
|
2033
|
+
width: 1
|
|
1167
2034
|
};
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
2035
|
+
const deltaMs = dx / (rect.width || 1) * viewRef.current.spanMs;
|
|
2036
|
+
const rawMs = drag.originMs - deltaMs;
|
|
2037
|
+
drag.samples.push({
|
|
2038
|
+
t: event.timeStamp,
|
|
2039
|
+
ms: rawMs
|
|
2040
|
+
});
|
|
2041
|
+
if (drag.samples.length > 12) {
|
|
2042
|
+
drag.samples.shift();
|
|
2043
|
+
}
|
|
2044
|
+
moveToRef.current(rawMs);
|
|
2045
|
+
setVisualTargetRef.current(rawMs, {
|
|
2046
|
+
immediate: true
|
|
2047
|
+
});
|
|
2048
|
+
};
|
|
2049
|
+
const endDrag = event => {
|
|
2050
|
+
if (resizeRef.current) {
|
|
2051
|
+
if (!event || resizeRef.current.pointerId === event.pointerId) {
|
|
2052
|
+
endResize();
|
|
1172
2053
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
const drag = dragRef.current;
|
|
2057
|
+
if (!drag || event && drag.pointerId !== event.pointerId) {
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
dragRef.current = null;
|
|
2061
|
+
interactingRef.current = false;
|
|
2062
|
+
setDragging(false);
|
|
2063
|
+
if (drag.moved) {
|
|
2064
|
+
const velocity = inertiaRef.current && event ? estimateVelocity(drag.samples, event.timeStamp) : 0;
|
|
2065
|
+
const projection = velocity * inertiaRef.current;
|
|
2066
|
+
if (Math.abs(projection) >= baseWindowRef.current.step * 60 * 1000) {
|
|
2067
|
+
glideToRef.current(visualRef.current + projection);
|
|
2068
|
+
} else {
|
|
2069
|
+
setVisualTargetRef.current(baseWindowRef.current.start.valueOf());
|
|
1180
2070
|
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
2071
|
+
}
|
|
2072
|
+
if (!drag.moved && drag.braked) {
|
|
2073
|
+
moveToRef.current(drag.originMs);
|
|
2074
|
+
setVisualTargetRef.current(baseWindowRef.current.start.valueOf());
|
|
2075
|
+
return;
|
|
2076
|
+
}
|
|
2077
|
+
if (!drag.moved && event && event.type === 'pointerup') {
|
|
2078
|
+
const rect = trackRef.current ? trackRef.current.getBoundingClientRect() : {
|
|
2079
|
+
left: 0,
|
|
2080
|
+
width: 1
|
|
2081
|
+
};
|
|
2082
|
+
const view = viewRef.current;
|
|
2083
|
+
const clickedMs = timeAtClientX({
|
|
2084
|
+
clientX: event.clientX,
|
|
2085
|
+
rectLeft: rect.left,
|
|
2086
|
+
rectWidth: rect.width,
|
|
2087
|
+
rangeStartMs: view.startMs,
|
|
2088
|
+
rangeEndMs: view.endMs
|
|
1185
2089
|
});
|
|
2090
|
+
const current = baseWindowRef.current;
|
|
2091
|
+
moveToRef.current(clickedMs - current.duration * 60 * 1000 / 2);
|
|
2092
|
+
}
|
|
2093
|
+
};
|
|
2094
|
+
const onKeyDown = event => {
|
|
2095
|
+
stopGlideRef.current();
|
|
2096
|
+
const current = baseWindowRef.current;
|
|
2097
|
+
const stepMinutes = current.step;
|
|
2098
|
+
const moves = {
|
|
2099
|
+
ArrowLeft: -stepMinutes,
|
|
2100
|
+
ArrowDown: -stepMinutes,
|
|
2101
|
+
ArrowRight: stepMinutes,
|
|
2102
|
+
ArrowUp: stepMinutes,
|
|
2103
|
+
PageDown: -current.duration,
|
|
2104
|
+
PageUp: current.duration
|
|
1186
2105
|
};
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
2106
|
+
if (event.key === 'Home' && current.rangeStart) {
|
|
2107
|
+
event.preventDefault();
|
|
2108
|
+
moveToRef.current(current.rangeStart);
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
if (event.key === 'End' && current.rangeEnd) {
|
|
2112
|
+
event.preventDefault();
|
|
2113
|
+
moveToRef.current(current.rangeEnd);
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
if (!(event.key in moves)) {
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
2119
|
+
event.preventDefault();
|
|
2120
|
+
const minutes = event.shiftKey ? Math.sign(moves[event.key]) * Math.max(60, stepMinutes) : moves[event.key];
|
|
2121
|
+
moveToRef.current(current.start.add(minutes, 'minute'));
|
|
2122
|
+
};
|
|
2123
|
+
useEffect(() => {
|
|
2124
|
+
const node = rootRef.current;
|
|
2125
|
+
if (!node) {
|
|
2126
|
+
return undefined;
|
|
2127
|
+
}
|
|
2128
|
+
const onWheel = event => {
|
|
2129
|
+
if (isLockedRef.current) {
|
|
1194
2130
|
return;
|
|
1195
2131
|
}
|
|
1196
|
-
const
|
|
1197
|
-
|
|
1198
|
-
if (Number.isNaN(index) || (slot == null ? void 0 : slot.status) !== 'free') {
|
|
2132
|
+
const dominant = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY;
|
|
2133
|
+
if (!dominant) {
|
|
1199
2134
|
return;
|
|
1200
2135
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
2136
|
+
event.preventDefault();
|
|
2137
|
+
const current = baseWindowRef.current;
|
|
2138
|
+
const rectWidth = trackRef.current ? trackRef.current.getBoundingClientRect().width : node.clientWidth;
|
|
2139
|
+
const deltaPx = wheelDeltaToPx({
|
|
2140
|
+
delta: dominant,
|
|
2141
|
+
deltaMode: event.deltaMode,
|
|
2142
|
+
trackWidth: rectWidth
|
|
2143
|
+
});
|
|
2144
|
+
const stepMs = current.step * 60 * 1000;
|
|
2145
|
+
const pxPerStep = rectWidth * stepMs / viewRef.current.spanMs;
|
|
2146
|
+
const discrete = isDiscreteWheel(event, deltaPx);
|
|
2147
|
+
const {
|
|
2148
|
+
steps,
|
|
2149
|
+
accumulatedPx
|
|
2150
|
+
} = consumeWheelSteps({
|
|
2151
|
+
accumulatedPx: wheelAccumRef.current,
|
|
2152
|
+
deltaPx,
|
|
2153
|
+
pxPerStep,
|
|
2154
|
+
discrete
|
|
2155
|
+
});
|
|
2156
|
+
wheelAccumRef.current = accumulatedPx;
|
|
2157
|
+
wheelActiveRef.current = true;
|
|
2158
|
+
clearTimeout(wheelIdleRef.current);
|
|
2159
|
+
wheelIdleRef.current = setTimeout(() => {
|
|
2160
|
+
wheelActiveRef.current = false;
|
|
2161
|
+
wheelAccumRef.current = 0;
|
|
2162
|
+
wheelBaseRef.current = null;
|
|
2163
|
+
wheelSamplesRef.current = [];
|
|
2164
|
+
wheelDirectionRef.current = 0;
|
|
2165
|
+
if (!glidingRef.current) {
|
|
2166
|
+
setVisualTargetRef.current(baseWindowRef.current.start.valueOf());
|
|
2167
|
+
}
|
|
2168
|
+
}, WHEEL_IDLE_MS);
|
|
2169
|
+
if (discrete && inertiaRef.current) {
|
|
2170
|
+
if (!steps) {
|
|
1221
2171
|
return;
|
|
1222
2172
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
const touchState = touchStateRef.current;
|
|
1229
|
-
if (!touchState || event.pointerId !== touchState.pointerId) {
|
|
1230
|
-
return;
|
|
1231
|
-
}
|
|
1232
|
-
touchState.lastX = event.clientX;
|
|
1233
|
-
touchState.lastY = event.clientY;
|
|
1234
|
-
if (!touchState.active) {
|
|
1235
|
-
if (shouldCancelPendingGesture(touchState, event.clientX, event.clientY)) {
|
|
1236
|
-
touchState.moved = true;
|
|
1237
|
-
clearPendingTouch();
|
|
2173
|
+
const samples = wheelSamplesRef.current;
|
|
2174
|
+
if (wheelDirectionRef.current !== Math.sign(steps)) {
|
|
2175
|
+
samples.length = 0;
|
|
2176
|
+
wheelBaseRef.current = null;
|
|
2177
|
+
wheelDirectionRef.current = Math.sign(steps);
|
|
1238
2178
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
}
|
|
1248
|
-
};
|
|
1249
|
-
const handlePointerUp = event => {
|
|
1250
|
-
const touchState = touchStateRef.current;
|
|
1251
|
-
if (!touchState || event.pointerId !== touchState.pointerId) {
|
|
1252
|
-
return;
|
|
1253
|
-
}
|
|
1254
|
-
releasePointerCapture(event.pointerId);
|
|
1255
|
-
suppressMouseUntilRef.current = Date.now() + 700;
|
|
1256
|
-
if (touchState.active) {
|
|
1257
|
-
event.preventDefault();
|
|
1258
|
-
touchState.selectionTriggered = true;
|
|
1259
|
-
const index = getSlotIndexFromPoint(container, event.clientX, event.clientY);
|
|
1260
|
-
if (index !== null) {
|
|
1261
|
-
handlersRef.current.updateSelectionRange(index);
|
|
1262
|
-
}
|
|
1263
|
-
suppressTriggerUntilRef.current = Date.now() + 500;
|
|
1264
|
-
handlersRef.current.finishSelection({
|
|
1265
|
-
fromTouch: true
|
|
2179
|
+
const from = wheelBaseRef.current == null ? current.start.valueOf() : wheelBaseRef.current;
|
|
2180
|
+
const base = createTimeWindow(_extends({
|
|
2181
|
+
start: from + steps * stepMs
|
|
2182
|
+
}, optionsRef.current)).start.valueOf();
|
|
2183
|
+
wheelBaseRef.current = base;
|
|
2184
|
+
samples.push({
|
|
2185
|
+
t: event.timeStamp,
|
|
2186
|
+
ms: base
|
|
1266
2187
|
});
|
|
2188
|
+
if (samples.length > 8) {
|
|
2189
|
+
samples.shift();
|
|
2190
|
+
}
|
|
2191
|
+
const projection = estimateVelocity(samples, event.timeStamp) * inertiaRef.current * WHEEL_INERTIA_SCALE;
|
|
2192
|
+
let target = base + projection;
|
|
2193
|
+
const lastTarget = visualTargetRef.current;
|
|
2194
|
+
if (samples.length > 1 && lastTarget != null && (target - lastTarget) * wheelDirectionRef.current < 0) {
|
|
2195
|
+
target = lastTarget;
|
|
2196
|
+
}
|
|
2197
|
+
glideToRef.current(target, projection ? inertiaRef.current : WHEEL_NOTCH_EASE_MS);
|
|
1267
2198
|
return;
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
touchState.selectionTriggered = true;
|
|
1277
|
-
suppressTriggerUntilRef.current = Date.now() + 500;
|
|
1278
|
-
handlersRef.current.selectSlots([slot]);
|
|
1279
|
-
handlersRef.current.showFormPopoverForSlots([slot]);
|
|
1280
|
-
handlersRef.current.onFreeSlotClick == null || handlersRef.current.onFreeSlotClick(slot);
|
|
1281
|
-
return;
|
|
1282
|
-
}
|
|
1283
|
-
handlersRef.current.resetGestureState();
|
|
1284
|
-
};
|
|
1285
|
-
const handleContextMenu = event => {
|
|
1286
|
-
var _touchStateRef$curren;
|
|
1287
|
-
if ((_touchStateRef$curren = touchStateRef.current) != null && _touchStateRef$curren.active || draggingRef.current) {
|
|
1288
|
-
event.preventDefault();
|
|
1289
|
-
}
|
|
1290
|
-
};
|
|
1291
|
-
const handleSelectStart = event => {
|
|
1292
|
-
var _touchStateRef$curren2;
|
|
1293
|
-
if (!((_touchStateRef$curren2 = touchStateRef.current) != null && _touchStateRef$curren2.active) && !draggingRef.current) {
|
|
1294
|
-
return;
|
|
1295
|
-
}
|
|
1296
|
-
event.preventDefault();
|
|
1297
|
-
};
|
|
1298
|
-
const handleTouchMove = event => {
|
|
1299
|
-
var _touchStateRef$curren3;
|
|
1300
|
-
if (!draggingRef.current && !((_touchStateRef$curren3 = touchStateRef.current) != null && _touchStateRef$curren3.active)) {
|
|
1301
|
-
return;
|
|
1302
|
-
}
|
|
1303
|
-
event.preventDefault();
|
|
1304
|
-
};
|
|
1305
|
-
const touchOptions = {
|
|
1306
|
-
passive: false,
|
|
1307
|
-
capture: true
|
|
1308
|
-
};
|
|
1309
|
-
container.addEventListener('pointerdown', handlePointerDown, {
|
|
1310
|
-
passive: true
|
|
1311
|
-
});
|
|
1312
|
-
container.addEventListener('pointermove', handlePointerMove, {
|
|
1313
|
-
passive: false
|
|
1314
|
-
});
|
|
1315
|
-
container.addEventListener('pointerup', handlePointerUp, {
|
|
1316
|
-
passive: false
|
|
1317
|
-
});
|
|
1318
|
-
container.addEventListener('pointercancel', handlePointerUp, {
|
|
2199
|
+
}
|
|
2200
|
+
if (steps) {
|
|
2201
|
+
moveToRef.current(current.start.valueOf() + steps * stepMs);
|
|
2202
|
+
}
|
|
2203
|
+
const residualMs = pxPerStep > 0 ? accumulatedPx / pxPerStep * stepMs : 0;
|
|
2204
|
+
setVisualTargetRef.current(baseWindowRef.current.start.valueOf() + residualMs);
|
|
2205
|
+
};
|
|
2206
|
+
node.addEventListener('wheel', onWheel, {
|
|
1319
2207
|
passive: false
|
|
1320
2208
|
});
|
|
1321
|
-
|
|
1322
|
-
container.addEventListener('contextmenu', handleContextMenu);
|
|
1323
|
-
container.addEventListener('selectstart', handleSelectStart);
|
|
1324
|
-
return () => {
|
|
1325
|
-
var _touchStateRef$curren4;
|
|
1326
|
-
releasePointerCapture((_touchStateRef$curren4 = touchStateRef.current) == null ? void 0 : _touchStateRef$curren4.pointerId);
|
|
1327
|
-
handlersRef.current.clearLongPress();
|
|
1328
|
-
handlersRef.current.resetGestureState();
|
|
1329
|
-
longPressTimerRef.current = null;
|
|
1330
|
-
container.removeEventListener('touchmove', handleTouchMove, touchOptions);
|
|
1331
|
-
};
|
|
2209
|
+
return () => node.removeEventListener('wheel', onWheel);
|
|
1332
2210
|
}, []);
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
handleMouseEnter,
|
|
1345
|
-
handleMouseUp,
|
|
1346
|
-
handleMouseLeave,
|
|
1347
|
-
handlePopoverOpenChange
|
|
2211
|
+
const toggleLocked = () => {
|
|
2212
|
+
const next = !isLockedRef.current;
|
|
2213
|
+
if (next) {
|
|
2214
|
+
stopGlideRef.current();
|
|
2215
|
+
}
|
|
2216
|
+
if (!lockedControlled) {
|
|
2217
|
+
setInnerLocked(next);
|
|
2218
|
+
}
|
|
2219
|
+
if (onLockedChangeRef.current) {
|
|
2220
|
+
onLockedChangeRef.current(next);
|
|
2221
|
+
}
|
|
1348
2222
|
};
|
|
2223
|
+
const labelTicks = ticks.filter(tick => tick.showLabel);
|
|
2224
|
+
const ariaBounds = {};
|
|
2225
|
+
if (rangeStartMs != null) {
|
|
2226
|
+
ariaBounds['aria-valuemin'] = rangeStartMs;
|
|
2227
|
+
}
|
|
2228
|
+
if (rangeEndMs != null) {
|
|
2229
|
+
ariaBounds['aria-valuemax'] = rangeEndMs - durationMs;
|
|
2230
|
+
}
|
|
2231
|
+
return /*#__PURE__*/jsxs("div", {
|
|
2232
|
+
ref: rootRef,
|
|
2233
|
+
className: classNames(style$2.root, style$2[`root-${_size}`], className, {
|
|
2234
|
+
[style$2['root-dragging']]: dragging,
|
|
2235
|
+
[style$2['root-resizing']]: !!resizeView,
|
|
2236
|
+
[style$2['root-locked']]: isLocked
|
|
2237
|
+
}),
|
|
2238
|
+
style: styleProp,
|
|
2239
|
+
children: [/*#__PURE__*/jsxs("div", {
|
|
2240
|
+
className: style$2.header,
|
|
2241
|
+
children: [/*#__PURE__*/jsx("div", {
|
|
2242
|
+
className: style$2.label,
|
|
2243
|
+
children: label
|
|
2244
|
+
}), /*#__PURE__*/jsxs("div", {
|
|
2245
|
+
className: style$2.actions,
|
|
2246
|
+
children: [_lockable ? /*#__PURE__*/jsx("button", {
|
|
2247
|
+
type: "button",
|
|
2248
|
+
role: "switch",
|
|
2249
|
+
"aria-checked": !isLocked,
|
|
2250
|
+
"aria-label": formatMessage({
|
|
2251
|
+
id: isLocked ? 'TimeWindow.locked' : 'TimeWindow.editable'
|
|
2252
|
+
}),
|
|
2253
|
+
className: classNames(style$2.switch, {
|
|
2254
|
+
[style$2['switch-on']]: !isLocked
|
|
2255
|
+
}),
|
|
2256
|
+
onClick: toggleLocked,
|
|
2257
|
+
children: /*#__PURE__*/jsx("i", {})
|
|
2258
|
+
}) : null, _showDuration ? /*#__PURE__*/jsx("span", {
|
|
2259
|
+
className: style$2.duration,
|
|
2260
|
+
children: durationText
|
|
2261
|
+
}) : null]
|
|
2262
|
+
})]
|
|
2263
|
+
}), isLocked ? null : /*#__PURE__*/jsxs("div", {
|
|
2264
|
+
className: style$2.scale,
|
|
2265
|
+
children: [/*#__PURE__*/jsxs("div", _extends({
|
|
2266
|
+
ref: trackRef,
|
|
2267
|
+
className: classNames(style$2.track, {
|
|
2268
|
+
[style$2['track-wheel']]: _wheelEffect
|
|
2269
|
+
}),
|
|
2270
|
+
role: "slider",
|
|
2271
|
+
tabIndex: 0
|
|
2272
|
+
}, ariaBounds, {
|
|
2273
|
+
"aria-valuenow": baseWindow.start.valueOf(),
|
|
2274
|
+
"aria-valuetext": ariaValueText,
|
|
2275
|
+
onKeyDown: onKeyDown,
|
|
2276
|
+
onPointerDown: onPointerDown,
|
|
2277
|
+
onPointerMove: onPointerMove,
|
|
2278
|
+
onPointerUp: endDrag,
|
|
2279
|
+
onPointerCancel: endDrag,
|
|
2280
|
+
children: [leftShade > 0 ? /*#__PURE__*/jsx("span", {
|
|
2281
|
+
className: classNames(style$2.shade, style$2['shade-start']),
|
|
2282
|
+
style: {
|
|
2283
|
+
width: `${leftShade}%`
|
|
2284
|
+
}
|
|
2285
|
+
}) : null, rightShade > 0 ? /*#__PURE__*/jsx("span", {
|
|
2286
|
+
className: classNames(style$2.shade, style$2['shade-end']),
|
|
2287
|
+
style: {
|
|
2288
|
+
width: `${rightShade}%`
|
|
2289
|
+
}
|
|
2290
|
+
}) : null, /*#__PURE__*/jsx("div", {
|
|
2291
|
+
className: style$2.ticks,
|
|
2292
|
+
children: ticks.map(tick => /*#__PURE__*/jsx("span", {
|
|
2293
|
+
className: classNames(style$2.tick, style$2[`tick-${tick.level}`], {
|
|
2294
|
+
[style$2['tick-out']]: tick.outOfRange
|
|
2295
|
+
}),
|
|
2296
|
+
style: {
|
|
2297
|
+
left: `${tick.ratio * 100}%`
|
|
2298
|
+
}
|
|
2299
|
+
}, tick.key))
|
|
2300
|
+
}), _wheelEffect ? /*#__PURE__*/jsx("span", {
|
|
2301
|
+
className: style$2.cylinder,
|
|
2302
|
+
"aria-hidden": "true"
|
|
2303
|
+
}) : null, /*#__PURE__*/jsx("span", {
|
|
2304
|
+
className: classNames(style$2.window, {
|
|
2305
|
+
[style$2['window-compact']]: windowPx < GRIP_MIN_WIDTH,
|
|
2306
|
+
[style$2['window-resizable']]: _resizable,
|
|
2307
|
+
[style$2['window-fixed']]: !_resizable
|
|
2308
|
+
}),
|
|
2309
|
+
style: {
|
|
2310
|
+
left: `${left}%`,
|
|
2311
|
+
width: `${width}%`
|
|
2312
|
+
},
|
|
2313
|
+
children: _resizable ? /*#__PURE__*/jsxs(Fragment, {
|
|
2314
|
+
children: [/*#__PURE__*/jsx("span", {
|
|
2315
|
+
"data-edge": "start",
|
|
2316
|
+
"aria-hidden": "true",
|
|
2317
|
+
className: classNames(style$2.edge, style$2['edge-start'], {
|
|
2318
|
+
[style$2['edge-active']]: resizeEdge === 'start'
|
|
2319
|
+
})
|
|
2320
|
+
}), /*#__PURE__*/jsx("span", {
|
|
2321
|
+
"data-edge": "end",
|
|
2322
|
+
"aria-hidden": "true",
|
|
2323
|
+
className: classNames(style$2.edge, style$2['edge-end'], {
|
|
2324
|
+
[style$2['edge-active']]: resizeEdge === 'end'
|
|
2325
|
+
})
|
|
2326
|
+
})]
|
|
2327
|
+
}) : null
|
|
2328
|
+
})]
|
|
2329
|
+
})), /*#__PURE__*/jsx("div", {
|
|
2330
|
+
className: style$2.labels,
|
|
2331
|
+
children: labelTicks.map(tick => /*#__PURE__*/jsx("span", {
|
|
2332
|
+
className: classNames(style$2['tick-label'], style$2[`tick-label-${tick.level}`], {
|
|
2333
|
+
[style$2['tick-out']]: tick.outOfRange
|
|
2334
|
+
}),
|
|
2335
|
+
style: {
|
|
2336
|
+
left: `${tick.ratio * 100}%`
|
|
2337
|
+
},
|
|
2338
|
+
children: tick.label
|
|
2339
|
+
}, tick.key))
|
|
2340
|
+
})]
|
|
2341
|
+
})]
|
|
2342
|
+
});
|
|
1349
2343
|
};
|
|
2344
|
+
const TimeWindow = withLocale(TimeWindowInner);
|
|
1350
2345
|
|
|
1351
2346
|
const DefaultFormInner = () => {
|
|
1352
2347
|
const {
|
|
@@ -1377,186 +2372,620 @@ const DefaultFormInner = () => {
|
|
|
1377
2372
|
});
|
|
1378
2373
|
};
|
|
1379
2374
|
|
|
1380
|
-
var style$1 = {"slots":"kne-react-calendar_Ftp-V","
|
|
2375
|
+
var style$1 = {"kne-responsive-boundary":"kne-react-calendar_U1upa","kne-responsive-scroll":"kne-react-calendar_vKocI","slots":"kne-react-calendar_Ftp-V","timeline":"kne-react-calendar_EaUED","timeline-axis":"kne-react-calendar_X2W6x","timeline-axis-label":"kne-react-calendar_NvxDW","timeline-grid":"kne-react-calendar_gTav1","timeline-grid-dragging":"kne-react-calendar_4qf3-","timeline-line":"kne-react-calendar_Tp2ES","timeline-line-hour":"kne-react-calendar_M5-iS","timeline-disabled":"kne-react-calendar_-g-DR","timeline-disabled-reason":"kne-react-calendar_3vFqg","timeline-event":"kne-react-calendar_Wd-9L","timeline-event-title":"kne-react-calendar_WKbMK","timeline-event-time":"kne-react-calendar_K98yc","timeline-event-compact":"kne-react-calendar_WrUNC","timeline-event-disabled":"kne-react-calendar_C4d2-","timeline-ghost":"kne-react-calendar_0T5mO","timeline-now":"kne-react-calendar_AzaVv","timeline-popover-anchor":"kne-react-calendar_WtBzl","timeline-selection":"kne-react-calendar_z-uOR","timeline-selection-dragging":"kne-react-calendar_MbeJu","timeline-selection-label":"kne-react-calendar_7nYLD","timeline-selection-compact":"kne-react-calendar_CpsWx","timeline-selection-badge":"kne-react-calendar_NqiWA","timeline-selection-conflict":"kne-react-calendar_XQQ3E","timeline-selection-edge":"kne-react-calendar_s2EvF","timeline-selection-edge-start":"kne-react-calendar_PcIon","timeline-selection-edge-end":"kne-react-calendar_nS8Mr","form-popover":"kne-react-calendar_W-I5J","schedule-popover":"kne-react-calendar_rVLO8","form-popover-title":"kne-react-calendar_mMpFQ","form-sheet":"kne-react-calendar_n-MRC","form-sheet-content":"kne-react-calendar_ZaEkY","form-actions":"kne-react-calendar_4NPv0","form-conflict":"kne-react-calendar_PpU92","form-time-window":"kne-react-calendar_Wd4qh","event-detail":"kne-react-calendar_Dc-t3","event-detail-clickable":"kne-react-calendar_C3waU","event-detail-title":"kne-react-calendar_FkbSR","event-detail-disabled":"kne-react-calendar_UsltM","event-detail-bar":"kne-react-calendar_ZETY4","event-detail-body":"kne-react-calendar_v9RjG","event-detail-time":"kne-react-calendar_NT-kw","event-detail-text":"kne-react-calendar_vOb62","event-detail-tag":"kne-react-calendar_rs8bh"};
|
|
1381
2376
|
|
|
1382
|
-
const
|
|
1383
|
-
|
|
2377
|
+
const EventDetail = ({
|
|
2378
|
+
event,
|
|
1384
2379
|
date,
|
|
1385
2380
|
onEventClick
|
|
1386
|
-
}) =>
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
2381
|
+
}) => {
|
|
2382
|
+
const clickable = !event.disabled && (typeof event.onClick === 'function' || typeof onEventClick === 'function');
|
|
2383
|
+
const handleClick = () => {
|
|
2384
|
+
if (typeof event.onClick === 'function') {
|
|
2385
|
+
event.onClick(event, {
|
|
2386
|
+
date,
|
|
2387
|
+
mode: 'schedule'
|
|
2388
|
+
});
|
|
2389
|
+
return;
|
|
2390
|
+
}
|
|
2391
|
+
onEventClick == null || onEventClick(event, {
|
|
2392
|
+
date,
|
|
2393
|
+
mode: 'schedule'
|
|
2394
|
+
});
|
|
2395
|
+
};
|
|
2396
|
+
const tagLabel = getEventTagLabel(event);
|
|
2397
|
+
return /*#__PURE__*/jsxs("div", {
|
|
2398
|
+
className: classNames(style$1['event-detail'], {
|
|
2399
|
+
[style$1['event-detail-clickable']]: clickable,
|
|
2400
|
+
[style$1['event-detail-disabled']]: event.disabled
|
|
2401
|
+
}),
|
|
2402
|
+
style: event.color ? {
|
|
2403
|
+
'--timeline-event-color': event.color
|
|
2404
|
+
} : undefined,
|
|
2405
|
+
role: clickable ? 'button' : undefined,
|
|
2406
|
+
tabIndex: clickable ? 0 : undefined,
|
|
2407
|
+
onClick: clickable ? handleClick : undefined,
|
|
2408
|
+
onKeyDown: clickable ? keyEvent => {
|
|
2409
|
+
if (keyEvent.key === 'Enter' || keyEvent.key === ' ') {
|
|
2410
|
+
keyEvent.preventDefault();
|
|
2411
|
+
handleClick();
|
|
2412
|
+
}
|
|
2413
|
+
} : undefined,
|
|
2414
|
+
children: [/*#__PURE__*/jsx("span", {
|
|
2415
|
+
className: style$1['event-detail-bar']
|
|
2416
|
+
}), /*#__PURE__*/jsxs("div", {
|
|
2417
|
+
className: style$1['event-detail-body'],
|
|
2418
|
+
children: [/*#__PURE__*/jsx("div", {
|
|
2419
|
+
className: style$1['event-detail-title'],
|
|
2420
|
+
children: event.title
|
|
2421
|
+
}), /*#__PURE__*/jsx("div", {
|
|
2422
|
+
className: style$1['event-detail-time'],
|
|
2423
|
+
children: formatRange(event.start, event.end)
|
|
2424
|
+
}), event.detail ? /*#__PURE__*/jsx("div", {
|
|
2425
|
+
className: style$1['event-detail-text'],
|
|
2426
|
+
children: event.detail
|
|
2427
|
+
}) : null, tagLabel ? /*#__PURE__*/jsx(Tag, {
|
|
2428
|
+
className: style$1['event-detail-tag'],
|
|
2429
|
+
color: event.color,
|
|
2430
|
+
children: tagLabel
|
|
2431
|
+
}) : null]
|
|
2432
|
+
})]
|
|
2433
|
+
});
|
|
2434
|
+
};
|
|
1399
2435
|
|
|
1400
|
-
const
|
|
1401
|
-
|
|
2436
|
+
const DRAG_THRESHOLD_PX = 4;
|
|
2437
|
+
const TOUCH_SLOP_PX = 8;
|
|
2438
|
+
const LONG_PRESS_MS = 350;
|
|
2439
|
+
const Timeline = ({
|
|
1402
2440
|
date,
|
|
1403
|
-
|
|
2441
|
+
day,
|
|
2442
|
+
hourHeight,
|
|
2443
|
+
timeUnit,
|
|
2444
|
+
timeStep,
|
|
2445
|
+
eventItems,
|
|
2446
|
+
disabledRanges,
|
|
2447
|
+
blockers,
|
|
2448
|
+
conflicts,
|
|
2449
|
+
allowMultiSelect,
|
|
2450
|
+
selection,
|
|
2451
|
+
dragging,
|
|
1404
2452
|
formPopover,
|
|
1405
2453
|
popoverOpen,
|
|
2454
|
+
popoverPlacement,
|
|
2455
|
+
popoverAnchor,
|
|
1406
2456
|
onPopoverOpenChange,
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
onMouseEnter,
|
|
1411
|
-
onMouseUp,
|
|
1412
|
-
onClick,
|
|
2457
|
+
onSelectionDrag,
|
|
2458
|
+
onSelectionCommit,
|
|
2459
|
+
onFreeSlotClick,
|
|
1413
2460
|
onEventClick,
|
|
1414
|
-
|
|
1415
|
-
renderOccupiedSlot
|
|
2461
|
+
renderEventBlock,
|
|
2462
|
+
renderOccupiedSlot,
|
|
2463
|
+
interactionRef,
|
|
2464
|
+
isMobile
|
|
1416
2465
|
}) => {
|
|
1417
|
-
const
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
const
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
2466
|
+
const gridRef = useRef(null);
|
|
2467
|
+
const gestureRef = useRef(null);
|
|
2468
|
+
const suppressClickRef = useRef(false);
|
|
2469
|
+
const touchCommitRef = useRef(0);
|
|
2470
|
+
const [hover, setHover] = useState(null);
|
|
2471
|
+
const [activeEventKey, setActiveEventKey] = useState(null);
|
|
2472
|
+
const pxPerMs = hourHeight / (60 * MINUTE_MS$1);
|
|
2473
|
+
const height = (day.endMs - day.startMs) * pxPerMs;
|
|
2474
|
+
const unitMs = timeUnit * MINUTE_MS$1;
|
|
2475
|
+
const stepMs = timeStep * MINUTE_MS$1;
|
|
2476
|
+
const latest = useRef({});
|
|
2477
|
+
latest.current = {
|
|
2478
|
+
day,
|
|
2479
|
+
blockers,
|
|
2480
|
+
unitMs,
|
|
2481
|
+
stepMs,
|
|
2482
|
+
allowMultiSelect,
|
|
2483
|
+
selection,
|
|
2484
|
+
onSelectionDrag,
|
|
2485
|
+
onSelectionCommit,
|
|
2486
|
+
onFreeSlotClick,
|
|
2487
|
+
pxPerMs
|
|
2488
|
+
};
|
|
2489
|
+
const toY = ms => (ms - day.startMs) * pxPerMs;
|
|
2490
|
+
const lines = useMemo(() => {
|
|
2491
|
+
const list = [];
|
|
2492
|
+
for (let ms = day.startMs; ms <= day.endMs; ms += unitMs) {
|
|
2493
|
+
list.push({
|
|
2494
|
+
ms,
|
|
2495
|
+
hour: dayjs(ms).minute() === 0
|
|
1445
2496
|
});
|
|
1446
2497
|
}
|
|
1447
|
-
return
|
|
1448
|
-
}
|
|
1449
|
-
const
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
2498
|
+
return list;
|
|
2499
|
+
}, [day.endMs, day.startMs, unitMs]);
|
|
2500
|
+
const labels = useMemo(() => {
|
|
2501
|
+
const list = [];
|
|
2502
|
+
let cursor = dayjs(day.startMs);
|
|
2503
|
+
if (cursor.minute() !== 0) {
|
|
2504
|
+
list.push(day.startMs);
|
|
2505
|
+
cursor = cursor.add(1, 'hour').startOf('hour');
|
|
2506
|
+
}
|
|
2507
|
+
for (; cursor.valueOf() <= day.endMs; cursor = cursor.add(1, 'hour')) {
|
|
2508
|
+
list.push(cursor.valueOf());
|
|
2509
|
+
}
|
|
2510
|
+
if (list[list.length - 1] !== day.endMs) {
|
|
2511
|
+
list.push(day.endMs);
|
|
2512
|
+
}
|
|
2513
|
+
return list;
|
|
2514
|
+
}, [day.endMs, day.startMs]);
|
|
2515
|
+
const [now, setNow] = useState(() => Date.now());
|
|
2516
|
+
useEffect(() => {
|
|
2517
|
+
const timer = setInterval(() => setNow(Date.now()), 60 * 1000);
|
|
2518
|
+
return () => clearInterval(timer);
|
|
2519
|
+
}, []);
|
|
2520
|
+
const showNow = now >= day.startMs && now <= day.endMs;
|
|
2521
|
+
const msAtClientY = clientY => {
|
|
2522
|
+
const rect = gridRef.current.getBoundingClientRect();
|
|
2523
|
+
const {
|
|
2524
|
+
day: currentDay,
|
|
2525
|
+
pxPerMs: currentPx
|
|
2526
|
+
} = latest.current;
|
|
2527
|
+
return Math.min(Math.max(currentDay.startMs + (clientY - rect.top) / currentPx, currentDay.startMs), currentDay.endMs - 1);
|
|
2528
|
+
};
|
|
2529
|
+
const finishGesture = () => {
|
|
2530
|
+
const gesture = gestureRef.current;
|
|
2531
|
+
if (!gesture) {
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
clearTimeout(gesture.timer);
|
|
2535
|
+
window.removeEventListener('pointermove', gesture.onMove);
|
|
2536
|
+
window.removeEventListener('pointerup', gesture.onUp);
|
|
2537
|
+
window.removeEventListener('pointercancel', gesture.onCancel);
|
|
2538
|
+
gestureRef.current = null;
|
|
2539
|
+
};
|
|
2540
|
+
useEffect(() => finishGesture, []);
|
|
2541
|
+
useEffect(() => {
|
|
2542
|
+
const grid = gridRef.current;
|
|
2543
|
+
if (!grid) {
|
|
2544
|
+
return undefined;
|
|
2545
|
+
}
|
|
2546
|
+
const preventScroll = event => {
|
|
2547
|
+
var _gestureRef$current;
|
|
2548
|
+
if ((_gestureRef$current = gestureRef.current) != null && _gestureRef$current.active) {
|
|
2549
|
+
event.preventDefault();
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
const preventGhostClick = event => {
|
|
2553
|
+
if (Date.now() - touchCommitRef.current < 300) {
|
|
2554
|
+
event.preventDefault();
|
|
2555
|
+
}
|
|
2556
|
+
};
|
|
2557
|
+
grid.addEventListener('touchmove', preventScroll, {
|
|
2558
|
+
passive: false
|
|
2559
|
+
});
|
|
2560
|
+
grid.addEventListener('touchend', preventGhostClick, {
|
|
2561
|
+
passive: false
|
|
2562
|
+
});
|
|
2563
|
+
return () => {
|
|
2564
|
+
grid.removeEventListener('touchmove', preventScroll);
|
|
2565
|
+
grid.removeEventListener('touchend', preventGhostClick);
|
|
2566
|
+
};
|
|
2567
|
+
}, []);
|
|
2568
|
+
const handlePointerDown = event => {
|
|
2569
|
+
var _gridRef$current;
|
|
2570
|
+
if (!((_gridRef$current = gridRef.current) != null && _gridRef$current.contains(event.target))) {
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
if (interactionRef) {
|
|
2574
|
+
interactionRef.current = Date.now();
|
|
2575
|
+
}
|
|
2576
|
+
if (event.pointerType === 'mouse' && event.button !== 0) {
|
|
2577
|
+
return;
|
|
2578
|
+
}
|
|
2579
|
+
const {
|
|
2580
|
+
day: currentDay,
|
|
2581
|
+
blockers: currentBlockers,
|
|
2582
|
+
selection: currentSelection,
|
|
2583
|
+
allowMultiSelect: multi
|
|
2584
|
+
} = latest.current;
|
|
2585
|
+
const edgeNode = event.target.closest('[data-selection-edge]');
|
|
2586
|
+
const onSelection = event.target.closest('[data-timeline-selection]');
|
|
2587
|
+
const eventNode = event.target.closest('[data-timeline-event]');
|
|
2588
|
+
const pointerMs = msAtClientY(event.clientY);
|
|
2589
|
+
let gesture;
|
|
2590
|
+
if (edgeNode && currentSelection && multi) {
|
|
2591
|
+
gesture = {
|
|
2592
|
+
type: 'resize',
|
|
2593
|
+
edge: edgeNode.dataset.selectionEdge,
|
|
2594
|
+
active: true
|
|
2595
|
+
};
|
|
2596
|
+
} else if (onSelection && currentSelection) {
|
|
2597
|
+
gesture = {
|
|
2598
|
+
type: 'move',
|
|
2599
|
+
active: true
|
|
2600
|
+
};
|
|
2601
|
+
} else {
|
|
2602
|
+
const gap = getFreeGap(pointerMs, currentBlockers, currentDay);
|
|
2603
|
+
if (!gap) {
|
|
2604
|
+
return;
|
|
2605
|
+
}
|
|
2606
|
+
gesture = {
|
|
2607
|
+
type: 'create',
|
|
2608
|
+
gap,
|
|
2609
|
+
active: event.pointerType === 'mouse',
|
|
2610
|
+
onEvent: !!eventNode
|
|
2611
|
+
};
|
|
2612
|
+
}
|
|
2613
|
+
if (gesture.type !== 'create') {
|
|
2614
|
+
const ownGap = getFreeGap(currentSelection.startMs, currentBlockers, currentDay) || currentDay;
|
|
2615
|
+
gesture.gap = ownGap;
|
|
2616
|
+
gesture.range0 = currentSelection;
|
|
2617
|
+
event.preventDefault();
|
|
2618
|
+
}
|
|
2619
|
+
Object.assign(gesture, {
|
|
2620
|
+
pointerId: event.pointerId,
|
|
2621
|
+
pointerType: event.pointerType,
|
|
2622
|
+
startX: event.clientX,
|
|
2623
|
+
startY: event.clientY,
|
|
2624
|
+
startMs: pointerMs,
|
|
2625
|
+
moved: false
|
|
2626
|
+
});
|
|
2627
|
+
setHover(null);
|
|
2628
|
+
gesture.onMove = moveEvent => {
|
|
2629
|
+
if (moveEvent.pointerId !== gesture.pointerId) {
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
const dx = moveEvent.clientX - gesture.startX;
|
|
2633
|
+
const dy = moveEvent.clientY - gesture.startY;
|
|
2634
|
+
if (!gesture.active) {
|
|
2635
|
+
if (Math.hypot(dx, dy) > TOUCH_SLOP_PX) {
|
|
2636
|
+
finishGesture();
|
|
2637
|
+
}
|
|
2638
|
+
return;
|
|
2639
|
+
}
|
|
2640
|
+
if (!gesture.moved && Math.abs(dy) < DRAG_THRESHOLD_PX) {
|
|
2641
|
+
return;
|
|
2642
|
+
}
|
|
2643
|
+
const {
|
|
2644
|
+
stepMs: step,
|
|
2645
|
+
day: d,
|
|
2646
|
+
allowMultiSelect: canResize
|
|
2647
|
+
} = latest.current;
|
|
2648
|
+
const ms = msAtClientY(moveEvent.clientY);
|
|
2649
|
+
let next;
|
|
2650
|
+
if (gesture.type === 'create') {
|
|
2651
|
+
if (!canResize) {
|
|
2652
|
+
return;
|
|
2653
|
+
}
|
|
2654
|
+
next = createSelectionRange({
|
|
2655
|
+
anchorMs: gesture.startMs,
|
|
2656
|
+
pointerMs: ms,
|
|
2657
|
+
gap: gesture.gap,
|
|
2658
|
+
originMs: d.startMs,
|
|
2659
|
+
stepMs: step
|
|
2660
|
+
});
|
|
2661
|
+
} else if (gesture.type === 'move') {
|
|
2662
|
+
next = moveSelectionRange({
|
|
2663
|
+
range: gesture.range0,
|
|
2664
|
+
deltaMs: ms - gesture.startMs,
|
|
2665
|
+
gap: gesture.gap,
|
|
2666
|
+
originMs: d.startMs,
|
|
2667
|
+
stepMs: step
|
|
2668
|
+
});
|
|
2669
|
+
} else {
|
|
2670
|
+
next = resizeSelectionRange({
|
|
2671
|
+
range: gesture.range0,
|
|
2672
|
+
edge: gesture.edge,
|
|
2673
|
+
pointerMs: ms,
|
|
2674
|
+
gap: gesture.gap,
|
|
2675
|
+
originMs: d.startMs,
|
|
2676
|
+
stepMs: step
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
2679
|
+
gesture.moved = true;
|
|
2680
|
+
gesture.range = next;
|
|
2681
|
+
latest.current.onSelectionDrag(next);
|
|
2682
|
+
};
|
|
2683
|
+
gesture.onUp = upEvent => {
|
|
2684
|
+
if (upEvent.pointerId !== gesture.pointerId) {
|
|
2685
|
+
return;
|
|
2686
|
+
}
|
|
2687
|
+
finishGesture();
|
|
2688
|
+
if (gesture.pointerType !== 'mouse') {
|
|
2689
|
+
touchCommitRef.current = Date.now();
|
|
2690
|
+
}
|
|
2691
|
+
const {
|
|
2692
|
+
unitMs: unit,
|
|
2693
|
+
day: d,
|
|
2694
|
+
onSelectionCommit: commit,
|
|
2695
|
+
onFreeSlotClick: onFreeClick
|
|
2696
|
+
} = latest.current;
|
|
2697
|
+
if (gesture.moved) {
|
|
2698
|
+
suppressClickRef.current = true;
|
|
2699
|
+
setTimeout(() => {
|
|
2700
|
+
suppressClickRef.current = false;
|
|
2701
|
+
}, 0);
|
|
2702
|
+
commit(gesture.range);
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2705
|
+
if (gesture.type !== 'create') {
|
|
2706
|
+
commit(gesture.range0);
|
|
2707
|
+
return;
|
|
2708
|
+
}
|
|
2709
|
+
if (gesture.onEvent) {
|
|
2710
|
+
return;
|
|
2711
|
+
}
|
|
2712
|
+
const range = clickSelectionRange({
|
|
2713
|
+
pointerMs: gesture.startMs,
|
|
2714
|
+
gap: gesture.gap,
|
|
2715
|
+
originMs: d.startMs,
|
|
2716
|
+
unitMs: unit
|
|
2717
|
+
});
|
|
2718
|
+
const slotStart = snapToStep(gesture.startMs, {
|
|
2719
|
+
originMs: d.startMs,
|
|
2720
|
+
stepMs: unit,
|
|
2721
|
+
mode: 'floor'
|
|
2722
|
+
});
|
|
2723
|
+
onFreeClick == null || onFreeClick({
|
|
2724
|
+
start: new Date(slotStart),
|
|
2725
|
+
end: new Date(Math.min(slotStart + unit, d.endMs))
|
|
1460
2726
|
});
|
|
2727
|
+
commit(range);
|
|
2728
|
+
};
|
|
2729
|
+
gesture.onCancel = cancelEvent => {
|
|
2730
|
+
if (cancelEvent.pointerId !== gesture.pointerId) {
|
|
2731
|
+
return;
|
|
2732
|
+
}
|
|
2733
|
+
const wasMoved = gesture.moved;
|
|
2734
|
+
finishGesture();
|
|
2735
|
+
if (wasMoved) {
|
|
2736
|
+
latest.current.onSelectionCommit(gesture.range);
|
|
2737
|
+
}
|
|
2738
|
+
};
|
|
2739
|
+
if (!gesture.active) {
|
|
2740
|
+
gesture.timer = setTimeout(() => {
|
|
2741
|
+
gesture.active = true;
|
|
2742
|
+
navigator.vibrate == null || navigator.vibrate(10);
|
|
2743
|
+
const {
|
|
2744
|
+
day: d,
|
|
2745
|
+
unitMs: unit
|
|
2746
|
+
} = latest.current;
|
|
2747
|
+
const range = clickSelectionRange({
|
|
2748
|
+
pointerMs: gesture.startMs,
|
|
2749
|
+
gap: gesture.gap,
|
|
2750
|
+
originMs: d.startMs,
|
|
2751
|
+
unitMs: unit
|
|
2752
|
+
});
|
|
2753
|
+
gesture.moved = true;
|
|
2754
|
+
gesture.range = range;
|
|
2755
|
+
gesture.startMs = range.startMs;
|
|
2756
|
+
latest.current.onSelectionDrag(range);
|
|
2757
|
+
}, LONG_PRESS_MS);
|
|
1461
2758
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
2759
|
+
finishGesture();
|
|
2760
|
+
gestureRef.current = gesture;
|
|
2761
|
+
window.addEventListener('pointermove', gesture.onMove);
|
|
2762
|
+
window.addEventListener('pointerup', gesture.onUp);
|
|
2763
|
+
window.addEventListener('pointercancel', gesture.onCancel);
|
|
2764
|
+
};
|
|
2765
|
+
const handleHover = event => {
|
|
2766
|
+
var _gridRef$current2;
|
|
2767
|
+
if (event.pointerType !== 'mouse' || gestureRef.current || !((_gridRef$current2 = gridRef.current) != null && _gridRef$current2.contains(event.target))) {
|
|
2768
|
+
return;
|
|
2769
|
+
}
|
|
2770
|
+
if (event.target.closest('[data-timeline-selection]')) {
|
|
2771
|
+
setHover(null);
|
|
2772
|
+
return;
|
|
2773
|
+
}
|
|
2774
|
+
const ms = msAtClientY(event.clientY);
|
|
2775
|
+
const gap = getFreeGap(ms, blockers, day);
|
|
2776
|
+
if (!gap) {
|
|
2777
|
+
setHover(null);
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2780
|
+
const range = clickSelectionRange({
|
|
2781
|
+
pointerMs: ms,
|
|
2782
|
+
gap,
|
|
2783
|
+
originMs: day.startMs,
|
|
2784
|
+
unitMs
|
|
2785
|
+
});
|
|
2786
|
+
setHover(current => current && current.startMs === range.startMs && current.endMs === range.endMs ? current : range);
|
|
2787
|
+
};
|
|
2788
|
+
const renderEventContent = item => {
|
|
2789
|
+
const {
|
|
2790
|
+
event
|
|
2791
|
+
} = item;
|
|
2792
|
+
if (renderEventBlock) {
|
|
2793
|
+
return renderEventBlock(event, {
|
|
2794
|
+
start: new Date(item.startMs),
|
|
2795
|
+
end: new Date(item.endMs)
|
|
1476
2796
|
});
|
|
1477
2797
|
}
|
|
1478
|
-
if (
|
|
1479
|
-
return
|
|
1480
|
-
|
|
1481
|
-
|
|
2798
|
+
if (renderOccupiedSlot) {
|
|
2799
|
+
return renderOccupiedSlot({
|
|
2800
|
+
start: new Date(item.startMs),
|
|
2801
|
+
end: new Date(item.endMs),
|
|
2802
|
+
status: 'occupied',
|
|
2803
|
+
events: [event]
|
|
2804
|
+
}, {
|
|
2805
|
+
events: [event]
|
|
1482
2806
|
});
|
|
1483
2807
|
}
|
|
1484
|
-
return
|
|
2808
|
+
return /*#__PURE__*/jsxs(Fragment, {
|
|
2809
|
+
children: [/*#__PURE__*/jsx("div", {
|
|
2810
|
+
className: style$1['timeline-event-title'],
|
|
2811
|
+
children: event.title
|
|
2812
|
+
}), /*#__PURE__*/jsx("div", {
|
|
2813
|
+
className: style$1['timeline-event-time'],
|
|
2814
|
+
children: formatRange(event.start, event.end)
|
|
2815
|
+
})]
|
|
2816
|
+
});
|
|
1485
2817
|
};
|
|
1486
|
-
const
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}),
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
}) : null,
|
|
1501
|
-
className: style$1['
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
})
|
|
1509
|
-
|
|
1510
|
-
|
|
2818
|
+
const selectionBlock = selection ? /*#__PURE__*/jsxs("div", {
|
|
2819
|
+
"data-timeline-selection": "",
|
|
2820
|
+
className: classNames(style$1['timeline-selection'], {
|
|
2821
|
+
[style$1['timeline-selection-dragging']]: dragging,
|
|
2822
|
+
[style$1['timeline-selection-conflict']]: conflicts.length > 0,
|
|
2823
|
+
[style$1['timeline-selection-compact']]: (selection.endMs - selection.startMs) * pxPerMs < 34
|
|
2824
|
+
}),
|
|
2825
|
+
style: {
|
|
2826
|
+
top: toY(selection.startMs),
|
|
2827
|
+
height: Math.max((selection.endMs - selection.startMs) * pxPerMs, 6)
|
|
2828
|
+
},
|
|
2829
|
+
children: [allowMultiSelect ? /*#__PURE__*/jsx("span", {
|
|
2830
|
+
"data-selection-edge": "start",
|
|
2831
|
+
className: classNames(style$1['timeline-selection-edge'], style$1['timeline-selection-edge-start'])
|
|
2832
|
+
}) : null, /*#__PURE__*/jsxs("div", {
|
|
2833
|
+
className: style$1['timeline-selection-label'],
|
|
2834
|
+
children: [/*#__PURE__*/jsx("span", {
|
|
2835
|
+
children: formatRange(selection.startMs, selection.endMs)
|
|
2836
|
+
}), conflicts.length ? /*#__PURE__*/jsx("span", {
|
|
2837
|
+
className: style$1['timeline-selection-badge'],
|
|
2838
|
+
children: conflicts.length
|
|
2839
|
+
}) : null]
|
|
2840
|
+
}), allowMultiSelect ? /*#__PURE__*/jsx("span", {
|
|
2841
|
+
"data-selection-edge": "end",
|
|
2842
|
+
className: classNames(style$1['timeline-selection-edge'], style$1['timeline-selection-edge-end'])
|
|
1511
2843
|
}) : null]
|
|
2844
|
+
}) : null;
|
|
2845
|
+
return /*#__PURE__*/jsxs("div", {
|
|
2846
|
+
className: style$1['timeline'],
|
|
2847
|
+
style: {
|
|
2848
|
+
height
|
|
2849
|
+
},
|
|
2850
|
+
children: [/*#__PURE__*/jsx("div", {
|
|
2851
|
+
className: style$1['timeline-axis'],
|
|
2852
|
+
"aria-hidden": true,
|
|
2853
|
+
children: labels.map(ms => /*#__PURE__*/jsx("span", {
|
|
2854
|
+
className: style$1['timeline-axis-label'],
|
|
2855
|
+
style: {
|
|
2856
|
+
top: toY(ms)
|
|
2857
|
+
},
|
|
2858
|
+
children: dayjs(ms).format(TIME_FORMAT)
|
|
2859
|
+
}, ms))
|
|
2860
|
+
}), /*#__PURE__*/jsxs("div", {
|
|
2861
|
+
ref: gridRef,
|
|
2862
|
+
className: classNames(style$1['timeline-grid'], {
|
|
2863
|
+
[style$1['timeline-grid-dragging']]: dragging
|
|
2864
|
+
}),
|
|
2865
|
+
onPointerDown: handlePointerDown,
|
|
2866
|
+
onPointerMove: handleHover,
|
|
2867
|
+
onPointerLeave: () => setHover(null),
|
|
2868
|
+
onContextMenu: event => {
|
|
2869
|
+
var _gridRef$current3;
|
|
2870
|
+
if ((_gridRef$current3 = gridRef.current) != null && _gridRef$current3.contains(event.target)) {
|
|
2871
|
+
event.preventDefault();
|
|
2872
|
+
}
|
|
2873
|
+
},
|
|
2874
|
+
onClickCapture: event => {
|
|
2875
|
+
var _gridRef$current4;
|
|
2876
|
+
if (suppressClickRef.current && (_gridRef$current4 = gridRef.current) != null && _gridRef$current4.contains(event.target)) {
|
|
2877
|
+
suppressClickRef.current = false;
|
|
2878
|
+
event.stopPropagation();
|
|
2879
|
+
}
|
|
2880
|
+
},
|
|
2881
|
+
children: [lines.map(line => /*#__PURE__*/jsx("span", {
|
|
2882
|
+
className: classNames(style$1['timeline-line'], {
|
|
2883
|
+
[style$1['timeline-line-hour']]: line.hour
|
|
2884
|
+
}),
|
|
2885
|
+
style: {
|
|
2886
|
+
top: toY(line.ms)
|
|
2887
|
+
}
|
|
2888
|
+
}, line.ms)), disabledRanges.map(range => {
|
|
2889
|
+
const block = /*#__PURE__*/jsx("div", {
|
|
2890
|
+
className: style$1['timeline-disabled'],
|
|
2891
|
+
style: {
|
|
2892
|
+
top: toY(range.startMs),
|
|
2893
|
+
height: (range.endMs - range.startMs) * pxPerMs
|
|
2894
|
+
},
|
|
2895
|
+
children: range.reason && (range.endMs - range.startMs) * pxPerMs >= 22 ? /*#__PURE__*/jsx("span", {
|
|
2896
|
+
className: style$1['timeline-disabled-reason'],
|
|
2897
|
+
children: range.reason
|
|
2898
|
+
}) : null
|
|
2899
|
+
}, range.startMs);
|
|
2900
|
+
return range.reason ? /*#__PURE__*/jsx(Tooltip, {
|
|
2901
|
+
title: range.reason,
|
|
2902
|
+
children: block
|
|
2903
|
+
}, range.startMs) : block;
|
|
2904
|
+
}), eventItems.map(item => {
|
|
2905
|
+
const key = `${item.event.id}-${item.startMs}`;
|
|
2906
|
+
const blockHeight = (item.endMs - item.startMs) * pxPerMs;
|
|
2907
|
+
return /*#__PURE__*/jsx(Popover, {
|
|
2908
|
+
arrow: false,
|
|
2909
|
+
trigger: "click",
|
|
2910
|
+
placement: isMobile ? 'bottom' : 'left',
|
|
2911
|
+
rootClassName: style$1['schedule-popover'],
|
|
2912
|
+
open: activeEventKey === key,
|
|
2913
|
+
onOpenChange: open => setActiveEventKey(open ? key : null),
|
|
2914
|
+
content: /*#__PURE__*/jsx(EventDetail, {
|
|
2915
|
+
event: item.event,
|
|
2916
|
+
date: date,
|
|
2917
|
+
onEventClick: onEventClick
|
|
2918
|
+
}),
|
|
2919
|
+
children: /*#__PURE__*/jsx("div", {
|
|
2920
|
+
"data-timeline-event": "",
|
|
2921
|
+
className: classNames(style$1['timeline-event'], {
|
|
2922
|
+
[style$1['timeline-event-disabled']]: item.event.disabled,
|
|
2923
|
+
[style$1['timeline-event-compact']]: blockHeight < 34
|
|
2924
|
+
}),
|
|
2925
|
+
style: _extends({
|
|
2926
|
+
top: toY(item.startMs),
|
|
2927
|
+
height: Math.max(blockHeight - 2, 4),
|
|
2928
|
+
left: `calc(${item.column / item.columns * 100}% + 2px)`,
|
|
2929
|
+
width: `calc(${100 / item.columns}% - 4px)`
|
|
2930
|
+
}, item.event.color ? {
|
|
2931
|
+
'--timeline-event-color': item.event.color
|
|
2932
|
+
} : null),
|
|
2933
|
+
children: renderEventContent(item)
|
|
2934
|
+
})
|
|
2935
|
+
}, key);
|
|
2936
|
+
}), hover && !selection ? /*#__PURE__*/jsxs("div", {
|
|
2937
|
+
className: style$1['timeline-ghost'],
|
|
2938
|
+
style: {
|
|
2939
|
+
top: toY(hover.startMs),
|
|
2940
|
+
height: (hover.endMs - hover.startMs) * pxPerMs
|
|
2941
|
+
},
|
|
2942
|
+
children: ["+ ", dayjs(hover.startMs).format(TIME_FORMAT)]
|
|
2943
|
+
}) : null, showNow ? /*#__PURE__*/jsx("span", {
|
|
2944
|
+
className: style$1['timeline-now'],
|
|
2945
|
+
style: {
|
|
2946
|
+
top: toY(now)
|
|
2947
|
+
}
|
|
2948
|
+
}) : null, selectionBlock, formPopover && popoverAnchor ? /*#__PURE__*/jsx(Popover, {
|
|
2949
|
+
arrow: false,
|
|
2950
|
+
trigger: "click",
|
|
2951
|
+
placement: popoverPlacement,
|
|
2952
|
+
rootClassName: style$1['schedule-popover'],
|
|
2953
|
+
open: popoverOpen && !dragging,
|
|
2954
|
+
onOpenChange: onPopoverOpenChange,
|
|
2955
|
+
content: formPopover,
|
|
2956
|
+
destroyOnHidden: true,
|
|
2957
|
+
children: /*#__PURE__*/jsx("span", {
|
|
2958
|
+
"aria-hidden": true,
|
|
2959
|
+
className: style$1['timeline-popover-anchor'],
|
|
2960
|
+
style: {
|
|
2961
|
+
top: toY(popoverAnchor.startMs),
|
|
2962
|
+
height: (popoverAnchor.endMs - popoverAnchor.startMs) * pxPerMs
|
|
2963
|
+
}
|
|
2964
|
+
})
|
|
2965
|
+
}) : null]
|
|
2966
|
+
})]
|
|
1512
2967
|
});
|
|
1513
|
-
const slotHandlers = {
|
|
1514
|
-
onContextMenu,
|
|
1515
|
-
onMouseDown,
|
|
1516
|
-
onPointerDown,
|
|
1517
|
-
onMouseEnter,
|
|
1518
|
-
onMouseUp,
|
|
1519
|
-
onClick
|
|
1520
|
-
};
|
|
1521
|
-
const inner = slot.status === 'disabled' ? /*#__PURE__*/jsx("button", {
|
|
1522
|
-
type: "button",
|
|
1523
|
-
className: slotClassName,
|
|
1524
|
-
"data-slot-index": slot.index,
|
|
1525
|
-
"data-slot-status": slot.status,
|
|
1526
|
-
disabled: true,
|
|
1527
|
-
onContextMenu: onContextMenu,
|
|
1528
|
-
onMouseDown: onMouseDown,
|
|
1529
|
-
onPointerDown: onPointerDown,
|
|
1530
|
-
onMouseEnter: onMouseEnter,
|
|
1531
|
-
onMouseUp: onMouseUp,
|
|
1532
|
-
onClick: onClick,
|
|
1533
|
-
children: slotChildren
|
|
1534
|
-
}) : /*#__PURE__*/jsx("div", _extends({
|
|
1535
|
-
role: "button",
|
|
1536
|
-
tabIndex: 0,
|
|
1537
|
-
className: slotClassName,
|
|
1538
|
-
"data-slot-index": slot.index,
|
|
1539
|
-
"data-slot-status": slot.status
|
|
1540
|
-
}, slotHandlers, {
|
|
1541
|
-
children: slotChildren
|
|
1542
|
-
}));
|
|
1543
|
-
return wrapWithPopover(inner);
|
|
1544
2968
|
};
|
|
1545
2969
|
|
|
1546
2970
|
const SchedulePanel = ({
|
|
1547
2971
|
date,
|
|
1548
2972
|
slots,
|
|
2973
|
+
events: dayEvents,
|
|
2974
|
+
dayStart,
|
|
2975
|
+
dayEnd,
|
|
2976
|
+
timeUnit,
|
|
2977
|
+
timeStep,
|
|
2978
|
+
hourHeight,
|
|
2979
|
+
allowConflict,
|
|
1549
2980
|
loading,
|
|
1550
2981
|
allowMultiSelect,
|
|
1551
|
-
selectedSlots,
|
|
1552
|
-
setSelectedSlots,
|
|
1553
2982
|
onSlotRangeChange,
|
|
1554
2983
|
onFreeSlotClick,
|
|
1555
2984
|
onCreate,
|
|
1556
2985
|
formInner,
|
|
1557
2986
|
formProps,
|
|
1558
2987
|
defaultFormValues,
|
|
1559
|
-
|
|
2988
|
+
renderEventBlock,
|
|
1560
2989
|
renderOccupiedSlot,
|
|
1561
2990
|
renderPanelHeader,
|
|
1562
2991
|
onEventClick
|
|
@@ -1564,56 +2993,129 @@ const SchedulePanel = ({
|
|
|
1564
2993
|
const {
|
|
1565
2994
|
formatMessage
|
|
1566
2995
|
} = useIntl();
|
|
1567
|
-
const
|
|
2996
|
+
const isMobile = useIsMobile();
|
|
2997
|
+
const scrollRef = useRef(null);
|
|
2998
|
+
const interactionRef = useRef(0);
|
|
2999
|
+
const openedAtRef = useRef(0);
|
|
3000
|
+
const [selection, setSelection] = useState(null);
|
|
3001
|
+
const [dragging, setDragging] = useState(false);
|
|
3002
|
+
const [popoverOpen, setPopoverOpen] = useState(false);
|
|
3003
|
+
const [popoverPlacement, setPopoverPlacement] = useState('left');
|
|
3004
|
+
const [popoverAnchor, setPopoverAnchor] = useState(null);
|
|
3005
|
+
const day = useMemo(() => ({
|
|
3006
|
+
startMs: parseDayTime(date, dayStart).valueOf(),
|
|
3007
|
+
endMs: parseDayTime(date, dayEnd).valueOf()
|
|
3008
|
+
}), [date, dayEnd, dayStart]);
|
|
3009
|
+
const events = useMemo(() => sortEventsByStart(dayEvents || []), [dayEvents]);
|
|
3010
|
+
const eventItems = useMemo(() => layoutTimelineEvents(events, day), [day, events]);
|
|
3011
|
+
const disabledRanges = useMemo(() => mergeDisabledSlots(slots), [slots]);
|
|
3012
|
+
const blockers = useMemo(() => toBlockers(allowConflict ? disabledRanges : [...disabledRanges, ...eventItems]), [allowConflict, disabledRanges, eventItems]);
|
|
3013
|
+
const conflicts = useMemo(() => findOverlappingEvents(selection, eventItems.map(item => item.event)), [eventItems, selection]);
|
|
3014
|
+
const gap = selection && getFreeGap(selection.startMs, blockers, day) || day;
|
|
3015
|
+
const windowRange = useMemo(() => [new Date(gap.startMs), new Date(gap.endMs)], [gap.startMs, gap.endMs]);
|
|
1568
3016
|
const panelHeader = renderPanelHeader == null ? void 0 : renderPanelHeader({
|
|
1569
3017
|
date: date.toDate(),
|
|
1570
3018
|
mode: 'schedule',
|
|
1571
3019
|
events
|
|
1572
3020
|
});
|
|
1573
|
-
const {
|
|
1574
|
-
activePopoverKey,
|
|
1575
|
-
dragging,
|
|
1576
|
-
formPopoverKey,
|
|
1577
|
-
selectedAnchorKey,
|
|
1578
|
-
selectedKeys,
|
|
1579
|
-
selectSlots,
|
|
1580
|
-
slotsRef,
|
|
1581
|
-
handleMouseDown,
|
|
1582
|
-
handleMouseEnter,
|
|
1583
|
-
handleMouseUp,
|
|
1584
|
-
handleMouseLeave,
|
|
1585
|
-
handlePopoverOpenChange
|
|
1586
|
-
} = useScheduleSelectionGesture({
|
|
1587
|
-
slots,
|
|
1588
|
-
selectedSlots,
|
|
1589
|
-
setSelectedSlots,
|
|
1590
|
-
allowMultiSelect,
|
|
1591
|
-
onSlotRangeChange,
|
|
1592
|
-
onFreeSlotClick
|
|
1593
|
-
});
|
|
1594
3021
|
const {
|
|
1595
3022
|
scrolled,
|
|
1596
3023
|
onScroll
|
|
1597
|
-
} = usePanelScrollShadow(date.format(DATE_FORMAT),
|
|
1598
|
-
const
|
|
3024
|
+
} = usePanelScrollShadow(date.format(DATE_FORMAT), scrollRef);
|
|
3025
|
+
const getFormContext = range => ({
|
|
1599
3026
|
date: date.toDate(),
|
|
1600
|
-
|
|
3027
|
+
start: range ? new Date(range.startMs) : null,
|
|
3028
|
+
end: range ? new Date(range.endMs) : null,
|
|
3029
|
+
slots: findOverlappingSlots(range, slots),
|
|
3030
|
+
conflicts: findOverlappingEvents(range, eventItems.map(item => item.event))
|
|
3031
|
+
});
|
|
3032
|
+
const emitRange = range => {
|
|
3033
|
+
if (range) {
|
|
3034
|
+
onSlotRangeChange == null || onSlotRangeChange(findOverlappingSlots(range, slots), {
|
|
3035
|
+
action: 'select',
|
|
3036
|
+
start: new Date(range.startMs),
|
|
3037
|
+
end: new Date(range.endMs)
|
|
3038
|
+
});
|
|
3039
|
+
} else {
|
|
3040
|
+
onSlotRangeChange == null || onSlotRangeChange([], {
|
|
3041
|
+
action: 'clear'
|
|
3042
|
+
});
|
|
3043
|
+
}
|
|
3044
|
+
};
|
|
3045
|
+
const clearSelection = () => {
|
|
3046
|
+
setPopoverOpen(false);
|
|
3047
|
+
setPopoverAnchor(null);
|
|
3048
|
+
setDragging(false);
|
|
3049
|
+
if (selection) {
|
|
3050
|
+
setSelection(null);
|
|
3051
|
+
emitRange(null);
|
|
3052
|
+
}
|
|
1601
3053
|
};
|
|
1602
|
-
const
|
|
1603
|
-
|
|
3054
|
+
const commitSelection = range => {
|
|
3055
|
+
setDragging(false);
|
|
3056
|
+
if (!range) {
|
|
3057
|
+
return;
|
|
3058
|
+
}
|
|
3059
|
+
if (!popoverOpen) {
|
|
3060
|
+
var _scrollRef$current;
|
|
3061
|
+
const stacked = !!((_scrollRef$current = scrollRef.current) != null && _scrollRef$current.closest('[data-schedule-layout="stacked"]'));
|
|
3062
|
+
setPopoverPlacement(stacked ? 'bottom' : 'left');
|
|
3063
|
+
setPopoverAnchor(range);
|
|
3064
|
+
}
|
|
3065
|
+
setSelection(range);
|
|
3066
|
+
openedAtRef.current = Date.now();
|
|
3067
|
+
setPopoverOpen(true);
|
|
3068
|
+
emitRange(range);
|
|
3069
|
+
};
|
|
3070
|
+
const formContext = getFormContext(selection);
|
|
3071
|
+
const formPopover = selection ? /*#__PURE__*/jsxs("div", {
|
|
3072
|
+
className: classNames(style$1['form-popover'], {
|
|
3073
|
+
[style$1['form-sheet-content']]: isMobile
|
|
3074
|
+
}),
|
|
1604
3075
|
children: [/*#__PURE__*/jsx("div", {
|
|
1605
3076
|
className: style$1['form-popover-title'],
|
|
1606
3077
|
children: formatMessage({
|
|
1607
|
-
id: 'SchedulePanel.
|
|
1608
|
-
}, {
|
|
1609
|
-
range: formatRange(selectedSlots[0].start, selectedSlots[selectedSlots.length - 1].end)
|
|
3078
|
+
id: 'SchedulePanel.newScheduleTitle'
|
|
1610
3079
|
})
|
|
1611
|
-
}), /*#__PURE__*/
|
|
3080
|
+
}), /*#__PURE__*/jsx(TimeWindow, {
|
|
3081
|
+
className: style$1['form-time-window'],
|
|
3082
|
+
size: isMobile ? 'default' : 'small',
|
|
3083
|
+
value: [new Date(selection.startMs), new Date(selection.endMs)],
|
|
3084
|
+
range: windowRange,
|
|
3085
|
+
step: timeStep,
|
|
3086
|
+
minDuration: timeStep,
|
|
3087
|
+
resizable: allowMultiSelect,
|
|
3088
|
+
debounceWait: 120,
|
|
3089
|
+
format: "HH:mm",
|
|
3090
|
+
separator: " - ",
|
|
3091
|
+
onChange: value => {
|
|
3092
|
+
if (!value) {
|
|
3093
|
+
return;
|
|
3094
|
+
}
|
|
3095
|
+
const range = {
|
|
3096
|
+
startMs: value[0].getTime(),
|
|
3097
|
+
endMs: value[1].getTime()
|
|
3098
|
+
};
|
|
3099
|
+
setSelection(range);
|
|
3100
|
+
emitRange(range);
|
|
3101
|
+
}
|
|
3102
|
+
}), conflicts.length ? /*#__PURE__*/jsx(Alert, {
|
|
3103
|
+
className: style$1['form-conflict'],
|
|
3104
|
+
type: "warning",
|
|
3105
|
+
showIcon: true,
|
|
3106
|
+
title: `${formatMessage({
|
|
3107
|
+
id: 'SchedulePanel.conflict'
|
|
3108
|
+
}, {
|
|
3109
|
+
count: conflicts.length
|
|
3110
|
+
})}:${conflicts.map(event => event.title).join('、')}`
|
|
3111
|
+
}) : null, /*#__PURE__*/jsxs(Form, _extends({}, formProps, isMobile ? {
|
|
3112
|
+
size: 'large'
|
|
3113
|
+
} : null, {
|
|
1612
3114
|
data: getDefaultFormValues(defaultFormValues, formContext),
|
|
1613
3115
|
onSubmit: async values => {
|
|
1614
3116
|
try {
|
|
1615
|
-
await (onCreate == null ? void 0 : onCreate(values,
|
|
1616
|
-
|
|
3117
|
+
await (onCreate == null ? void 0 : onCreate(values, getFormContext(selection)));
|
|
3118
|
+
clearSelection();
|
|
1617
3119
|
} catch (_unused) {
|
|
1618
3120
|
// Keep the form open when creation fails.
|
|
1619
3121
|
}
|
|
@@ -1621,7 +3123,7 @@ const SchedulePanel = ({
|
|
|
1621
3123
|
children: [formInner || /*#__PURE__*/jsx(DefaultFormInner, {}), /*#__PURE__*/jsxs("div", {
|
|
1622
3124
|
className: style$1['form-actions'],
|
|
1623
3125
|
children: [/*#__PURE__*/jsx(Button, {
|
|
1624
|
-
onClick:
|
|
3126
|
+
onClick: clearSelection,
|
|
1625
3127
|
children: formatMessage({
|
|
1626
3128
|
id: 'SchedulePanel.cancel'
|
|
1627
3129
|
})
|
|
@@ -1633,7 +3135,7 @@ const SchedulePanel = ({
|
|
|
1633
3135
|
})]
|
|
1634
3136
|
}))]
|
|
1635
3137
|
}) : null;
|
|
1636
|
-
return /*#__PURE__*/
|
|
3138
|
+
return /*#__PURE__*/jsxs(Card, {
|
|
1637
3139
|
bordered: false,
|
|
1638
3140
|
title: panelHeader || /*#__PURE__*/jsx(PanelTitle, {
|
|
1639
3141
|
date: date,
|
|
@@ -1642,60 +3144,82 @@ const SchedulePanel = ({
|
|
|
1642
3144
|
})
|
|
1643
3145
|
}),
|
|
1644
3146
|
className: panelStyle['panel-card'],
|
|
1645
|
-
children: /*#__PURE__*/jsx(Spin, {
|
|
3147
|
+
children: [/*#__PURE__*/jsx(Spin, {
|
|
1646
3148
|
spinning: !!loading,
|
|
1647
3149
|
classNames: {
|
|
1648
3150
|
root: panelStyle['panel-spin'],
|
|
1649
3151
|
container: panelStyle['panel-spin-container']
|
|
1650
3152
|
},
|
|
1651
3153
|
children: /*#__PURE__*/jsxs("div", {
|
|
1652
|
-
ref:
|
|
1653
|
-
className:
|
|
1654
|
-
[style$1['slots-dragging']]: dragging
|
|
1655
|
-
}),
|
|
3154
|
+
ref: scrollRef,
|
|
3155
|
+
className: style$1['slots'],
|
|
1656
3156
|
onScroll: onScroll,
|
|
1657
|
-
onMouseUp: handleMouseUp,
|
|
1658
|
-
onMouseLeave: handleMouseLeave,
|
|
1659
3157
|
children: [/*#__PURE__*/jsx(PanelScrollShadow, {
|
|
1660
3158
|
visible: scrolled
|
|
1661
|
-
}), /*#__PURE__*/jsx(
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
3159
|
+
}), /*#__PURE__*/jsx(Timeline, {
|
|
3160
|
+
date: date.toDate(),
|
|
3161
|
+
day: day,
|
|
3162
|
+
hourHeight: hourHeight,
|
|
3163
|
+
timeUnit: timeUnit,
|
|
3164
|
+
timeStep: timeStep,
|
|
3165
|
+
eventItems: eventItems,
|
|
3166
|
+
disabledRanges: disabledRanges,
|
|
3167
|
+
blockers: blockers,
|
|
3168
|
+
conflicts: conflicts,
|
|
3169
|
+
allowMultiSelect: allowMultiSelect,
|
|
3170
|
+
selection: selection,
|
|
3171
|
+
dragging: dragging,
|
|
3172
|
+
formPopover: isMobile ? null : formPopover,
|
|
3173
|
+
isMobile: isMobile,
|
|
3174
|
+
popoverOpen: popoverOpen,
|
|
3175
|
+
popoverPlacement: popoverPlacement,
|
|
3176
|
+
popoverAnchor: popoverAnchor,
|
|
3177
|
+
interactionRef: interactionRef,
|
|
3178
|
+
onPopoverOpenChange: open => {
|
|
3179
|
+
if (open) {
|
|
3180
|
+
setPopoverOpen(true);
|
|
3181
|
+
return;
|
|
3182
|
+
}
|
|
3183
|
+
if (Date.now() - interactionRef.current < 400) {
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
clearSelection();
|
|
3187
|
+
},
|
|
3188
|
+
onSelectionDrag: range => {
|
|
3189
|
+
setDragging(true);
|
|
3190
|
+
setSelection(range);
|
|
3191
|
+
},
|
|
3192
|
+
onSelectionCommit: commitSelection,
|
|
3193
|
+
onFreeSlotClick: slot => onFreeSlotClick == null ? void 0 : onFreeSlotClick(_extends({}, slot, {
|
|
3194
|
+
key: `${date.format(DATE_FORMAT)}-${slot.start.getTime()}`,
|
|
3195
|
+
date: date.format(DATE_FORMAT),
|
|
3196
|
+
status: 'free',
|
|
3197
|
+
events: []
|
|
3198
|
+
})),
|
|
3199
|
+
onEventClick: onEventClick,
|
|
3200
|
+
renderEventBlock: renderEventBlock,
|
|
3201
|
+
renderOccupiedSlot: renderOccupiedSlot
|
|
1692
3202
|
})]
|
|
1693
3203
|
})
|
|
1694
|
-
})
|
|
3204
|
+
}), isMobile ? /*#__PURE__*/jsx(Drawer, {
|
|
3205
|
+
placement: "bottom",
|
|
3206
|
+
size: "auto",
|
|
3207
|
+
closable: false,
|
|
3208
|
+
rootClassName: style$1['form-sheet'],
|
|
3209
|
+
open: !!selection && popoverOpen && !dragging,
|
|
3210
|
+
onClose: () => {
|
|
3211
|
+
if (Date.now() - openedAtRef.current < 500) {
|
|
3212
|
+
return;
|
|
3213
|
+
}
|
|
3214
|
+
clearSelection();
|
|
3215
|
+
},
|
|
3216
|
+
destroyOnHidden: true,
|
|
3217
|
+
children: formPopover
|
|
3218
|
+
}) : null]
|
|
1695
3219
|
});
|
|
1696
3220
|
};
|
|
1697
3221
|
|
|
1698
|
-
var style = {"schedule-calendar-shell":"kne-react-calendar_csu63","schedule-calendar":"kne-react-calendar_5p1TK","calendar-side":"kne-react-calendar_nd7DK","panel-side":"kne-react-calendar_rWvHE","schedule-calendar-stacked":"kne-react-calendar_zGXr5"};
|
|
3222
|
+
var style = {"kne-responsive-boundary":"kne-react-calendar_gh14z","kne-responsive-scroll":"kne-react-calendar_Kfn6L","schedule-calendar-shell":"kne-react-calendar_csu63","schedule-calendar":"kne-react-calendar_5p1TK","calendar-side":"kne-react-calendar_nd7DK","panel-side":"kne-react-calendar_rWvHE","schedule-calendar-event-list-expanded":"kne-react-calendar_Av0q4","schedule-calendar-stacked":"kne-react-calendar_zGXr5"};
|
|
1699
3223
|
|
|
1700
3224
|
dayjs.extend(customParseFormat);
|
|
1701
3225
|
const buildMonthRuleCache = ({
|
|
@@ -1733,10 +3257,13 @@ const ScheduleCalendarInner = props => {
|
|
|
1733
3257
|
timeUnit = 30,
|
|
1734
3258
|
dayStart = '09:00',
|
|
1735
3259
|
dayEnd = '18:00',
|
|
3260
|
+
timeStep = 5,
|
|
3261
|
+
hourHeight = 56,
|
|
3262
|
+
allowConflict = false,
|
|
1736
3263
|
allowMultiSelect = mode === 'schedule',
|
|
1737
3264
|
renderDateCell,
|
|
1738
3265
|
renderEventItem,
|
|
1739
|
-
|
|
3266
|
+
renderEventBlock,
|
|
1740
3267
|
renderOccupiedSlot,
|
|
1741
3268
|
renderPanelHeader,
|
|
1742
3269
|
formInner,
|
|
@@ -1750,13 +3277,15 @@ const ScheduleCalendarInner = props => {
|
|
|
1750
3277
|
className,
|
|
1751
3278
|
style: propsStyle,
|
|
1752
3279
|
stackAt = LAYOUT_STACK_BREAKPOINT,
|
|
3280
|
+
eventListScrollable = true,
|
|
1753
3281
|
calendarProps,
|
|
1754
3282
|
panelProps
|
|
1755
3283
|
} = props;
|
|
1756
3284
|
const rootRef = useRef(null);
|
|
1757
3285
|
const calendarRef = useRef(null);
|
|
1758
3286
|
const stacked = useStackedLayout(rootRef, stackAt);
|
|
1759
|
-
const
|
|
3287
|
+
const eventListExpanded = mode === 'view' && !eventListScrollable;
|
|
3288
|
+
const stackedLayoutStyle = useStackedPanelLayout(rootRef, calendarRef, stacked && !eventListExpanded);
|
|
1760
3289
|
const [value, setValue] = useControlValue(props);
|
|
1761
3290
|
const selectedDate = useMemo(() => toDayjs(value) || dayjs(), [value]);
|
|
1762
3291
|
const [current, setCurrent] = useControlledState({
|
|
@@ -1765,7 +3294,6 @@ const ScheduleCalendarInner = props => {
|
|
|
1765
3294
|
onChange: props.onCurrentChange
|
|
1766
3295
|
});
|
|
1767
3296
|
const currentDate = useMemo(() => toDayjs(current) || selectedDate, [current, selectedDate]);
|
|
1768
|
-
const [selectedSlots, setSelectedSlots] = useState([]);
|
|
1769
3297
|
const eventGroup = useMemo(() => groupEventsByDate(events), [events]);
|
|
1770
3298
|
const selectedEvents = useMemo(() => eventGroup[selectedDate.format(DATE_FORMAT)] || [], [eventGroup, selectedDate]);
|
|
1771
3299
|
const ruleContext = useMemo(() => ({
|
|
@@ -1801,7 +3329,6 @@ const ScheduleCalendarInner = props => {
|
|
|
1801
3329
|
const nextDate = date.startOf('day').toDate();
|
|
1802
3330
|
setValue(nextDate, info);
|
|
1803
3331
|
onDateChange == null || onDateChange(nextDate, info);
|
|
1804
|
-
setSelectedSlots([]);
|
|
1805
3332
|
};
|
|
1806
3333
|
const handleCurrentChange = (date, info) => {
|
|
1807
3334
|
setCurrent(date.startOf('month').toDate(), info);
|
|
@@ -1837,9 +3364,9 @@ const ScheduleCalendarInner = props => {
|
|
|
1837
3364
|
style: propsStyle,
|
|
1838
3365
|
children: /*#__PURE__*/jsxs("div", {
|
|
1839
3366
|
ref: rootRef,
|
|
1840
|
-
className: classNames(style['schedule-calendar'], stacked && style['schedule-calendar-stacked'], className),
|
|
3367
|
+
className: classNames(style['schedule-calendar'], stacked && style['schedule-calendar-stacked'], eventListExpanded && style['schedule-calendar-event-list-expanded'], className),
|
|
1841
3368
|
"data-schedule-layout": stacked ? 'stacked' : 'row',
|
|
1842
|
-
style: stacked ? stackedLayoutStyle : undefined,
|
|
3369
|
+
style: stacked && !eventListExpanded ? stackedLayoutStyle : undefined,
|
|
1843
3370
|
children: [/*#__PURE__*/jsx("div", {
|
|
1844
3371
|
ref: calendarRef,
|
|
1845
3372
|
className: style['calendar-side'],
|
|
@@ -1876,27 +3403,33 @@ const ScheduleCalendarInner = props => {
|
|
|
1876
3403
|
children: mode === 'schedule' ? /*#__PURE__*/jsx(SchedulePanel, {
|
|
1877
3404
|
date: selectedDate,
|
|
1878
3405
|
slots: slots,
|
|
3406
|
+
events: selectedEvents,
|
|
3407
|
+
dayStart: dayStart,
|
|
3408
|
+
dayEnd: dayEnd,
|
|
3409
|
+
timeUnit: timeUnit,
|
|
3410
|
+
timeStep: timeStep,
|
|
3411
|
+
hourHeight: hourHeight,
|
|
3412
|
+
allowConflict: allowConflict,
|
|
1879
3413
|
loading: loading,
|
|
1880
3414
|
allowMultiSelect: allowMultiSelect,
|
|
1881
|
-
selectedSlots: selectedSlots,
|
|
1882
|
-
setSelectedSlots: setSelectedSlots,
|
|
1883
3415
|
onSlotRangeChange: onSlotRangeChange,
|
|
1884
3416
|
onFreeSlotClick: onFreeSlotClick,
|
|
1885
3417
|
onCreate: onCreate,
|
|
1886
3418
|
formInner: formInner,
|
|
1887
3419
|
formProps: formProps,
|
|
1888
3420
|
defaultFormValues: defaultFormValues,
|
|
1889
|
-
|
|
3421
|
+
renderEventBlock: renderEventBlock,
|
|
1890
3422
|
renderOccupiedSlot: renderOccupiedSlot,
|
|
1891
3423
|
renderPanelHeader: renderPanelHeader,
|
|
1892
3424
|
onEventClick: onEventClick
|
|
1893
|
-
}) : /*#__PURE__*/jsx(ViewPanel, {
|
|
3425
|
+
}, selectedDate.format(DATE_FORMAT)) : /*#__PURE__*/jsx(ViewPanel, {
|
|
1894
3426
|
date: selectedDate,
|
|
1895
3427
|
events: selectedEvents,
|
|
1896
3428
|
loading: loading,
|
|
1897
3429
|
renderEventItem: renderEventItem,
|
|
1898
3430
|
renderPanelHeader: renderPanelHeader,
|
|
1899
|
-
onEventClick: onEventClick
|
|
3431
|
+
onEventClick: onEventClick,
|
|
3432
|
+
scrollable: eventListScrollable
|
|
1900
3433
|
})
|
|
1901
3434
|
}))]
|
|
1902
3435
|
})
|
|
@@ -1904,5 +3437,5 @@ const ScheduleCalendarInner = props => {
|
|
|
1904
3437
|
};
|
|
1905
3438
|
const ScheduleCalendar = withLocale(ScheduleCalendarInner);
|
|
1906
3439
|
|
|
1907
|
-
export { ScheduleCalendar as ReactCalendar, ScheduleCalendar, dedupeEventsById, ScheduleCalendar as default, generateTimeSlots, groupEventsByDate, normalizeEvent, sortEventsByStart };
|
|
3440
|
+
export { ScheduleCalendar as ReactCalendar, ScheduleCalendar, TimeWindow, dedupeEventsById, ScheduleCalendar as default, generateTimeSlots, groupEventsByDate, normalizeEvent, sortEventsByStart };
|
|
1908
3441
|
//# sourceMappingURL=index.modern.js.map
|