@kne/react-calendar 1.0.6 → 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/dist/index.js CHANGED
@@ -6,6 +6,7 @@ var antd = require('antd');
6
6
  var reactIntl = require('@kne/react-intl');
7
7
  var jsxRuntime = require('react/jsx-runtime');
8
8
  var FormInfo = require('@kne/form-info');
9
+ var responsiveUtils = require('@kne/responsive-utils');
9
10
 
10
11
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
11
12
 
@@ -14,16 +15,6 @@ var customParseFormat__default = /*#__PURE__*/_interopDefaultLegacy(customParseF
14
15
  var useControlValue__default = /*#__PURE__*/_interopDefaultLegacy(useControlValue);
15
16
  var FormInfo__default = /*#__PURE__*/_interopDefaultLegacy(FormInfo);
16
17
 
17
- function _extends() {
18
- return _extends = Object.assign ? Object.assign.bind() : function (n) {
19
- for (var e = 1; e < arguments.length; e++) {
20
- var t = arguments[e];
21
- for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]);
22
- }
23
- return n;
24
- }, _extends.apply(null, arguments);
25
- }
26
-
27
18
  const locale$1 = {
28
19
  'Header.today': '今天',
29
20
  'Header.prevMonth': '上一月',
@@ -36,16 +27,19 @@ const locale$1 = {
36
27
  'PanelTitle.dateLabel': '{year}年{monthName}{day}日',
37
28
  'PanelTitle.eventCount': '共 {count} 个日程',
38
29
  'ViewPanel.empty': '暂无日程',
39
- 'SchedulePanel.selectFreeSlot': '选择空闲时间段',
40
- 'SchedulePanel.newSchedule': '新增日程:{range}',
30
+ 'SchedulePanel.selectFreeSlot': '点击或拖拽空白处选择时间',
41
31
  'SchedulePanel.cancel': '取消',
42
32
  'SchedulePanel.save': '保存',
43
- 'SlotItem.disabled': '不可选',
44
- 'SlotItem.occupied': '已占用',
33
+ 'SchedulePanel.newScheduleTitle': '新增日程',
34
+ 'SchedulePanel.conflict': '与 {count} 个日程时间冲突',
45
35
  'DefaultFormInner.title': '标题',
46
36
  'DefaultFormInner.titlePlaceholder': '请输入日程标题',
47
37
  'DefaultFormInner.detail': '详情',
48
38
  'DefaultFormInner.detailPlaceholder': '请输入日程详情',
39
+ 'TimeWindow.hours': '{count} 小时',
40
+ 'TimeWindow.minutes': '{count} 分钟',
41
+ 'TimeWindow.editable': '允许编辑',
42
+ 'TimeWindow.locked': '已锁定',
49
43
  'Calendar.month.1': '1月',
50
44
  'Calendar.month.2': '2月',
51
45
  'Calendar.month.3': '3月',
@@ -72,16 +66,19 @@ const locale = {
72
66
  'PanelTitle.dateLabel': '{monthName} {day}, {year}',
73
67
  'PanelTitle.eventCount': '{count} events',
74
68
  'ViewPanel.empty': 'No events',
75
- 'SchedulePanel.selectFreeSlot': 'Select an available time slot',
76
- 'SchedulePanel.newSchedule': 'New event: {range}',
69
+ 'SchedulePanel.selectFreeSlot': 'Click or drag on free time',
77
70
  'SchedulePanel.cancel': 'Cancel',
78
71
  'SchedulePanel.save': 'Save',
79
- 'SlotItem.disabled': 'Unavailable',
80
- 'SlotItem.occupied': 'Occupied',
72
+ 'SchedulePanel.newScheduleTitle': 'New event',
73
+ 'SchedulePanel.conflict': 'Overlaps {count} event(s)',
81
74
  'DefaultFormInner.title': 'Title',
82
75
  'DefaultFormInner.titlePlaceholder': 'Enter event title',
83
76
  'DefaultFormInner.detail': 'Details',
84
77
  'DefaultFormInner.detailPlaceholder': 'Enter event details',
78
+ 'TimeWindow.hours': '{count} h',
79
+ 'TimeWindow.minutes': '{count} min',
80
+ 'TimeWindow.editable': 'Editable',
81
+ 'TimeWindow.locked': 'Locked',
85
82
  'Calendar.month.1': 'Jan',
86
83
  'Calendar.month.2': 'Feb',
87
84
  'Calendar.month.3': 'Mar',
@@ -173,23 +170,23 @@ const toDayjs = value => {
173
170
  };
174
171
  const parseDayTime = (date, time) => {
175
172
  const day = toDayjs(date) || dayjs__default["default"]();
176
- const parsed = dayjs__default["default"](day.format(DATE_FORMAT) + " " + time, DATE_FORMAT + " " + TIME_FORMAT);
173
+ const parsed = dayjs__default["default"](`${day.format(DATE_FORMAT)} ${time}`, `${DATE_FORMAT} ${TIME_FORMAT}`);
177
174
  return parsed.isValid() ? parsed : day.startOf('day');
178
175
  };
179
- const formatRange = (start, end) => dayjs__default["default"](start).format(TIME_FORMAT) + " - " + dayjs__default["default"](end).format(TIME_FORMAT);
176
+ const formatRange = (start, end) => `${dayjs__default["default"](start).format(TIME_FORMAT)} - ${dayjs__default["default"](end).format(TIME_FORMAT)}`;
180
177
  const getDefaultFormValues = (defaultFormValues, ctx) => {
181
178
  if (typeof defaultFormValues === 'function') {
182
179
  return defaultFormValues(ctx);
183
180
  }
184
- return _extends({
181
+ return {
185
182
  title: '',
186
- detail: ''
187
- }, defaultFormValues || {});
183
+ detail: '',
184
+ ...(defaultFormValues || {})
185
+ };
188
186
  };
189
187
  const sortEventsByStart = events => [...(events || [])].sort((a, b) => {
190
- var _toDayjs$valueOf, _toDayjs, _toDayjs$valueOf2, _toDayjs2;
191
- const aStart = (_toDayjs$valueOf = (_toDayjs = toDayjs(a.start)) == null ? void 0 : _toDayjs.valueOf()) != null ? _toDayjs$valueOf : 0;
192
- const bStart = (_toDayjs$valueOf2 = (_toDayjs2 = toDayjs(b.start)) == null ? void 0 : _toDayjs2.valueOf()) != null ? _toDayjs$valueOf2 : 0;
188
+ const aStart = toDayjs(a.start)?.valueOf() ?? 0;
189
+ const bStart = toDayjs(b.start)?.valueOf() ?? 0;
193
190
  return aStart - bStart;
194
191
  });
195
192
  const dedupeEventsById = events => {
@@ -200,9 +197,11 @@ const dedupeEventsById = events => {
200
197
  return [...map.values()];
201
198
  };
202
199
  const useControlledState = _ref => {
203
- let value = _ref.value,
204
- defaultValue = _ref.defaultValue,
205
- onChange = _ref.onChange;
200
+ let {
201
+ value,
202
+ defaultValue,
203
+ onChange
204
+ } = _ref;
206
205
  const props = {};
207
206
  if (typeof value !== 'undefined') {
208
207
  props.value = value;
@@ -234,14 +233,15 @@ const getRuleDisabled = (result, defaultReason) => {
234
233
  }
235
234
  return null;
236
235
  };
237
- const checkRule = ({
238
- date,
239
- slot,
240
- mode,
241
- disabledDate,
242
- disabledDateTime,
243
- availableDateTime
244
- }) => {
236
+ const checkRule = _ref => {
237
+ let {
238
+ date,
239
+ slot,
240
+ mode,
241
+ disabledDate,
242
+ disabledDateTime,
243
+ availableDateTime
244
+ } = _ref;
245
245
  if (disabledDate?.(date.toDate())) {
246
246
  return {
247
247
  disabled: true
@@ -272,10 +272,11 @@ const checkRule = ({
272
272
  const normalizeEvent = event => {
273
273
  const start = toDayjs(event.start || event.date);
274
274
  const end = toDayjs(event.end || event.start || event.date);
275
- return _extends({}, event, {
275
+ return {
276
+ ...event,
276
277
  start,
277
278
  end: end && start && !end.isAfter(start) ? start.add(1, 'minute') : end
278
- });
279
+ };
279
280
  };
280
281
  const groupEventsByDate = events => {
281
282
  return (events || []).reduce((result, event) => {
@@ -309,19 +310,17 @@ const getOverlappingEvents = (slotStart, slotEnd, sortedEvents) => {
309
310
  return matched;
310
311
  };
311
312
  const generateTimeSlots = _ref => {
312
- let date = _ref.date,
313
- events = _ref.events,
314
- _ref$timeUnit = _ref.timeUnit,
315
- timeUnit = _ref$timeUnit === void 0 ? 30 : _ref$timeUnit,
316
- _ref$dayStart = _ref.dayStart,
317
- dayStart = _ref$dayStart === void 0 ? '09:00' : _ref$dayStart,
318
- _ref$dayEnd = _ref.dayEnd,
319
- dayEnd = _ref$dayEnd === void 0 ? '18:00' : _ref$dayEnd,
320
- disabledDate = _ref.disabledDate,
321
- disabledDateTime = _ref.disabledDateTime,
322
- availableDateTime = _ref.availableDateTime,
323
- _ref$mode = _ref.mode,
324
- mode = _ref$mode === void 0 ? 'schedule' : _ref$mode;
313
+ let {
314
+ date,
315
+ events,
316
+ timeUnit = 30,
317
+ dayStart = '09:00',
318
+ dayEnd = '18:00',
319
+ disabledDate,
320
+ disabledDateTime,
321
+ availableDateTime,
322
+ mode = 'schedule'
323
+ } = _ref;
325
324
  const selectedDate = toDayjs(date) || dayjs__default["default"]();
326
325
  const startAt = parseDayTime(selectedDate, dayStart);
327
326
  const endAt = parseDayTime(selectedDate, dayEnd);
@@ -336,7 +335,7 @@ const generateTimeSlots = _ref => {
336
335
  const slotEnd = slotStart.add(timeUnit, 'minute');
337
336
  const matchedEvents = getOverlappingEvents(slotStart, slotEnd, normalizedEvents);
338
337
  const baseSlot = {
339
- key: slotStart.format(DATE_FORMAT) + "-" + slotStart.format(TIME_FORMAT),
338
+ key: `${slotStart.format(DATE_FORMAT)}-${slotStart.format(TIME_FORMAT)}`,
340
339
  date: slotStart.format(DATE_FORMAT),
341
340
  start: slotStart,
342
341
  end: slotEnd
@@ -349,13 +348,14 @@ const generateTimeSlots = _ref => {
349
348
  disabledDateTime,
350
349
  availableDateTime
351
350
  });
352
- slots.push(_extends({}, baseSlot, {
351
+ slots.push({
352
+ ...baseSlot,
353
353
  start: slotStart.toDate(),
354
354
  end: slotEnd.toDate(),
355
355
  status: ruleResult ? 'disabled' : matchedEvents.length > 0 ? 'occupied' : 'free',
356
- disabledReason: ruleResult == null ? void 0 : ruleResult.reason,
356
+ disabledReason: ruleResult?.reason,
357
357
  events: matchedEvents
358
- }));
358
+ });
359
359
  current = slotEnd;
360
360
  }
361
361
  return slots;
@@ -394,13 +394,13 @@ const getMonthPickerItemLabel = (formatMessage, value, monthIndex) => formatMess
394
394
  monthName: getMonthName(formatMessage, monthIndex)
395
395
  });
396
396
 
397
- var style$4 = {"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"};
397
+ 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"};
398
398
 
399
399
  const HeaderNavIcon = ({
400
400
  direction,
401
401
  className
402
402
  }) => /*#__PURE__*/jsxRuntime.jsx("svg", {
403
- className: className || style$4['header-nav-icon'],
403
+ className: className || style$5['header-nav-icon'],
404
404
  viewBox: "0 0 16 16",
405
405
  "aria-hidden": true,
406
406
  focusable: "false",
@@ -423,7 +423,7 @@ const PickerPanelNav = ({
423
423
  children
424
424
  }) => /*#__PURE__*/jsxRuntime.jsx("button", {
425
425
  type: "button",
426
- className: style$4['picker-panel-nav'],
426
+ className: style$5['picker-panel-nav'],
427
427
  "aria-label": label,
428
428
  onClick: onClick,
429
429
  children: children
@@ -441,9 +441,9 @@ const YearPickerPanel = ({
441
441
  length: YEAR_PAGE_SIZE
442
442
  }, (_, index) => pageStart + index), [pageStart]);
443
443
  return /*#__PURE__*/jsxRuntime.jsxs("div", {
444
- className: style$4['picker-panel'],
444
+ className: style$5['picker-panel'],
445
445
  children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
446
- className: style$4['picker-panel-header'],
446
+ className: style$5['picker-panel-header'],
447
447
  children: [/*#__PURE__*/jsxRuntime.jsx(PickerPanelNav, {
448
448
  label: formatMessage({
449
449
  id: 'Header.prevYearPage'
@@ -451,10 +451,10 @@ const YearPickerPanel = ({
451
451
  onClick: () => onPageStartChange(pageStart - YEAR_PAGE_SIZE),
452
452
  children: /*#__PURE__*/jsxRuntime.jsx(HeaderNavIcon, {
453
453
  direction: "prev",
454
- className: style$4['picker-panel-nav-icon']
454
+ className: style$5['picker-panel-nav-icon']
455
455
  })
456
456
  }), /*#__PURE__*/jsxRuntime.jsxs("div", {
457
- className: style$4['picker-panel-range'],
457
+ className: style$5['picker-panel-range'],
458
458
  children: [pageStart, "-", pageStart + YEAR_PAGE_SIZE - 1]
459
459
  }), /*#__PURE__*/jsxRuntime.jsx(PickerPanelNav, {
460
460
  label: formatMessage({
@@ -463,15 +463,15 @@ const YearPickerPanel = ({
463
463
  onClick: () => onPageStartChange(pageStart + YEAR_PAGE_SIZE),
464
464
  children: /*#__PURE__*/jsxRuntime.jsx(HeaderNavIcon, {
465
465
  direction: "next",
466
- className: style$4['picker-panel-nav-icon']
466
+ className: style$5['picker-panel-nav-icon']
467
467
  })
468
468
  })]
469
469
  }), /*#__PURE__*/jsxRuntime.jsx("div", {
470
- className: style$4['picker-panel-grid'],
470
+ className: style$5['picker-panel-grid'],
471
471
  children: years.map(year => /*#__PURE__*/jsxRuntime.jsx("button", {
472
472
  type: "button",
473
- className: classNames(style$4['picker-cell'], {
474
- [style$4['picker-cell-active']]: year === value.year()
473
+ className: classNames(style$5['picker-cell'], {
474
+ [style$5['picker-cell-active']]: year === value.year()
475
475
  }),
476
476
  onClick: () => onSelect(year),
477
477
  children: year
@@ -491,13 +491,13 @@ const MonthPickerPanel = ({
491
491
  length: 12
492
492
  }, (_, index) => index), []);
493
493
  return /*#__PURE__*/jsxRuntime.jsx("div", {
494
- className: style$4['picker-panel'],
494
+ className: style$5['picker-panel'],
495
495
  children: /*#__PURE__*/jsxRuntime.jsx("div", {
496
- className: style$4['picker-panel-grid'],
496
+ className: style$5['picker-panel-grid'],
497
497
  children: months.map(month => /*#__PURE__*/jsxRuntime.jsx("button", {
498
498
  type: "button",
499
- className: classNames(style$4['picker-cell'], {
500
- [style$4['picker-cell-active']]: month === value.month()
499
+ className: classNames(style$5['picker-cell'], {
500
+ [style$5['picker-cell-active']]: month === value.month()
501
501
  }),
502
502
  onClick: () => onSelect(month),
503
503
  children: getMonthPickerItemLabel(formatMessage, value, month)
@@ -506,23 +506,27 @@ const MonthPickerPanel = ({
506
506
  });
507
507
  };
508
508
 
509
- const HeaderNavButton = ({
510
- label,
511
- onClick,
512
- children
513
- }) => /*#__PURE__*/jsxRuntime.jsx("button", {
514
- type: "button",
515
- className: style$4['header-nav'],
516
- "aria-label": label,
517
- onClick: onClick,
518
- children: children
519
- });
520
- const Header = ({
521
- current,
522
- showTodayButton,
523
- onCurrentChange,
524
- onSelectToday
525
- }) => {
509
+ const HeaderNavButton = _ref => {
510
+ let {
511
+ label,
512
+ onClick,
513
+ children
514
+ } = _ref;
515
+ return /*#__PURE__*/jsxRuntime.jsx("button", {
516
+ type: "button",
517
+ className: style$5['header-nav'],
518
+ "aria-label": label,
519
+ onClick: onClick,
520
+ children: children
521
+ });
522
+ };
523
+ const Header = _ref2 => {
524
+ let {
525
+ current,
526
+ showTodayButton,
527
+ onCurrentChange,
528
+ onSelectToday
529
+ } = _ref2;
526
530
  const {
527
531
  formatMessage
528
532
  } = reactIntl.useIntl();
@@ -555,9 +559,9 @@ const Header = ({
555
559
  setMonthOpen(false);
556
560
  };
557
561
  return /*#__PURE__*/jsxRuntime.jsxs("div", {
558
- className: style$4['calendar-header'],
562
+ className: style$5['calendar-header'],
559
563
  children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
560
- className: style$4['calendar-title'],
564
+ className: style$5['calendar-title'],
561
565
  children: [/*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
562
566
  open: yearOpen,
563
567
  onOpenChange: open => {
@@ -568,7 +572,7 @@ const Header = ({
568
572
  },
569
573
  trigger: "click",
570
574
  placement: "bottomLeft",
571
- rootClassName: style$4['header-picker-popover'],
575
+ rootClassName: style$5['header-picker-popover'],
572
576
  content: /*#__PURE__*/jsxRuntime.jsx(YearPickerPanel, {
573
577
  value: date,
574
578
  pageStart: yearPageStart,
@@ -577,7 +581,7 @@ const Header = ({
577
581
  }),
578
582
  children: /*#__PURE__*/jsxRuntime.jsx("button", {
579
583
  type: "button",
580
- className: style$4['calendar-title-part'],
584
+ className: style$5['calendar-title-part'],
581
585
  children: yearText
582
586
  })
583
587
  }), /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
@@ -590,22 +594,22 @@ const Header = ({
590
594
  },
591
595
  trigger: "click",
592
596
  placement: "bottomLeft",
593
- rootClassName: style$4['header-picker-popover'],
597
+ rootClassName: style$5['header-picker-popover'],
594
598
  content: /*#__PURE__*/jsxRuntime.jsx(MonthPickerPanel, {
595
599
  value: date,
596
600
  onSelect: handleMonthSelect
597
601
  }),
598
602
  children: /*#__PURE__*/jsxRuntime.jsx("button", {
599
603
  type: "button",
600
- className: style$4['calendar-title-part'],
604
+ className: style$5['calendar-title-part'],
601
605
  children: monthText
602
606
  })
603
607
  })]
604
608
  }), /*#__PURE__*/jsxRuntime.jsxs(antd.Space, {
605
609
  size: 4,
606
- className: style$4['header-actions'],
610
+ className: style$5['header-actions'],
607
611
  children: [showTodayButton ? /*#__PURE__*/jsxRuntime.jsx(antd.Button, {
608
- className: style$4['header-today'],
612
+ className: style$5['header-today'],
609
613
  type: "text",
610
614
  size: "small",
611
615
  onClick: onSelectToday,
@@ -672,17 +676,18 @@ const useStackedLayout = (ref, stackAt) => {
672
676
  return stacked;
673
677
  };
674
678
 
675
- var style$3 = {"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"};
679
+ 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"};
676
680
 
677
- const DateCell = ({
678
- date,
679
- dayEvents,
680
- selected,
681
- currentMonth,
682
- disabled,
683
- badgeMaxCount,
684
- renderDateCell
685
- }) => {
681
+ const DateCell = _ref => {
682
+ let {
683
+ date,
684
+ dayEvents,
685
+ selected,
686
+ currentMonth,
687
+ disabled,
688
+ badgeMaxCount,
689
+ renderDateCell
690
+ } = _ref;
686
691
  const count = dayEvents.length;
687
692
  const customCell = renderDateCell?.(date.toDate(), {
688
693
  events: dayEvents,
@@ -693,14 +698,14 @@ const DateCell = ({
693
698
  return customCell;
694
699
  }
695
700
  return /*#__PURE__*/jsxRuntime.jsxs("div", {
696
- className: classNames(style$3['date-cell'], {
697
- [style$3['date-selected']]: selected,
698
- [style$3['date-muted']]: !currentMonth && !selected,
699
- [style$3['date-disabled']]: disabled && !selected,
700
- [style$3['date-has-events']]: count > 0 && !selected
701
+ className: classNames(style$4['date-cell'], {
702
+ [style$4['date-selected']]: selected,
703
+ [style$4['date-muted']]: !currentMonth && !selected,
704
+ [style$4['date-disabled']]: disabled && !selected,
705
+ [style$4['date-has-events']]: count > 0 && !selected
701
706
  }),
702
707
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
703
- className: style$3['date-number'],
708
+ className: style$4['date-number'],
704
709
  children: date.date()
705
710
  }), count ? /*#__PURE__*/jsxRuntime.jsx(antd.Badge, {
706
711
  count: count > badgeMaxCount ? `${badgeMaxCount}+` : count,
@@ -726,7 +731,7 @@ const getEventTagLabel = event => {
726
731
  return null;
727
732
  };
728
733
 
729
- var style$2 = {"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"};
734
+ 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"};
730
735
 
731
736
  const EventItem = ({
732
737
  event,
@@ -758,11 +763,11 @@ const EventItem = ({
758
763
  };
759
764
  const tagLabel = getEventTagLabel(event);
760
765
  return /*#__PURE__*/jsxRuntime.jsx(antd.List.Item, {
761
- className: style$2['event-list-item'],
766
+ className: style$3['event-list-item'],
762
767
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
763
- className: classNames(style$2['event-item'], {
764
- [style$2['event-item-clickable']]: clickable,
765
- [style$2['event-item-disabled']]: event.disabled
768
+ className: classNames(style$3['event-item'], {
769
+ [style$3['event-item-clickable']]: clickable,
770
+ [style$3['event-item-disabled']]: event.disabled
766
771
  }),
767
772
  role: clickable ? 'button' : undefined,
768
773
  tabIndex: clickable ? 0 : undefined,
@@ -774,20 +779,20 @@ const EventItem = ({
774
779
  }
775
780
  } : undefined,
776
781
  children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
777
- className: style$2['event-item-head'],
782
+ className: style$3['event-item-head'],
778
783
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
779
- className: style$2['event-item-title'],
784
+ className: style$3['event-item-title'],
780
785
  children: event.title
781
786
  }), tagLabel ? /*#__PURE__*/jsxRuntime.jsx(antd.Tag, {
782
- className: style$2['event-item-tag'],
787
+ className: style$3['event-item-tag'],
783
788
  color: event.color,
784
789
  children: tagLabel
785
790
  }) : null]
786
791
  }), /*#__PURE__*/jsxRuntime.jsx("div", {
787
- className: style$2['event-item-time'],
792
+ className: style$3['event-item-time'],
788
793
  children: formatRange(event.start, event.end)
789
794
  }), event.detail ? /*#__PURE__*/jsxRuntime.jsx("div", {
790
- className: style$2['event-item-detail'],
795
+ className: style$3['event-item-detail'],
791
796
  children: event.detail
792
797
  }) : null]
793
798
  })
@@ -846,15 +851,16 @@ const usePanelScrollShadow = (resetKey, scrollRef) => {
846
851
  };
847
852
  };
848
853
 
849
- const ViewPanel = ({
850
- date,
851
- events,
852
- loading,
853
- renderEventItem,
854
- renderPanelHeader,
855
- onEventClick,
856
- scrollable = true
857
- }) => {
854
+ const ViewPanel = _ref => {
855
+ let {
856
+ date,
857
+ events,
858
+ loading,
859
+ renderEventItem,
860
+ renderPanelHeader,
861
+ onEventClick,
862
+ scrollable = true
863
+ } = _ref;
858
864
  const {
859
865
  formatMessage
860
866
  } = reactIntl.useIntl();
@@ -910,448 +916,1468 @@ const ViewPanel = ({
910
916
  });
911
917
  };
912
918
 
913
- const LONG_PRESS_DELAY = 400;
914
- const SCROLL_CANCEL_THRESHOLD = 12;
915
- const TAP_MOVE_THRESHOLD = 8;
916
- const getPopoverKey = slot => slot ? `form:${slot.key}` : null;
917
- const getSlotIndexFromPoint = (container, clientX, clientY) => {
918
- const slotElements = container?.querySelectorAll?.('[data-slot-index]');
919
- if (!slotElements?.length) {
920
- return null;
919
+ const MINUTE_MS$1 = 60 * 1000;
920
+ const toMs = value => {
921
+ if (value === null || typeof value === 'undefined') {
922
+ return NaN;
921
923
  }
922
- let closestIndex = null;
923
- let closestDistance = Infinity;
924
- for (const candidate of slotElements) {
925
- const rect = candidate.getBoundingClientRect();
926
- const index = Number(candidate.getAttribute('data-slot-index'));
927
- if (Number.isNaN(index)) {
928
- continue;
924
+ if (typeof value === 'number') {
925
+ return value;
926
+ }
927
+ const date = dayjs__default["default"](value);
928
+ return date.isValid() ? date.valueOf() : NaN;
929
+ };
930
+ const snapToStep = (ms, {
931
+ originMs,
932
+ stepMs,
933
+ mode = 'round'
934
+ }) => {
935
+ const fn = mode === 'floor' ? Math.floor : mode === 'ceil' ? Math.ceil : Math.round;
936
+ return originMs + fn((ms - originMs) / stepMs) * stepMs;
937
+ };
938
+ const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
939
+
940
+ /**
941
+ * Clip events to the day window and lay overlapping ones out side by side.
942
+ * Returns `{ event, startMs, endMs, column, columns }` sorted by start.
943
+ */
944
+ const layoutTimelineEvents = (events, {
945
+ startMs,
946
+ endMs
947
+ }) => {
948
+ const items = (events || []).map(event => {
949
+ const s = Math.max(toMs(event.start), startMs);
950
+ const e = Math.min(toMs(event.end), endMs);
951
+ return {
952
+ event,
953
+ startMs: s,
954
+ endMs: e
955
+ };
956
+ }).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);
957
+ let cluster = [];
958
+ let clusterEnd = -Infinity;
959
+ let columnEnds = [];
960
+ const flush = () => {
961
+ cluster.forEach(item => {
962
+ item.columns = columnEnds.length;
963
+ });
964
+ cluster = [];
965
+ columnEnds = [];
966
+ };
967
+ items.forEach(item => {
968
+ if (item.startMs >= clusterEnd) {
969
+ flush();
970
+ clusterEnd = -Infinity;
929
971
  }
930
- if (clientY >= rect.top && clientY <= rect.bottom && clientX >= rect.left && clientX <= rect.right) {
931
- return index;
972
+ let column = columnEnds.findIndex(end => end <= item.startMs);
973
+ if (column === -1) {
974
+ column = columnEnds.length;
975
+ columnEnds.push(item.endMs);
976
+ } else {
977
+ columnEnds[column] = item.endMs;
932
978
  }
933
- if (clientX < rect.left || clientX > rect.right) {
934
- continue;
979
+ item.column = column;
980
+ cluster.push(item);
981
+ clusterEnd = Math.max(clusterEnd, item.endMs);
982
+ });
983
+ flush();
984
+ return items;
985
+ };
986
+
987
+ /** Merge consecutive disabled slots (same reason) into ranges. */
988
+ const mergeDisabledSlots = slots => {
989
+ const ranges = [];
990
+ (slots || []).forEach(slot => {
991
+ if (slot.status !== 'disabled') {
992
+ return;
993
+ }
994
+ const startMs = toMs(slot.start);
995
+ const endMs = toMs(slot.end);
996
+ const last = ranges[ranges.length - 1];
997
+ if (last && last.endMs === startMs && last.reason === slot.disabledReason) {
998
+ last.endMs = endMs;
999
+ return;
1000
+ }
1001
+ ranges.push({
1002
+ startMs,
1003
+ endMs,
1004
+ reason: slot.disabledReason
1005
+ });
1006
+ });
1007
+ return ranges;
1008
+ };
1009
+ const toBlockers = ranges => (ranges || []).map(range => ({
1010
+ startMs: toMs(range.startMs ?? range.start),
1011
+ endMs: toMs(range.endMs ?? range.end)
1012
+ })).filter(range => Number.isFinite(range.startMs) && Number.isFinite(range.endMs) && range.endMs > range.startMs).sort((a, b) => a.startMs - b.startMs);
1013
+
1014
+ /**
1015
+ * The free gap around `ms` between blockers, bounded by the day window.
1016
+ * Returns null when `ms` falls inside a blocker.
1017
+ */
1018
+ const getFreeGap = (ms, blockers, {
1019
+ startMs,
1020
+ endMs
1021
+ }) => {
1022
+ let gapStart = startMs;
1023
+ let gapEnd = endMs;
1024
+ for (const blocker of blockers || []) {
1025
+ if (blocker.startMs <= ms && ms < blocker.endMs) {
1026
+ return null;
935
1027
  }
936
- const centerY = (rect.top + rect.bottom) / 2;
937
- const distance = Math.abs(clientY - centerY);
938
- if (distance < closestDistance) {
939
- closestDistance = distance;
940
- closestIndex = index;
1028
+ if (blocker.endMs <= ms) {
1029
+ gapStart = Math.max(gapStart, blocker.endMs);
1030
+ } else if (blocker.startMs > ms) {
1031
+ gapEnd = Math.min(gapEnd, blocker.startMs);
941
1032
  }
942
1033
  }
943
- return closestIndex;
1034
+ if (ms < startMs || ms >= endMs || gapEnd <= gapStart) {
1035
+ return null;
1036
+ }
1037
+ return {
1038
+ startMs: gapStart,
1039
+ endMs: gapEnd
1040
+ };
944
1041
  };
945
- const clearDocumentTextSelection = () => {
946
- const selection = document.getSelection?.();
947
- if (selection && !selection.isCollapsed) {
948
- selection.removeAllRanges();
1042
+
1043
+ /** Range created by pressing at `anchorMs` and dragging to `pointerMs`, kept inside `gap`. */
1044
+ const createSelectionRange = ({
1045
+ anchorMs,
1046
+ pointerMs,
1047
+ gap,
1048
+ originMs,
1049
+ stepMs
1050
+ }) => {
1051
+ const anchorStart = clamp(snapToStep(anchorMs, {
1052
+ originMs,
1053
+ stepMs,
1054
+ mode: 'floor'
1055
+ }), gap.startMs, gap.endMs);
1056
+ const anchorEnd = Math.min(anchorStart + stepMs, gap.endMs);
1057
+ const pointer = clamp(pointerMs, gap.startMs, gap.endMs);
1058
+ if (pointer >= anchorStart) {
1059
+ const end = clamp(snapToStep(pointer, {
1060
+ originMs,
1061
+ stepMs,
1062
+ mode: 'ceil'
1063
+ }), anchorEnd, gap.endMs);
1064
+ return {
1065
+ startMs: anchorStart,
1066
+ endMs: end
1067
+ };
949
1068
  }
1069
+ const start = clamp(snapToStep(pointer, {
1070
+ originMs,
1071
+ stepMs,
1072
+ mode: 'floor'
1073
+ }), gap.startMs, anchorStart);
1074
+ return {
1075
+ startMs: start,
1076
+ endMs: anchorEnd
1077
+ };
950
1078
  };
951
- const useScheduleSelectionGesture = ({
952
- slots,
953
- selectedSlots,
954
- setSelectedSlots,
955
- allowMultiSelect,
956
- onSlotRangeChange,
957
- onFreeSlotClick
1079
+
1080
+ /** Default range for a single click: one `unitMs` block that contains the pointer, fitted into `gap`. */
1081
+ const clickSelectionRange = ({
1082
+ pointerMs,
1083
+ gap,
1084
+ originMs,
1085
+ unitMs
958
1086
  }) => {
959
- const [dragging, setDragging] = react.useState(false);
960
- const [activePopoverKey, setActivePopoverKey] = react.useState(null);
961
- const slotsRef = react.useRef(null);
962
- const draggingRef = react.useRef(false);
963
- const dragStartIndexRef = react.useRef(null);
964
- const longPressTimerRef = react.useRef(null);
965
- const touchStateRef = react.useRef(null);
966
- const slotsDataRef = react.useRef(slots);
967
- const selectedSlotsRef = react.useRef(selectedSlots);
968
- const allowMultiSelectRef = react.useRef(allowMultiSelect);
969
- const suppressTriggerUntilRef = react.useRef(0);
970
- const suppressMouseUntilRef = react.useRef(0);
971
- const scrollLockRef = react.useRef(null);
972
- const handlersRef = react.useRef({});
973
- slotsDataRef.current = slots;
974
- selectedSlotsRef.current = selectedSlots;
975
- allowMultiSelectRef.current = allowMultiSelect;
976
- const selectedKeys = react.useMemo(() => new Set(selectedSlots.map(slot => slot.key)), [selectedSlots]);
977
- const selectedAnchorKey = selectedSlots[selectedSlots.length - 1]?.key;
978
- const formPopoverKey = selectedAnchorKey ? `form:${selectedAnchorKey}` : null;
979
- const clearLongPress = react.useCallback(() => {
980
- if (longPressTimerRef.current) {
981
- window.clearTimeout(longPressTimerRef.current);
982
- longPressTimerRef.current = null;
983
- }
984
- }, []);
985
- const unlockContainerScroll = react.useCallback(() => {
986
- const container = slotsRef.current;
987
- if (!container || scrollLockRef.current == null) {
988
- return;
1087
+ const duration = Math.min(unitMs, gap.endMs - gap.startMs);
1088
+ const start = clamp(snapToStep(pointerMs, {
1089
+ originMs,
1090
+ stepMs: unitMs,
1091
+ mode: 'floor'
1092
+ }), gap.startMs, gap.endMs - duration);
1093
+ return {
1094
+ startMs: start,
1095
+ endMs: start + duration
1096
+ };
1097
+ };
1098
+ const moveSelectionRange = ({
1099
+ range,
1100
+ deltaMs,
1101
+ gap,
1102
+ originMs,
1103
+ stepMs
1104
+ }) => {
1105
+ const duration = range.endMs - range.startMs;
1106
+ const start = clamp(snapToStep(range.startMs + deltaMs, {
1107
+ originMs,
1108
+ stepMs
1109
+ }), gap.startMs, gap.endMs - duration);
1110
+ return {
1111
+ startMs: start,
1112
+ endMs: start + duration
1113
+ };
1114
+ };
1115
+ const resizeSelectionRange = ({
1116
+ range,
1117
+ edge,
1118
+ pointerMs,
1119
+ gap,
1120
+ originMs,
1121
+ stepMs,
1122
+ minMs = stepMs
1123
+ }) => {
1124
+ const snapped = snapToStep(pointerMs, {
1125
+ originMs,
1126
+ stepMs
1127
+ });
1128
+ if (edge === 'start') {
1129
+ const start = clamp(snapped, gap.startMs, range.endMs - minMs);
1130
+ return {
1131
+ startMs: start,
1132
+ endMs: range.endMs
1133
+ };
1134
+ }
1135
+ const end = clamp(snapped, range.startMs + minMs, gap.endMs);
1136
+ return {
1137
+ startMs: range.startMs,
1138
+ endMs: end
1139
+ };
1140
+ };
1141
+ const findOverlappingEvents = (range, events) => {
1142
+ if (!range) {
1143
+ return [];
1144
+ }
1145
+ return (events || []).filter(event => toMs(event.start) < range.endMs && toMs(event.end) > range.startMs);
1146
+ };
1147
+ const findOverlappingSlots = (range, slots) => {
1148
+ if (!range) {
1149
+ return [];
1150
+ }
1151
+ return (slots || []).filter(slot => toMs(slot.start) < range.endMs && toMs(slot.end) > range.startMs);
1152
+ };
1153
+
1154
+ dayjs__default["default"].extend(customParseFormat__default["default"]);
1155
+ const DEFAULT_DURATION = 60;
1156
+ const DEFAULT_STEP = 1;
1157
+ const DEFAULT_DEBOUNCE_WAIT = 300;
1158
+ const DEFAULT_SEPARATOR = ' ~ ';
1159
+ const DEFAULT_FORMAT = ['YYYY-MM-DD HH:mm', 'HH:mm'];
1160
+ const DEFAULT_MIN_VISIBLE_SPAN = 360;
1161
+ const MINUTE_MS = 60 * 1000;
1162
+ const HOUR_MS = 60 * MINUTE_MS;
1163
+ const PARSE_FORMATS = ['YYYY-MM-DD HH:mm:ss', 'YYYY-MM-DD HH:mm', 'YYYY-MM-DD'];
1164
+ const parseDateLike = value => {
1165
+ if (value == null || value === '') {
1166
+ return null;
1167
+ }
1168
+ if (dayjs__default["default"].isDayjs(value)) {
1169
+ return value.isValid() ? value : null;
1170
+ }
1171
+ const direct = dayjs__default["default"](value);
1172
+ if (direct.isValid()) {
1173
+ return direct;
1174
+ }
1175
+ if (typeof value === 'string') {
1176
+ const parsed = dayjs__default["default"](value, PARSE_FORMATS, true);
1177
+ return parsed.isValid() ? parsed : null;
1178
+ }
1179
+ return null;
1180
+ };
1181
+ const isEmptyBound = value => value == null || value === '';
1182
+ const defaultDayRange = now => {
1183
+ const day = (parseDateLike(now) || dayjs__default["default"]()).startOf('day');
1184
+ return [day, day.add(1, 'day')];
1185
+ };
1186
+
1187
+ /**
1188
+ * `null` 或 `[null, null]` 表示不限制;单侧为空表示该侧不限制;`undefined` 为当天全天。
1189
+ */
1190
+ const resolveRange = function (range, now) {
1191
+ if (now === void 0) {
1192
+ now = dayjs__default["default"]();
1193
+ }
1194
+ if (range === null) {
1195
+ return [null, null];
1196
+ }
1197
+ if (!Array.isArray(range)) {
1198
+ return defaultDayRange(now);
1199
+ }
1200
+ const start = isEmptyBound(range[0]) ? null : parseDateLike(range[0]);
1201
+ const end = isEmptyBound(range[1]) ? null : parseDateLike(range[1]);
1202
+ const startInvalid = !isEmptyBound(range[0]) && !start;
1203
+ const endInvalid = !isEmptyBound(range[1]) && !end;
1204
+ if (startInvalid || endInvalid || start && end && !end.isAfter(start)) {
1205
+ return defaultDayRange(now);
1206
+ }
1207
+ return [start, end];
1208
+ };
1209
+ const resolveDuration = (duration, rangeStart, rangeEnd) => {
1210
+ const requested = Number(duration) > 0 ? Number(duration) : DEFAULT_DURATION;
1211
+ if (!rangeStart || !rangeEnd) {
1212
+ return requested;
1213
+ }
1214
+ const rangeMinutes = rangeEnd.diff(rangeStart, 'minute');
1215
+ if (rangeMinutes > 0 && requested > rangeMinutes) {
1216
+ return rangeMinutes;
1217
+ }
1218
+ return requested;
1219
+ };
1220
+ const resolveStep = step => Number(step) > 0 ? Number(step) : DEFAULT_STEP;
1221
+ const resolveVisibleSpan = _ref => {
1222
+ let {
1223
+ visibleSpan,
1224
+ duration,
1225
+ baseDuration = duration,
1226
+ rangeStart,
1227
+ rangeEnd
1228
+ } = _ref;
1229
+ const minimum = duration * 1.5;
1230
+ let span = Number(visibleSpan) > 0 ? Number(visibleSpan) : Math.max(baseDuration * 6, DEFAULT_MIN_VISIBLE_SPAN);
1231
+ if (rangeStart && rangeEnd && !(Number(visibleSpan) > 0)) {
1232
+ span = Math.min(span, rangeEnd.diff(rangeStart, 'minute'));
1233
+ }
1234
+ return Math.max(span, minimum);
1235
+ };
1236
+ const snapMs = function (ms, stepMs, mode) {
1237
+ if (mode === void 0) {
1238
+ mode = 'round';
1239
+ }
1240
+ const anchor = dayjs__default["default"](ms).startOf('day').valueOf();
1241
+ const units = (ms - anchor) / stepMs;
1242
+ const snapped = mode === 'floor' ? Math.floor(units) : mode === 'ceil' ? Math.ceil(units) : Math.round(units);
1243
+ return anchor + snapped * stepMs;
1244
+ };
1245
+ const clampWindowStart = (startInput, _ref2) => {
1246
+ let {
1247
+ rangeStart,
1248
+ rangeEnd,
1249
+ duration,
1250
+ step,
1251
+ snap = true,
1252
+ now
1253
+ } = _ref2;
1254
+ const durationMs = duration * MINUTE_MS;
1255
+ const minMs = rangeStart ? rangeStart.valueOf() : -Infinity;
1256
+ const maxMs = rangeEnd ? rangeEnd.valueOf() - durationMs : Infinity;
1257
+ if (maxMs <= minMs) {
1258
+ return dayjs__default["default"](minMs);
1259
+ }
1260
+ const fallback = rangeStart || parseDateLike(now) || dayjs__default["default"]();
1261
+ const start = parseDateLike(startInput) || fallback;
1262
+ const stepMs = step * MINUTE_MS;
1263
+ let target = snap ? snapMs(start.valueOf(), stepMs) : start.valueOf();
1264
+ if (target > maxMs) {
1265
+ target = snap ? snapMs(maxMs, stepMs, 'floor') : maxMs;
1266
+ }
1267
+ if (target < minMs) {
1268
+ target = snap ? snapMs(minMs, stepMs, 'ceil') : minMs;
1269
+ }
1270
+ return dayjs__default["default"](Math.min(Math.max(target, minMs), maxMs));
1271
+ };
1272
+ const createTimeWindow = function (_temp) {
1273
+ let {
1274
+ start,
1275
+ duration,
1276
+ range,
1277
+ step,
1278
+ now,
1279
+ snap = true
1280
+ } = _temp === void 0 ? {} : _temp;
1281
+ const [rangeStart, rangeEnd] = resolveRange(range, now);
1282
+ const resolvedDuration = resolveDuration(duration, rangeStart, rangeEnd);
1283
+ const resolvedStep = resolveStep(step);
1284
+ const windowStart = clampWindowStart(start == null ? null : start, {
1285
+ rangeStart,
1286
+ rangeEnd,
1287
+ duration: resolvedDuration,
1288
+ step: resolvedStep,
1289
+ snap,
1290
+ now
1291
+ });
1292
+ return {
1293
+ start: windowStart,
1294
+ end: windowStart.add(resolvedDuration, 'minute'),
1295
+ rangeStart,
1296
+ rangeEnd,
1297
+ duration: resolvedDuration,
1298
+ step: resolvedStep
1299
+ };
1300
+ };
1301
+
1302
+ /**
1303
+ * 拖动窗口一侧边界:另一侧保持不动,拖动侧按 step 吸附,并受最短 / 最长时长与范围约束。
1304
+ */
1305
+ const resizeTimeWindow = _ref3 => {
1306
+ let {
1307
+ edge,
1308
+ pointerMs,
1309
+ startMs,
1310
+ endMs,
1311
+ step,
1312
+ rangeStart,
1313
+ rangeEnd,
1314
+ minDuration,
1315
+ maxDuration
1316
+ } = _ref3;
1317
+ const stepMs = resolveStep(step) * MINUTE_MS;
1318
+ const minMs = (Number(minDuration) > 0 ? Number(minDuration) : resolveStep(step)) * MINUTE_MS;
1319
+ const maxMs = Number(maxDuration) > 0 ? Number(maxDuration) * MINUTE_MS : Infinity;
1320
+ const rangeStartMs = rangeStart ? dayjs__default["default"](rangeStart).valueOf() : -Infinity;
1321
+ const rangeEndMs = rangeEnd ? dayjs__default["default"](rangeEnd).valueOf() : Infinity;
1322
+ if (edge === 'start') {
1323
+ let next = snapMs(pointerMs, stepMs);
1324
+ next = Math.min(next, endMs - minMs);
1325
+ next = Math.max(next, endMs - maxMs, rangeStartMs);
1326
+ return {
1327
+ startMs: next,
1328
+ duration: (endMs - next) / MINUTE_MS
1329
+ };
1330
+ }
1331
+ let next = snapMs(pointerMs, stepMs);
1332
+ next = Math.max(next, startMs + minMs);
1333
+ next = Math.min(next, startMs + maxMs, rangeEndMs);
1334
+ return {
1335
+ startMs,
1336
+ duration: (next - startMs) / MINUTE_MS
1337
+ };
1338
+ };
1339
+ const toWindowValue = window => [window.start.toDate(), window.end.toDate()];
1340
+ const getViewport = (_ref4, visibleSpanMinutes) => {
1341
+ let {
1342
+ startMs,
1343
+ duration
1344
+ } = _ref4;
1345
+ const spanMs = visibleSpanMinutes * MINUTE_MS;
1346
+ const centerMs = startMs + duration * MINUTE_MS / 2;
1347
+ return {
1348
+ startMs: centerMs - spanMs / 2,
1349
+ endMs: centerMs + spanMs / 2,
1350
+ spanMs
1351
+ };
1352
+ };
1353
+ const wheelDeltaToPx = _ref5 => {
1354
+ let {
1355
+ delta,
1356
+ deltaMode = 0,
1357
+ trackWidth
1358
+ } = _ref5;
1359
+ if (deltaMode === 1) {
1360
+ return delta * 16;
1361
+ }
1362
+ if (deltaMode === 2) {
1363
+ return delta * (trackWidth > 0 ? trackWidth : 1);
1364
+ }
1365
+ return delta;
1366
+ };
1367
+
1368
+ /**
1369
+ * 把滚轮像素累加为整步数:步数与刻度尺比例一致,单次离散滚动至少移动一步。
1370
+ */
1371
+ const consumeWheelSteps = _ref6 => {
1372
+ let {
1373
+ accumulatedPx,
1374
+ deltaPx,
1375
+ pxPerStep,
1376
+ discrete
1377
+ } = _ref6;
1378
+ const sameDirection = accumulatedPx === 0 || Math.sign(accumulatedPx) === Math.sign(deltaPx);
1379
+ const total = (sameDirection ? accumulatedPx : 0) + deltaPx;
1380
+ const perStep = pxPerStep > 0 ? pxPerStep : 1;
1381
+ let steps = Math.trunc(total / perStep) || 0;
1382
+ let rest = total - steps * perStep;
1383
+ if (steps === 0 && discrete && deltaPx !== 0) {
1384
+ steps = Math.sign(deltaPx);
1385
+ rest = 0;
1386
+ }
1387
+ return {
1388
+ steps,
1389
+ accumulatedPx: rest
1390
+ };
1391
+ };
1392
+ const DEFAULT_INERTIA_TIME_CONSTANT = 325;
1393
+ const VELOCITY_WINDOW_MS = 120;
1394
+
1395
+ /**
1396
+ * `true` 为默认强度 1,数字为强度倍数,`false` / `0` 关闭。返回滑行衰减时间常数(ms),0 表示关闭。
1397
+ */
1398
+ const resolveInertia = inertia => {
1399
+ if (inertia === false || inertia == null) {
1400
+ return inertia == null ? DEFAULT_INERTIA_TIME_CONSTANT : 0;
1401
+ }
1402
+ if (inertia === true) {
1403
+ return DEFAULT_INERTIA_TIME_CONSTANT;
1404
+ }
1405
+ const strength = Number(inertia);
1406
+ return strength > 0 ? DEFAULT_INERTIA_TIME_CONSTANT * strength : 0;
1407
+ };
1408
+
1409
+ /**
1410
+ * samples: [{ t, ms }],按时间顺序;返回最近一段内每毫秒移动的时间量。
1411
+ */
1412
+ const estimateVelocity = (samples, now) => {
1413
+ if (!samples || samples.length < 2) {
1414
+ return 0;
1415
+ }
1416
+ const latest = samples[samples.length - 1];
1417
+ const reference = now == null ? latest.t : now;
1418
+ if (reference - latest.t > VELOCITY_WINDOW_MS / 2) {
1419
+ return 0;
1420
+ }
1421
+ const recent = samples.filter(sample => latest.t - sample.t <= VELOCITY_WINDOW_MS);
1422
+ if (recent.length < 2) {
1423
+ return 0;
1424
+ }
1425
+ const first = recent[0];
1426
+ const elapsed = latest.t - first.t;
1427
+ return elapsed > 0 ? (latest.ms - first.ms) / elapsed : 0;
1428
+ };
1429
+ const timeAtClientX = _ref7 => {
1430
+ let {
1431
+ clientX,
1432
+ rectLeft,
1433
+ rectWidth,
1434
+ rangeStartMs,
1435
+ rangeEndMs,
1436
+ grabOffsetMs = 0
1437
+ } = _ref7;
1438
+ const span = Math.max(rangeEndMs - rangeStartMs, 1);
1439
+ const width = rectWidth > 0 ? rectWidth : 1;
1440
+ const ratio = Math.min(Math.max((clientX - rectLeft) / width, 0), 1);
1441
+ return rangeStartMs + ratio * span - grabOffsetMs;
1442
+ };
1443
+ const formatTimeWindowParts = _ref8 => {
1444
+ let {
1445
+ start,
1446
+ end,
1447
+ format = DEFAULT_FORMAT
1448
+ } = _ref8;
1449
+ const startDate = parseDateLike(start);
1450
+ const endDate = parseDateLike(end);
1451
+ if (!startDate || !endDate || typeof format === 'function') {
1452
+ return null;
1453
+ }
1454
+ const formats = Array.isArray(format) ? format : [format, format];
1455
+ const startText = startDate.format(formats[0] || DEFAULT_FORMAT[0]);
1456
+ let endFormat = formats[1] || formats[0] || DEFAULT_FORMAT[1];
1457
+ const endAtMidnight = endDate.valueOf() === endDate.startOf('day').valueOf();
1458
+ if (format === DEFAULT_FORMAT && !endAtMidnight && !endDate.isSame(startDate, 'day')) {
1459
+ endFormat = 'MM-DD HH:mm';
1460
+ }
1461
+ return [startText, endDate.format(endFormat)];
1462
+ };
1463
+ const formatTimeWindow = _ref9 => {
1464
+ let {
1465
+ start,
1466
+ end,
1467
+ format = DEFAULT_FORMAT,
1468
+ separator = DEFAULT_SEPARATOR
1469
+ } = _ref9;
1470
+ if (typeof format === 'function') {
1471
+ const startDate = parseDateLike(start);
1472
+ const endDate = parseDateLike(end);
1473
+ return startDate && endDate ? format(startDate.toDate(), endDate.toDate()) : '';
1474
+ }
1475
+ const parts = formatTimeWindowParts({
1476
+ start,
1477
+ end,
1478
+ format
1479
+ });
1480
+ return parts ? `${parts[0]}${separator}${parts[1]}` : '';
1481
+ };
1482
+ const LABEL_INTERVALS = [1, 2, 3, 4, 6, 8, 12, 24];
1483
+ const pickLabelEvery = (pxPerHour, minLabelPx) => {
1484
+ if (!(pxPerHour > 0)) {
1485
+ return 3;
1486
+ }
1487
+ return LABEL_INTERVALS.find(every => pxPerHour * every >= minLabelPx) || 24;
1488
+ };
1489
+ const TICK_MINUTES = [1, 2, 5, 10, 15, 30, 60];
1490
+ const MIN_TICK_GAP_PX = 7;
1491
+ const pickTickMinutes = (pxPerHour, labelEvery) => {
1492
+ const fit = TICK_MINUTES.find(minutes => pxPerHour * minutes / 60 >= MIN_TICK_GAP_PX);
1493
+ return fit || labelEvery * 60;
1494
+ };
1495
+
1496
+ /**
1497
+ * 只生成当前视窗 [viewStart, viewEnd] 内的刻度;`outOfRange` 标出超出可选范围的刻度。
1498
+ */
1499
+ const buildScaleTicks = function (viewStart, viewEnd, _temp2) {
1500
+ let {
1501
+ trackWidth,
1502
+ rangeStart,
1503
+ rangeEnd,
1504
+ minLabelPx = 52
1505
+ } = _temp2 === void 0 ? {} : _temp2;
1506
+ const viewStartMs = dayjs__default["default"](viewStart).valueOf();
1507
+ const viewEndMs = dayjs__default["default"](viewEnd).valueOf();
1508
+ const span = viewEndMs - viewStartMs;
1509
+ if (span <= 0) {
1510
+ return [];
1511
+ }
1512
+ const pxPerHour = trackWidth > 0 ? trackWidth * HOUR_MS / span : 0;
1513
+ const labelEvery = trackWidth > 0 ? pickLabelEvery(pxPerHour, minLabelPx) : span / HOUR_MS <= 6 ? 1 : 3;
1514
+ const tickMinutes = trackWidth > 0 ? pickTickMinutes(pxPerHour, labelEvery) : 60;
1515
+ const tickMs = tickMinutes * MINUTE_MS;
1516
+ const rangeStartMs = rangeStart ? dayjs__default["default"](rangeStart).valueOf() : -Infinity;
1517
+ const rangeEndMs = rangeEnd ? dayjs__default["default"](rangeEnd).valueOf() : Infinity;
1518
+ const ticks = [];
1519
+ let cursorMs = snapMs(viewStartMs, tickMs, 'ceil');
1520
+ while (cursorMs <= viewEndMs) {
1521
+ const cursor = dayjs__default["default"](cursorMs);
1522
+ const major = cursor.minute() === 0;
1523
+ const midnight = major && cursor.hour() === 0;
1524
+ const minute = cursor.minute();
1525
+ const level = midnight ? 'day' : major ? 'hour' : minute === 30 ? 'half' : minute % 15 === 0 && tickMinutes < 15 ? 'quarter' : 'minute';
1526
+ ticks.push({
1527
+ key: cursorMs,
1528
+ ratio: (cursorMs - viewStartMs) / span,
1529
+ major,
1530
+ level,
1531
+ label: midnight ? cursor.format('MM-DD') : cursor.format('HH:mm'),
1532
+ showLabel: major && cursor.hour() % labelEvery === 0,
1533
+ outOfRange: cursorMs < rangeStartMs || cursorMs > rangeEndMs
1534
+ });
1535
+ cursorMs = snapMs(cursorMs + tickMs, tickMs);
1536
+ }
1537
+ return ticks;
1538
+ };
1539
+ const formatDurationParts = minutes => {
1540
+ const total = Math.max(Math.round(minutes), 0);
1541
+ return {
1542
+ hours: Math.floor(total / 60),
1543
+ minutes: total % 60
1544
+ };
1545
+ };
1546
+
1547
+ 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"};
1548
+
1549
+ const GRIP_MIN_WIDTH = 28;
1550
+ const CLICK_MOVE_TOLERANCE = 4;
1551
+ const DISCRETE_WHEEL_DELTA = 50;
1552
+ const VISUAL_EASE_MS = 70;
1553
+ const WHEEL_IDLE_MS = 140;
1554
+ const WHEEL_NOTCH_EASE_MS = 90;
1555
+ const WHEEL_INERTIA_SCALE = 0.6;
1556
+ const sameInstant = (left, right) => {
1557
+ const leftDate = parseDateLike(left);
1558
+ const rightDate = parseDateLike(right);
1559
+ if (!leftDate || !rightDate) {
1560
+ return false;
1561
+ }
1562
+ return Math.abs(leftDate.valueOf() - rightDate.valueOf()) < 1000;
1563
+ };
1564
+ const readWindowStart = value => {
1565
+ if (!Array.isArray(value)) {
1566
+ return null;
1567
+ }
1568
+ return parseDateLike(value[0]);
1569
+ };
1570
+ const isDiscreteWheel = (event, deltaPx) => {
1571
+ if (event.deltaMode !== 0) {
1572
+ return true;
1573
+ }
1574
+ const legacy = event.wheelDeltaY || event.wheelDeltaX;
1575
+ if (legacy && Math.abs(legacy) % 120 === 0) {
1576
+ return true;
1577
+ }
1578
+ return Math.abs(deltaPx) >= DISCRETE_WHEEL_DELTA;
1579
+ };
1580
+ const useElementWidth = ref => {
1581
+ const [width, setWidth] = react.useState(0);
1582
+ react.useEffect(() => {
1583
+ const node = ref.current;
1584
+ if (!node) {
1585
+ return undefined;
989
1586
  }
990
- const scrollTop = scrollLockRef.current;
991
- container.style.overflow = '';
992
- container.style.touchAction = '';
993
- container.scrollTop = scrollTop;
994
- scrollLockRef.current = null;
995
- }, []);
996
- const lockContainerScroll = react.useCallback(() => {
997
- const container = slotsRef.current;
998
- if (!container || scrollLockRef.current != null) {
999
- return;
1587
+ setWidth(node.getBoundingClientRect().width);
1588
+ if (typeof ResizeObserver === 'undefined') {
1589
+ return undefined;
1000
1590
  }
1001
- scrollLockRef.current = container.scrollTop;
1002
- container.style.overflow = 'hidden';
1003
- container.style.touchAction = 'none';
1004
- }, []);
1005
- const resetGestureState = react.useCallback(() => {
1006
- clearLongPress();
1007
- const container = slotsRef.current;
1008
- const touchState = touchStateRef.current;
1009
- if (container && touchState?.pointerId != null && container.hasPointerCapture?.(touchState.pointerId)) {
1010
- try {
1011
- container.releasePointerCapture(touchState.pointerId);
1012
- } catch {
1013
- // Ignore capture release errors on stale pointers.
1591
+ const observer = new ResizeObserver(entries => {
1592
+ const entry = entries[0];
1593
+ if (entry) {
1594
+ setWidth(entry.contentRect.width);
1014
1595
  }
1596
+ });
1597
+ observer.observe(node);
1598
+ return () => observer.disconnect();
1599
+ }, [ref]);
1600
+ return width;
1601
+ };
1602
+ const TimeWindowInner = _ref => {
1603
+ let {
1604
+ value,
1605
+ defaultValue,
1606
+ onChange,
1607
+ duration,
1608
+ range,
1609
+ step,
1610
+ visibleSpan,
1611
+ debounceWait = DEFAULT_DEBOUNCE_WAIT,
1612
+ format = DEFAULT_FORMAT,
1613
+ separator = DEFAULT_SEPARATOR,
1614
+ showDuration = true,
1615
+ size = 'default',
1616
+ inertia = true,
1617
+ resizable = true,
1618
+ wheelEffect = true,
1619
+ lockable = false,
1620
+ locked,
1621
+ defaultLocked = false,
1622
+ onLockedChange,
1623
+ minDuration,
1624
+ maxDuration,
1625
+ className,
1626
+ style: styleProp
1627
+ } = _ref;
1628
+ const {
1629
+ formatMessage
1630
+ } = reactIntl.useIntl();
1631
+ const isControlled = typeof value !== 'undefined';
1632
+ const [innerValue, setInnerValue] = react.useState(() => {
1633
+ if (Array.isArray(defaultValue) && readWindowStart(defaultValue)) {
1634
+ return defaultValue;
1015
1635
  }
1016
- draggingRef.current = false;
1017
- dragStartIndexRef.current = null;
1018
- touchStateRef.current = null;
1019
- container?.classList.remove('schedule-calendar-touch-dragging');
1020
- unlockContainerScroll();
1021
- setDragging(false);
1022
- }, [clearLongPress, unlockContainerScroll]);
1023
- const selectSlots = react.useCallback(nextSlots => {
1024
- selectedSlotsRef.current = nextSlots;
1025
- if (!nextSlots.length) {
1026
- setActivePopoverKey(null);
1027
- resetGestureState();
1028
- }
1029
- setSelectedSlots(nextSlots);
1030
- onSlotRangeChange?.(nextSlots, {
1031
- action: nextSlots.length ? 'select' : 'clear'
1636
+ return toWindowValue(createTimeWindow({
1637
+ duration,
1638
+ range,
1639
+ step
1640
+ }));
1641
+ });
1642
+ const [draftStart, setDraftStart] = react.useState(null);
1643
+ const [draftDuration, setDraftDuration] = react.useState(null);
1644
+ const [dragging, setDragging] = react.useState(false);
1645
+ const [innerLocked, setInnerLocked] = react.useState(defaultLocked);
1646
+ const lockedControlled = typeof locked === 'boolean';
1647
+ const isLocked = lockable && (lockedControlled ? locked : innerLocked);
1648
+ const [resizeView, setResizeView] = react.useState(null);
1649
+ const resizeRef = react.useRef(null);
1650
+ const resizeEdge = resizeView ? resizeView.edge : null;
1651
+ const rootRef = react.useRef(null);
1652
+ const trackRef = react.useRef(null);
1653
+ const dragRef = react.useRef(null);
1654
+ const wheelAccumRef = react.useRef(0);
1655
+ const interactingRef = react.useRef(false);
1656
+ const timerRef = react.useRef(null);
1657
+ const pendingRef = react.useRef(null);
1658
+ const onChangeRef = react.useRef(onChange);
1659
+ const onLockedChangeRef = react.useRef(onLockedChange);
1660
+ const isLockedRef = react.useRef(isLocked);
1661
+ onLockedChangeRef.current = onLockedChange;
1662
+ isLockedRef.current = isLocked;
1663
+ const trackWidth = useElementWidth(trackRef);
1664
+ const committedValue = isControlled ? value : innerValue;
1665
+ const committedStart = readWindowStart(committedValue);
1666
+ const committedMs = committedStart ? committedStart.valueOf() : null;
1667
+ const anchorDay = committedStart ? committedStart.startOf('day').valueOf() : undefined;
1668
+ const draftMs = draftStart ? draftStart.valueOf() : null;
1669
+ const committedEnd = Array.isArray(committedValue) ? parseDateLike(committedValue[1]) : null;
1670
+ const committedEndMs = committedEnd ? committedEnd.valueOf() : null;
1671
+ const committedDuration = committedStart && committedEnd && committedEnd.isAfter(committedStart) ? committedEnd.diff(committedStart, 'minute', true) : null;
1672
+ const activeDuration = draftDuration != null ? draftDuration : committedDuration != null ? committedDuration : duration;
1673
+ const initialDurationRef = react.useRef(committedDuration || DEFAULT_DURATION);
1674
+ const optionsRef = react.useRef(null);
1675
+ onChangeRef.current = onChange;
1676
+ optionsRef.current = {
1677
+ duration: activeDuration,
1678
+ range,
1679
+ step,
1680
+ now: anchorDay
1681
+ };
1682
+ const baseWindow = react.useMemo(() => {
1683
+ const start = draftMs == null ? committedMs : draftMs;
1684
+ return createTimeWindow({
1685
+ start,
1686
+ duration: activeDuration,
1687
+ range,
1688
+ step,
1689
+ now: anchorDay
1032
1690
  });
1033
- }, [onSlotRangeChange, resetGestureState, setSelectedSlots]);
1034
- const getSelectableRange = react.useCallback((startIndex, endIndex) => {
1035
- const [start, end] = [Math.min(startIndex, endIndex), Math.max(startIndex, endIndex)];
1036
- return slotsDataRef.current.slice(start, end + 1).filter(slot => slot.status === 'free');
1037
- }, []);
1038
- const showFormPopoverForSlots = react.useCallback(slotsList => {
1039
- const anchorSlot = slotsList[slotsList.length - 1];
1040
- if (anchorSlot) {
1041
- setActivePopoverKey(getPopoverKey(anchorSlot));
1691
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1692
+ }, [anchorDay, committedMs, draftMs, activeDuration, step, JSON.stringify(range === undefined ? 'default' : range)]);
1693
+ const visibleSpanMinutes = resolveVisibleSpan({
1694
+ visibleSpan,
1695
+ duration: baseWindow.duration,
1696
+ baseDuration: Number(duration) > 0 ? Number(duration) : initialDurationRef.current,
1697
+ rangeStart: baseWindow.rangeStart,
1698
+ rangeEnd: baseWindow.rangeEnd
1699
+ });
1700
+ const baseStartMs = baseWindow.start.valueOf();
1701
+ const rangeStartMs = baseWindow.rangeStart ? baseWindow.rangeStart.valueOf() : null;
1702
+ const rangeEndMs = baseWindow.rangeEnd ? baseWindow.rangeEnd.valueOf() : null;
1703
+ const durationMs = baseWindow.duration * 60 * 1000;
1704
+ const [visualMs, setVisualMs] = react.useState(null);
1705
+ const visualRef = react.useRef(null);
1706
+ const visualTargetRef = react.useRef(null);
1707
+ const frameRef = react.useRef(null);
1708
+ const lastFrameRef = react.useRef(0);
1709
+ const wheelActiveRef = react.useRef(false);
1710
+ const wheelIdleRef = react.useRef(null);
1711
+ const easeRef = react.useRef(VISUAL_EASE_MS);
1712
+ const glidingRef = react.useRef(false);
1713
+ const wheelBaseRef = react.useRef(null);
1714
+ const wheelSamplesRef = react.useRef([]);
1715
+ const wheelDirectionRef = react.useRef(0);
1716
+ const inertiaTau = resolveInertia(inertia);
1717
+ const inertiaRef = react.useRef(inertiaTau);
1718
+ inertiaRef.current = inertiaTau;
1719
+ const clampVisual = ms => {
1720
+ let next = ms;
1721
+ if (rangeEndMs != null) {
1722
+ next = Math.min(next, rangeEndMs - durationMs);
1042
1723
  }
1043
- }, []);
1044
- const startSlotSelection = react.useCallback((slot, index, {
1045
- showPopover = true,
1046
- lockScroll = false
1047
- } = {}) => {
1048
- clearDocumentTextSelection();
1049
- setActivePopoverKey(getPopoverKey(slot));
1050
- draggingRef.current = true;
1051
- dragStartIndexRef.current = index;
1052
- slotsRef.current?.classList.add('schedule-calendar-touch-dragging');
1053
- if (lockScroll) {
1054
- lockContainerScroll();
1055
- }
1056
- setDragging(true);
1057
- selectSlots([slot]);
1058
- if (!showPopover) {
1059
- suppressTriggerUntilRef.current = Date.now() + 700;
1060
- setActivePopoverKey(null);
1061
- }
1062
- onFreeSlotClick?.(slot);
1063
- }, [lockContainerScroll, onFreeSlotClick, selectSlots]);
1064
- const finishSelection = react.useCallback(({
1065
- fromTouch = false
1066
- } = {}) => {
1067
- const hadSelection = selectedSlotsRef.current.length > 0;
1068
- resetGestureState();
1069
- if (hadSelection) {
1070
- if (fromTouch) {
1071
- suppressTriggerUntilRef.current = Date.now() + 500;
1072
- }
1073
- showFormPopoverForSlots(selectedSlotsRef.current);
1724
+ if (rangeStartMs != null) {
1725
+ next = Math.max(next, rangeStartMs);
1074
1726
  }
1075
- }, [resetGestureState, showFormPopoverForSlots]);
1076
- const updateSelectionRange = react.useCallback(index => {
1077
- const startIndex = dragStartIndexRef.current;
1078
- if (startIndex === null || !allowMultiSelectRef.current) {
1079
- return;
1727
+ return next;
1728
+ };
1729
+ const clampVisualRef = react.useRef(clampVisual);
1730
+ clampVisualRef.current = clampVisual;
1731
+ const runFrame = time => {
1732
+ const target = visualTargetRef.current;
1733
+ const current = visualRef.current == null ? target : visualRef.current;
1734
+ const dt = lastFrameRef.current ? Math.min(time - lastFrameRef.current, 64) : 16;
1735
+ lastFrameRef.current = time;
1736
+ const diff = target - current;
1737
+ const settled = Math.abs(diff) < 1000;
1738
+ const next = settled ? target : current + diff * (1 - Math.exp(-dt / easeRef.current));
1739
+ visualRef.current = next;
1740
+ setVisualMs(next);
1741
+ if (glidingRef.current) {
1742
+ moveToRef.current(next);
1080
1743
  }
1081
- const nextSlots = getSelectableRange(startIndex, index);
1082
- selectSlots(nextSlots);
1083
- }, [getSelectableRange, selectSlots]);
1084
- const shouldIgnoreMouseSelectionEvent = react.useCallback(() => {
1085
- return !!touchStateRef.current || Date.now() < suppressMouseUntilRef.current;
1086
- }, []);
1087
- const handleMouseDown = react.useCallback((event, slot, index) => {
1088
- if (shouldIgnoreMouseSelectionEvent()) {
1744
+ if (settled) {
1745
+ frameRef.current = null;
1746
+ lastFrameRef.current = 0;
1747
+ easeRef.current = VISUAL_EASE_MS;
1748
+ glidingRef.current = false;
1089
1749
  return;
1090
1750
  }
1091
- if (slot.status !== 'free') {
1092
- return;
1751
+ frameRef.current = requestAnimationFrame(runFrame);
1752
+ };
1753
+ const stopGlide = () => {
1754
+ if (!glidingRef.current) {
1755
+ return false;
1093
1756
  }
1094
- event.preventDefault();
1095
- startSlotSelection(slot, index);
1096
- }, [shouldIgnoreMouseSelectionEvent, startSlotSelection]);
1097
- const handleMouseEnter = react.useCallback(index => {
1098
- if (shouldIgnoreMouseSelectionEvent() || !draggingRef.current) {
1099
- return;
1757
+ glidingRef.current = false;
1758
+ wheelBaseRef.current = null;
1759
+ wheelSamplesRef.current = [];
1760
+ wheelDirectionRef.current = 0;
1761
+ easeRef.current = VISUAL_EASE_MS;
1762
+ if (frameRef.current) {
1763
+ cancelAnimationFrame(frameRef.current);
1764
+ frameRef.current = null;
1765
+ lastFrameRef.current = 0;
1100
1766
  }
1101
- updateSelectionRange(index);
1102
- }, [shouldIgnoreMouseSelectionEvent, updateSelectionRange]);
1103
- const handleMouseUp = react.useCallback(() => {
1104
- if (shouldIgnoreMouseSelectionEvent()) {
1105
- return;
1767
+ visualTargetRef.current = visualRef.current;
1768
+ return true;
1769
+ };
1770
+ const glideTo = function (ms, easeMs) {
1771
+ if (easeMs === void 0) {
1772
+ easeMs = inertiaTau;
1106
1773
  }
1107
- finishSelection();
1108
- }, [finishSelection, shouldIgnoreMouseSelectionEvent]);
1109
- const handleMouseLeave = react.useCallback(() => {
1110
- if (shouldIgnoreMouseSelectionEvent()) {
1774
+ const snapped = createTimeWindow({
1775
+ start: ms,
1776
+ ...optionsRef.current
1777
+ }).start.valueOf();
1778
+ glidingRef.current = true;
1779
+ easeRef.current = easeMs;
1780
+ setVisualTarget(snapped);
1781
+ };
1782
+ const setVisualTarget = function (ms, _temp) {
1783
+ let {
1784
+ immediate = false
1785
+ } = _temp === void 0 ? {} : _temp;
1786
+ const target = clampVisualRef.current(ms);
1787
+ visualTargetRef.current = target;
1788
+ if (immediate || typeof requestAnimationFrame === 'undefined') {
1789
+ if (frameRef.current) {
1790
+ cancelAnimationFrame(frameRef.current);
1791
+ frameRef.current = null;
1792
+ lastFrameRef.current = 0;
1793
+ }
1794
+ visualRef.current = target;
1795
+ setVisualMs(target);
1111
1796
  return;
1112
1797
  }
1113
- finishSelection();
1114
- }, [finishSelection, shouldIgnoreMouseSelectionEvent]);
1115
- const handlePopoverOpenChange = react.useCallback((open, currentPopoverKey, {
1116
- isForm = false
1117
- } = {}) => {
1118
- if (Date.now() < suppressTriggerUntilRef.current) {
1798
+ if (!frameRef.current) {
1799
+ frameRef.current = requestAnimationFrame(runFrame);
1800
+ }
1801
+ };
1802
+ const setVisualTargetRef = react.useRef(setVisualTarget);
1803
+ setVisualTargetRef.current = setVisualTarget;
1804
+ const glideToRef = react.useRef(glideTo);
1805
+ glideToRef.current = glideTo;
1806
+ const stopGlideRef = react.useRef(stopGlide);
1807
+ stopGlideRef.current = stopGlide;
1808
+ react.useEffect(() => () => {
1809
+ if (frameRef.current) {
1810
+ cancelAnimationFrame(frameRef.current);
1811
+ }
1812
+ clearTimeout(wheelIdleRef.current);
1813
+ }, []);
1814
+ react.useEffect(() => {
1815
+ if (dragRef.current || resizeRef.current || wheelActiveRef.current || glidingRef.current) {
1119
1816
  return;
1120
1817
  }
1121
- if (!open && isForm) {
1122
- selectSlots([]);
1818
+ if (visualRef.current == null) {
1819
+ visualRef.current = baseStartMs;
1820
+ visualTargetRef.current = baseStartMs;
1123
1821
  return;
1124
1822
  }
1125
- setActivePopoverKey(prevKey => {
1126
- if (open) {
1127
- return currentPopoverKey;
1128
- }
1129
- return prevKey === currentPopoverKey ? null : prevKey;
1823
+ setVisualTargetRef.current(baseStartMs);
1824
+ }, [baseStartMs]);
1825
+ const displayStartMs = resizeView || visualMs == null ? baseStartMs : visualMs;
1826
+ const viewport = resizeView || getViewport({
1827
+ startMs: displayStartMs,
1828
+ duration: baseWindow.duration
1829
+ }, visibleSpanMinutes);
1830
+ const viewRef = react.useRef(null);
1831
+ const baseWindowRef = react.useRef(baseWindow);
1832
+ baseWindowRef.current = baseWindow;
1833
+ viewRef.current = viewport;
1834
+ const ticks = react.useMemo(() => buildScaleTicks(viewport.startMs, viewport.endMs, {
1835
+ trackWidth,
1836
+ rangeStart: rangeStartMs,
1837
+ rangeEnd: rangeEndMs
1838
+ }), [viewport.startMs, viewport.endMs, trackWidth, rangeStartMs, rangeEndMs]);
1839
+ const toPercent = ms => (ms - viewport.startMs) / viewport.spanMs * 100;
1840
+ const width = durationMs / viewport.spanMs * 100;
1841
+ const left = toPercent(displayStartMs);
1842
+ const windowPx = width / 100 * trackWidth;
1843
+ const leftShade = rangeStartMs != null ? Math.min(Math.max(toPercent(rangeStartMs), 0), 100) : 0;
1844
+ const rightShade = rangeEndMs != null ? Math.min(Math.max(100 - toPercent(rangeEndMs), 0), 100) : 0;
1845
+ const label = react.useMemo(() => {
1846
+ if (typeof format === 'function') {
1847
+ return format(baseWindow.start.toDate(), baseWindow.end.toDate());
1848
+ }
1849
+ const [startText, endText] = formatTimeWindowParts({
1850
+ start: baseWindow.start,
1851
+ end: baseWindow.end,
1852
+ format
1130
1853
  });
1131
- }, [selectSlots]);
1132
- handlersRef.current = {
1133
- clearLongPress,
1134
- finishSelection,
1135
- onFreeSlotClick,
1136
- resetGestureState,
1137
- selectSlots,
1138
- showFormPopoverForSlots,
1139
- startSlotSelection,
1140
- updateSelectionRange
1854
+ return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1855
+ children: [/*#__PURE__*/jsxRuntime.jsx("span", {
1856
+ children: startText
1857
+ }), /*#__PURE__*/jsxRuntime.jsx("span", {
1858
+ className: style$2.separator,
1859
+ children: separator
1860
+ }), /*#__PURE__*/jsxRuntime.jsx("span", {
1861
+ children: endText
1862
+ })]
1863
+ });
1864
+ }, [baseWindow.end, baseWindow.start, format, separator]);
1865
+ const ariaValueText = formatTimeWindow({
1866
+ start: baseWindow.start,
1867
+ end: baseWindow.end,
1868
+ format: typeof format === 'function' ? DEFAULT_FORMAT : format,
1869
+ separator: typeof separator === 'string' ? separator : DEFAULT_SEPARATOR
1870
+ });
1871
+ const durationText = react.useMemo(() => {
1872
+ const {
1873
+ hours,
1874
+ minutes
1875
+ } = formatDurationParts(baseWindow.duration);
1876
+ const parts = [];
1877
+ if (hours) {
1878
+ parts.push(formatMessage({
1879
+ id: 'TimeWindow.hours'
1880
+ }, {
1881
+ count: hours
1882
+ }));
1883
+ }
1884
+ if (minutes || !hours) {
1885
+ parts.push(formatMessage({
1886
+ id: 'TimeWindow.minutes'
1887
+ }, {
1888
+ count: minutes
1889
+ }));
1890
+ }
1891
+ return parts.join(' ');
1892
+ }, [baseWindow.duration, formatMessage]);
1893
+ const clearTimer = () => {
1894
+ if (timerRef.current) {
1895
+ clearTimeout(timerRef.current);
1896
+ timerRef.current = null;
1897
+ }
1141
1898
  };
1899
+ react.useEffect(() => clearTimer, []);
1142
1900
  react.useEffect(() => {
1143
- const container = slotsRef.current;
1144
- if (!container) {
1145
- return undefined;
1901
+ if (interactingRef.current || draftMs == null) {
1902
+ return;
1146
1903
  }
1147
- const releasePointerCapture = pointerId => {
1148
- if (pointerId == null || !container.hasPointerCapture?.(pointerId)) {
1149
- return;
1904
+ const pending = Array.isArray(pendingRef.current) ? pendingRef.current : null;
1905
+ if (pending && sameInstant(committedMs, pending[0]) && sameInstant(committedEndMs, pending[1])) {
1906
+ pendingRef.current = null;
1907
+ setDraftStart(null);
1908
+ setDraftDuration(null);
1909
+ }
1910
+ }, [committedMs, committedEndMs, draftMs]);
1911
+ const publish = nextWindow => {
1912
+ const nextValue = toWindowValue(nextWindow);
1913
+ baseWindowRef.current = nextWindow;
1914
+ pendingRef.current = nextValue;
1915
+ setDraftStart(nextWindow.start);
1916
+ setDraftDuration(nextWindow.duration);
1917
+ clearTimer();
1918
+ timerRef.current = setTimeout(() => {
1919
+ timerRef.current = null;
1920
+ if (!isControlled) {
1921
+ setInnerValue(nextValue);
1150
1922
  }
1151
- try {
1152
- container.releasePointerCapture(pointerId);
1153
- } catch {
1154
- // Ignore capture release errors on stale pointers.
1923
+ if (onChangeRef.current) {
1924
+ onChangeRef.current(nextValue);
1155
1925
  }
1926
+ }, debounceWait);
1927
+ };
1928
+ const moveTo = startInput => {
1929
+ const current = baseWindowRef.current;
1930
+ const nextWindow = createTimeWindow({
1931
+ start: startInput,
1932
+ ...optionsRef.current
1933
+ });
1934
+ if (nextWindow.start.valueOf() === current.start.valueOf()) {
1935
+ return;
1936
+ }
1937
+ publish(nextWindow);
1938
+ };
1939
+ const moveToRef = react.useRef(moveTo);
1940
+ moveToRef.current = moveTo;
1941
+ const durationPropRef = react.useRef(duration);
1942
+ react.useEffect(() => {
1943
+ if (durationPropRef.current === duration) {
1944
+ return;
1945
+ }
1946
+ durationPropRef.current = duration;
1947
+ publish(createTimeWindow({
1948
+ ...optionsRef.current,
1949
+ start: baseWindowRef.current.start,
1950
+ duration
1951
+ }));
1952
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1953
+ }, [duration]);
1954
+ const pointerTimeAt = (clientX, view) => {
1955
+ const rect = trackRef.current ? trackRef.current.getBoundingClientRect() : {
1956
+ left: 0,
1957
+ width: 1
1156
1958
  };
1157
- const clearPendingTouch = () => {
1158
- handlersRef.current.clearLongPress();
1159
- touchStateRef.current = null;
1959
+ return timeAtClientX({
1960
+ clientX,
1961
+ rectLeft: rect.left,
1962
+ rectWidth: rect.width,
1963
+ rangeStartMs: view.startMs,
1964
+ rangeEndMs: view.endMs
1965
+ });
1966
+ };
1967
+ const startResize = (event, edge) => {
1968
+ const view = viewRef.current;
1969
+ const current = baseWindowRef.current;
1970
+ const edgeMs = edge === 'start' ? current.start.valueOf() : current.end.valueOf();
1971
+ resizeRef.current = {
1972
+ pointerId: event.pointerId,
1973
+ edge,
1974
+ view,
1975
+ grabOffsetMs: pointerTimeAt(event.clientX, view) - edgeMs
1160
1976
  };
1161
- const isStillOnSameSlot = (touchState, clientX, clientY) => {
1162
- const currentIndex = getSlotIndexFromPoint(container, clientX, clientY);
1163
- return currentIndex === touchState.index;
1977
+ interactingRef.current = true;
1978
+ setResizeView({
1979
+ ...view,
1980
+ edge
1981
+ });
1982
+ try {
1983
+ event.currentTarget.setPointerCapture(event.pointerId);
1984
+ } catch (error) {
1985
+ // pointer may already be released
1986
+ }
1987
+ };
1988
+ const moveResize = event => {
1989
+ const resize = resizeRef.current;
1990
+ const current = baseWindowRef.current;
1991
+ const next = resizeTimeWindow({
1992
+ edge: resize.edge,
1993
+ pointerMs: pointerTimeAt(event.clientX, resize.view) - resize.grabOffsetMs,
1994
+ startMs: current.start.valueOf(),
1995
+ endMs: current.end.valueOf(),
1996
+ step: current.step,
1997
+ rangeStart: current.rangeStart,
1998
+ rangeEnd: current.rangeEnd,
1999
+ minDuration,
2000
+ maxDuration
2001
+ });
2002
+ if (next.startMs === current.start.valueOf() && next.duration === current.duration) {
2003
+ return;
2004
+ }
2005
+ publish(createTimeWindow({
2006
+ ...optionsRef.current,
2007
+ start: next.startMs,
2008
+ duration: next.duration
2009
+ }));
2010
+ };
2011
+ const endResize = () => {
2012
+ const resize = resizeRef.current;
2013
+ resizeRef.current = null;
2014
+ interactingRef.current = false;
2015
+ const current = baseWindowRef.current;
2016
+ const centerMs = resize.view.startMs + resize.view.spanMs / 2;
2017
+ const frozenStart = centerMs - current.duration * 60 * 1000 / 2;
2018
+ visualRef.current = frozenStart;
2019
+ setVisualMs(frozenStart);
2020
+ setResizeView(null);
2021
+ setVisualTargetRef.current(current.start.valueOf());
2022
+ };
2023
+ const onPointerDown = event => {
2024
+ if (event.button != null && event.button !== 0) {
2025
+ return;
2026
+ }
2027
+ const braked = stopGlideRef.current();
2028
+ const edgeNode = resizable && event.target && event.target.closest ? event.target.closest('[data-edge]') : null;
2029
+ if (edgeNode) {
2030
+ startResize(event, edgeNode.getAttribute('data-edge'));
2031
+ return;
2032
+ }
2033
+ dragRef.current = {
2034
+ pointerId: event.pointerId,
2035
+ startX: event.clientX,
2036
+ originMs: visualRef.current == null ? baseWindowRef.current.start.valueOf() : visualRef.current,
2037
+ moved: false,
2038
+ braked,
2039
+ samples: []
1164
2040
  };
1165
- const shouldCancelPendingGesture = (touchState, clientX, clientY) => {
1166
- const moveX = Math.abs(clientX - touchState.x);
1167
- const moveY = Math.abs(clientY - touchState.y);
1168
- if (moveY > SCROLL_CANCEL_THRESHOLD) {
1169
- return true;
1170
- }
1171
- if (!isStillOnSameSlot(touchState, clientX, clientY)) {
1172
- return true;
1173
- }
1174
- if (!allowMultiSelectRef.current && (moveX > TAP_MOVE_THRESHOLD || moveY > TAP_MOVE_THRESHOLD)) {
1175
- return true;
2041
+ interactingRef.current = true;
2042
+ try {
2043
+ event.currentTarget.setPointerCapture(event.pointerId);
2044
+ } catch (error) {
2045
+ // pointer may already be released
2046
+ }
2047
+ };
2048
+ const onPointerMove = event => {
2049
+ if (resizeRef.current) {
2050
+ if (resizeRef.current.pointerId === event.pointerId) {
2051
+ moveResize(event);
1176
2052
  }
1177
- return false;
2053
+ return;
2054
+ }
2055
+ const drag = dragRef.current;
2056
+ if (!drag || drag.pointerId !== event.pointerId) {
2057
+ return;
2058
+ }
2059
+ const dx = event.clientX - drag.startX;
2060
+ if (!drag.moved && Math.abs(dx) < CLICK_MOVE_TOLERANCE) {
2061
+ return;
2062
+ }
2063
+ if (!drag.moved) {
2064
+ drag.moved = true;
2065
+ setDragging(true);
2066
+ }
2067
+ const rect = trackRef.current ? trackRef.current.getBoundingClientRect() : {
2068
+ width: 1
1178
2069
  };
1179
- const activateLongPress = touchState => {
1180
- if (!isStillOnSameSlot(touchState, touchState.lastX, touchState.lastY)) {
1181
- return;
2070
+ const deltaMs = dx / (rect.width || 1) * viewRef.current.spanMs;
2071
+ const rawMs = drag.originMs - deltaMs;
2072
+ drag.samples.push({
2073
+ t: event.timeStamp,
2074
+ ms: rawMs
2075
+ });
2076
+ if (drag.samples.length > 12) {
2077
+ drag.samples.shift();
2078
+ }
2079
+ moveToRef.current(rawMs);
2080
+ setVisualTargetRef.current(rawMs, {
2081
+ immediate: true
2082
+ });
2083
+ };
2084
+ const endDrag = event => {
2085
+ if (resizeRef.current) {
2086
+ if (!event || resizeRef.current.pointerId === event.pointerId) {
2087
+ endResize();
1182
2088
  }
1183
- touchState.longPressed = true;
1184
- touchState.active = true;
1185
- clearDocumentTextSelection();
1186
- try {
1187
- container.setPointerCapture(touchState.pointerId);
1188
- } catch {
1189
- // Continue when capture is unavailable.
2089
+ return;
2090
+ }
2091
+ const drag = dragRef.current;
2092
+ if (!drag || event && drag.pointerId !== event.pointerId) {
2093
+ return;
2094
+ }
2095
+ dragRef.current = null;
2096
+ interactingRef.current = false;
2097
+ setDragging(false);
2098
+ if (drag.moved) {
2099
+ const velocity = inertiaRef.current && event ? estimateVelocity(drag.samples, event.timeStamp) : 0;
2100
+ const projection = velocity * inertiaRef.current;
2101
+ if (Math.abs(projection) >= baseWindowRef.current.step * 60 * 1000) {
2102
+ glideToRef.current(visualRef.current + projection);
2103
+ } else {
2104
+ setVisualTargetRef.current(baseWindowRef.current.start.valueOf());
1190
2105
  }
1191
- slotsRef.current?.classList.add('schedule-calendar-touch-dragging');
1192
- handlersRef.current.startSlotSelection(touchState.slot, touchState.index, {
1193
- showPopover: false,
1194
- lockScroll: true
2106
+ }
2107
+ if (!drag.moved && drag.braked) {
2108
+ moveToRef.current(drag.originMs);
2109
+ setVisualTargetRef.current(baseWindowRef.current.start.valueOf());
2110
+ return;
2111
+ }
2112
+ if (!drag.moved && event && event.type === 'pointerup') {
2113
+ const rect = trackRef.current ? trackRef.current.getBoundingClientRect() : {
2114
+ left: 0,
2115
+ width: 1
2116
+ };
2117
+ const view = viewRef.current;
2118
+ const clickedMs = timeAtClientX({
2119
+ clientX: event.clientX,
2120
+ rectLeft: rect.left,
2121
+ rectWidth: rect.width,
2122
+ rangeStartMs: view.startMs,
2123
+ rangeEndMs: view.endMs
1195
2124
  });
2125
+ const current = baseWindowRef.current;
2126
+ moveToRef.current(clickedMs - current.duration * 60 * 1000 / 2);
2127
+ }
2128
+ };
2129
+ const onKeyDown = event => {
2130
+ stopGlideRef.current();
2131
+ const current = baseWindowRef.current;
2132
+ const stepMinutes = current.step;
2133
+ const moves = {
2134
+ ArrowLeft: -stepMinutes,
2135
+ ArrowDown: -stepMinutes,
2136
+ ArrowRight: stepMinutes,
2137
+ ArrowUp: stepMinutes,
2138
+ PageDown: -current.duration,
2139
+ PageUp: current.duration
1196
2140
  };
1197
- const handlePointerDown = event => {
1198
- if (event.pointerType === 'mouse' || event.isPrimary === false) {
1199
- return;
1200
- }
1201
- const slotElement = event.target?.closest?.('[data-slot-index]');
1202
- if (!slotElement || !container.contains(slotElement)) {
1203
- return;
1204
- }
1205
- const index = Number(slotElement.getAttribute('data-slot-index'));
1206
- const slot = slotsDataRef.current[index];
1207
- if (Number.isNaN(index) || slot?.status !== 'free') {
2141
+ if (event.key === 'Home' && current.rangeStart) {
2142
+ event.preventDefault();
2143
+ moveToRef.current(current.rangeStart);
2144
+ return;
2145
+ }
2146
+ if (event.key === 'End' && current.rangeEnd) {
2147
+ event.preventDefault();
2148
+ moveToRef.current(current.rangeEnd);
2149
+ return;
2150
+ }
2151
+ if (!(event.key in moves)) {
2152
+ return;
2153
+ }
2154
+ event.preventDefault();
2155
+ const minutes = event.shiftKey ? Math.sign(moves[event.key]) * Math.max(60, stepMinutes) : moves[event.key];
2156
+ moveToRef.current(current.start.add(minutes, 'minute'));
2157
+ };
2158
+ react.useEffect(() => {
2159
+ const node = rootRef.current;
2160
+ if (!node) {
2161
+ return undefined;
2162
+ }
2163
+ const onWheel = event => {
2164
+ if (isLockedRef.current) {
1208
2165
  return;
1209
2166
  }
1210
- handlersRef.current.resetGestureState();
1211
- touchStateRef.current = {
1212
- pointerId: event.pointerId,
1213
- x: event.clientX,
1214
- y: event.clientY,
1215
- lastX: event.clientX,
1216
- lastY: event.clientY,
1217
- slot,
1218
- index,
1219
- moved: false,
1220
- longPressed: false,
1221
- active: false,
1222
- selectionTriggered: false
1223
- };
1224
- if (!allowMultiSelectRef.current) {
2167
+ const dominant = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY;
2168
+ if (!dominant) {
1225
2169
  return;
1226
2170
  }
1227
- longPressTimerRef.current = window.setTimeout(() => {
1228
- const touchState = touchStateRef.current;
1229
- if (!touchState || touchState.pointerId !== event.pointerId || touchState.moved) {
1230
- return;
2171
+ event.preventDefault();
2172
+ const current = baseWindowRef.current;
2173
+ const rectWidth = trackRef.current ? trackRef.current.getBoundingClientRect().width : node.clientWidth;
2174
+ const deltaPx = wheelDeltaToPx({
2175
+ delta: dominant,
2176
+ deltaMode: event.deltaMode,
2177
+ trackWidth: rectWidth
2178
+ });
2179
+ const stepMs = current.step * 60 * 1000;
2180
+ const pxPerStep = rectWidth * stepMs / viewRef.current.spanMs;
2181
+ const discrete = isDiscreteWheel(event, deltaPx);
2182
+ const {
2183
+ steps,
2184
+ accumulatedPx
2185
+ } = consumeWheelSteps({
2186
+ accumulatedPx: wheelAccumRef.current,
2187
+ deltaPx,
2188
+ pxPerStep,
2189
+ discrete
2190
+ });
2191
+ wheelAccumRef.current = accumulatedPx;
2192
+ wheelActiveRef.current = true;
2193
+ clearTimeout(wheelIdleRef.current);
2194
+ wheelIdleRef.current = setTimeout(() => {
2195
+ wheelActiveRef.current = false;
2196
+ wheelAccumRef.current = 0;
2197
+ wheelBaseRef.current = null;
2198
+ wheelSamplesRef.current = [];
2199
+ wheelDirectionRef.current = 0;
2200
+ if (!glidingRef.current) {
2201
+ setVisualTargetRef.current(baseWindowRef.current.start.valueOf());
1231
2202
  }
1232
- activateLongPress(touchState);
1233
- longPressTimerRef.current = null;
1234
- }, LONG_PRESS_DELAY);
1235
- };
1236
- const handlePointerMove = event => {
1237
- const touchState = touchStateRef.current;
1238
- if (!touchState || event.pointerId !== touchState.pointerId) {
1239
- return;
1240
- }
1241
- touchState.lastX = event.clientX;
1242
- touchState.lastY = event.clientY;
1243
- if (!touchState.active) {
1244
- if (shouldCancelPendingGesture(touchState, event.clientX, event.clientY)) {
1245
- touchState.moved = true;
1246
- clearPendingTouch();
2203
+ }, WHEEL_IDLE_MS);
2204
+ if (discrete && inertiaRef.current) {
2205
+ if (!steps) {
2206
+ return;
1247
2207
  }
1248
- return;
1249
- }
1250
- event.preventDefault();
1251
- event.stopPropagation();
1252
- clearDocumentTextSelection();
1253
- const index = getSlotIndexFromPoint(container, event.clientX, event.clientY);
1254
- if (index !== null) {
1255
- handlersRef.current.updateSelectionRange(index);
1256
- }
1257
- };
1258
- const handlePointerUp = event => {
1259
- const touchState = touchStateRef.current;
1260
- if (!touchState || event.pointerId !== touchState.pointerId) {
1261
- return;
1262
- }
1263
- releasePointerCapture(event.pointerId);
1264
- suppressMouseUntilRef.current = Date.now() + 700;
1265
- if (touchState.active) {
1266
- event.preventDefault();
1267
- touchState.selectionTriggered = true;
1268
- const index = getSlotIndexFromPoint(container, event.clientX, event.clientY);
1269
- if (index !== null) {
1270
- handlersRef.current.updateSelectionRange(index);
2208
+ const samples = wheelSamplesRef.current;
2209
+ if (wheelDirectionRef.current !== Math.sign(steps)) {
2210
+ samples.length = 0;
2211
+ wheelBaseRef.current = null;
2212
+ wheelDirectionRef.current = Math.sign(steps);
1271
2213
  }
1272
- suppressTriggerUntilRef.current = Date.now() + 500;
1273
- handlersRef.current.finishSelection({
1274
- fromTouch: true
2214
+ const from = wheelBaseRef.current == null ? current.start.valueOf() : wheelBaseRef.current;
2215
+ const base = createTimeWindow({
2216
+ start: from + steps * stepMs,
2217
+ ...optionsRef.current
2218
+ }).start.valueOf();
2219
+ wheelBaseRef.current = base;
2220
+ samples.push({
2221
+ t: event.timeStamp,
2222
+ ms: base
1275
2223
  });
2224
+ if (samples.length > 8) {
2225
+ samples.shift();
2226
+ }
2227
+ const projection = estimateVelocity(samples, event.timeStamp) * inertiaRef.current * WHEEL_INERTIA_SCALE;
2228
+ let target = base + projection;
2229
+ const lastTarget = visualTargetRef.current;
2230
+ if (samples.length > 1 && lastTarget != null && (target - lastTarget) * wheelDirectionRef.current < 0) {
2231
+ target = lastTarget;
2232
+ }
2233
+ glideToRef.current(target, projection ? inertiaRef.current : WHEEL_NOTCH_EASE_MS);
1276
2234
  return;
1277
2235
  }
1278
- handlersRef.current.clearLongPress();
1279
- const quickTap = !touchState.moved && !touchState.longPressed && Math.abs(event.clientX - touchState.x) <= TAP_MOVE_THRESHOLD && Math.abs(event.clientY - touchState.y) <= TAP_MOVE_THRESHOLD && isStillOnSameSlot(touchState, event.clientX, event.clientY);
1280
- const {
1281
- slot
1282
- } = touchState;
1283
- touchStateRef.current = null;
1284
- if (quickTap) {
1285
- touchState.selectionTriggered = true;
1286
- suppressTriggerUntilRef.current = Date.now() + 500;
1287
- handlersRef.current.selectSlots([slot]);
1288
- handlersRef.current.showFormPopoverForSlots([slot]);
1289
- handlersRef.current.onFreeSlotClick?.(slot);
1290
- return;
1291
- }
1292
- handlersRef.current.resetGestureState();
1293
- };
1294
- const handleContextMenu = event => {
1295
- if (touchStateRef.current?.active || draggingRef.current) {
1296
- event.preventDefault();
1297
- }
1298
- };
1299
- const handleSelectStart = event => {
1300
- if (!touchStateRef.current?.active && !draggingRef.current) {
1301
- return;
2236
+ if (steps) {
2237
+ moveToRef.current(current.start.valueOf() + steps * stepMs);
1302
2238
  }
1303
- event.preventDefault();
2239
+ const residualMs = pxPerStep > 0 ? accumulatedPx / pxPerStep * stepMs : 0;
2240
+ setVisualTargetRef.current(baseWindowRef.current.start.valueOf() + residualMs);
1304
2241
  };
1305
- const handleTouchMove = event => {
1306
- if (!draggingRef.current && !touchStateRef.current?.active) {
1307
- return;
1308
- }
1309
- event.preventDefault();
1310
- };
1311
- const touchOptions = {
1312
- passive: false,
1313
- capture: true
1314
- };
1315
- container.addEventListener('pointerdown', handlePointerDown, {
1316
- passive: true
1317
- });
1318
- container.addEventListener('pointermove', handlePointerMove, {
2242
+ node.addEventListener('wheel', onWheel, {
1319
2243
  passive: false
1320
2244
  });
1321
- container.addEventListener('pointerup', handlePointerUp, {
1322
- passive: false
1323
- });
1324
- container.addEventListener('pointercancel', handlePointerUp, {
1325
- passive: false
1326
- });
1327
- container.addEventListener('touchmove', handleTouchMove, touchOptions);
1328
- container.addEventListener('contextmenu', handleContextMenu);
1329
- container.addEventListener('selectstart', handleSelectStart);
1330
- return () => {
1331
- releasePointerCapture(touchStateRef.current?.pointerId);
1332
- handlersRef.current.clearLongPress();
1333
- handlersRef.current.resetGestureState();
1334
- longPressTimerRef.current = null;
1335
- container.removeEventListener('touchmove', handleTouchMove, touchOptions);
1336
- };
2245
+ return () => node.removeEventListener('wheel', onWheel);
1337
2246
  }, []);
1338
- return {
1339
- activePopoverKey,
1340
- dragging,
1341
- formPopoverKey,
1342
- selectedAnchorKey,
1343
- selectedKeys,
1344
- setActivePopoverKey,
1345
- selectSlots,
1346
- slotsRef,
1347
- finishSelection,
1348
- handleMouseDown,
1349
- handleMouseEnter,
1350
- handleMouseUp,
1351
- handleMouseLeave,
1352
- handlePopoverOpenChange
2247
+ const toggleLocked = () => {
2248
+ const next = !isLockedRef.current;
2249
+ if (next) {
2250
+ stopGlideRef.current();
2251
+ }
2252
+ if (!lockedControlled) {
2253
+ setInnerLocked(next);
2254
+ }
2255
+ if (onLockedChangeRef.current) {
2256
+ onLockedChangeRef.current(next);
2257
+ }
1353
2258
  };
2259
+ const labelTicks = ticks.filter(tick => tick.showLabel);
2260
+ const ariaBounds = {};
2261
+ if (rangeStartMs != null) {
2262
+ ariaBounds['aria-valuemin'] = rangeStartMs;
2263
+ }
2264
+ if (rangeEndMs != null) {
2265
+ ariaBounds['aria-valuemax'] = rangeEndMs - durationMs;
2266
+ }
2267
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
2268
+ ref: rootRef,
2269
+ className: classNames(style$2.root, style$2[`root-${size}`], className, {
2270
+ [style$2['root-dragging']]: dragging,
2271
+ [style$2['root-resizing']]: !!resizeView,
2272
+ [style$2['root-locked']]: isLocked
2273
+ }),
2274
+ style: styleProp,
2275
+ children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
2276
+ className: style$2.header,
2277
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
2278
+ className: style$2.label,
2279
+ children: label
2280
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
2281
+ className: style$2.actions,
2282
+ children: [lockable ? /*#__PURE__*/jsxRuntime.jsx("button", {
2283
+ type: "button",
2284
+ role: "switch",
2285
+ "aria-checked": !isLocked,
2286
+ "aria-label": formatMessage({
2287
+ id: isLocked ? 'TimeWindow.locked' : 'TimeWindow.editable'
2288
+ }),
2289
+ className: classNames(style$2.switch, {
2290
+ [style$2['switch-on']]: !isLocked
2291
+ }),
2292
+ onClick: toggleLocked,
2293
+ children: /*#__PURE__*/jsxRuntime.jsx("i", {})
2294
+ }) : null, showDuration ? /*#__PURE__*/jsxRuntime.jsx("span", {
2295
+ className: style$2.duration,
2296
+ children: durationText
2297
+ }) : null]
2298
+ })]
2299
+ }), isLocked ? null : /*#__PURE__*/jsxRuntime.jsxs("div", {
2300
+ className: style$2.scale,
2301
+ children: [/*#__PURE__*/jsxRuntime.jsxs("div", {
2302
+ ref: trackRef,
2303
+ className: classNames(style$2.track, {
2304
+ [style$2['track-wheel']]: wheelEffect
2305
+ }),
2306
+ role: "slider",
2307
+ tabIndex: 0,
2308
+ ...ariaBounds,
2309
+ "aria-valuenow": baseWindow.start.valueOf(),
2310
+ "aria-valuetext": ariaValueText,
2311
+ onKeyDown: onKeyDown,
2312
+ onPointerDown: onPointerDown,
2313
+ onPointerMove: onPointerMove,
2314
+ onPointerUp: endDrag,
2315
+ onPointerCancel: endDrag,
2316
+ children: [leftShade > 0 ? /*#__PURE__*/jsxRuntime.jsx("span", {
2317
+ className: classNames(style$2.shade, style$2['shade-start']),
2318
+ style: {
2319
+ width: `${leftShade}%`
2320
+ }
2321
+ }) : null, rightShade > 0 ? /*#__PURE__*/jsxRuntime.jsx("span", {
2322
+ className: classNames(style$2.shade, style$2['shade-end']),
2323
+ style: {
2324
+ width: `${rightShade}%`
2325
+ }
2326
+ }) : null, /*#__PURE__*/jsxRuntime.jsx("div", {
2327
+ className: style$2.ticks,
2328
+ children: ticks.map(tick => /*#__PURE__*/jsxRuntime.jsx("span", {
2329
+ className: classNames(style$2.tick, style$2[`tick-${tick.level}`], {
2330
+ [style$2['tick-out']]: tick.outOfRange
2331
+ }),
2332
+ style: {
2333
+ left: `${tick.ratio * 100}%`
2334
+ }
2335
+ }, tick.key))
2336
+ }), wheelEffect ? /*#__PURE__*/jsxRuntime.jsx("span", {
2337
+ className: style$2.cylinder,
2338
+ "aria-hidden": "true"
2339
+ }) : null, /*#__PURE__*/jsxRuntime.jsx("span", {
2340
+ className: classNames(style$2.window, {
2341
+ [style$2['window-compact']]: windowPx < GRIP_MIN_WIDTH,
2342
+ [style$2['window-resizable']]: resizable,
2343
+ [style$2['window-fixed']]: !resizable
2344
+ }),
2345
+ style: {
2346
+ left: `${left}%`,
2347
+ width: `${width}%`
2348
+ },
2349
+ children: resizable ? /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2350
+ children: [/*#__PURE__*/jsxRuntime.jsx("span", {
2351
+ "data-edge": "start",
2352
+ "aria-hidden": "true",
2353
+ className: classNames(style$2.edge, style$2['edge-start'], {
2354
+ [style$2['edge-active']]: resizeEdge === 'start'
2355
+ })
2356
+ }), /*#__PURE__*/jsxRuntime.jsx("span", {
2357
+ "data-edge": "end",
2358
+ "aria-hidden": "true",
2359
+ className: classNames(style$2.edge, style$2['edge-end'], {
2360
+ [style$2['edge-active']]: resizeEdge === 'end'
2361
+ })
2362
+ })]
2363
+ }) : null
2364
+ })]
2365
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
2366
+ className: style$2.labels,
2367
+ children: labelTicks.map(tick => /*#__PURE__*/jsxRuntime.jsx("span", {
2368
+ className: classNames(style$2['tick-label'], style$2[`tick-label-${tick.level}`], {
2369
+ [style$2['tick-out']]: tick.outOfRange
2370
+ }),
2371
+ style: {
2372
+ left: `${tick.ratio * 100}%`
2373
+ },
2374
+ children: tick.label
2375
+ }, tick.key))
2376
+ })]
2377
+ })]
2378
+ });
1354
2379
  };
2380
+ const TimeWindow = withLocale(TimeWindowInner);
1355
2381
 
1356
2382
  const DefaultFormInner = () => {
1357
2383
  const {
@@ -1382,170 +2408,595 @@ const DefaultFormInner = () => {
1382
2408
  });
1383
2409
  };
1384
2410
 
1385
- var style$1 = {"slots":"kne-react-calendar_Ftp-V","slots-content":"kne-react-calendar_ArIB7","slots-dragging":"kne-react-calendar_zOHks","slot-item":"kne-react-calendar_fkkFH","slot-dot":"kne-react-calendar_XjojI","slot-time":"kne-react-calendar_343hW","slot-title":"kne-react-calendar_mkuiI","slot-free":"kne-react-calendar_7kzzQ","slot-occupied":"kne-react-calendar_gdNm7","slot-disabled":"kne-react-calendar_2LDxX","slot-selected":"kne-react-calendar_TYAi6","slot-tag":"kne-react-calendar_-xuUf","form-popover":"kne-react-calendar_W-I5J","schedule-popover":"kne-react-calendar_rVLO8","form-popover-title":"kne-react-calendar_mMpFQ","slot-popover-content":"kne-react-calendar_xYptY","slot-popover-title":"kne-react-calendar_oagF-","form-actions":"kne-react-calendar_4NPv0"};
2411
+ 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"};
1386
2412
 
1387
- const SlotEvents = ({
1388
- slot,
2413
+ const EventDetail = ({
2414
+ event,
1389
2415
  date,
1390
2416
  onEventClick
1391
- }) => /*#__PURE__*/jsxRuntime.jsx("div", {
1392
- className: style$1['slot-popover-content'],
1393
- children: /*#__PURE__*/jsxRuntime.jsx(antd.List, {
1394
- size: "small",
1395
- dataSource: slot.events,
1396
- renderItem: event => /*#__PURE__*/jsxRuntime.jsx(EventItem, {
1397
- event: event,
1398
- date: date,
1399
- mode: "schedule",
1400
- onEventClick: onEventClick
1401
- })
1402
- })
1403
- });
2417
+ }) => {
2418
+ const clickable = !event.disabled && (typeof event.onClick === 'function' || typeof onEventClick === 'function');
2419
+ const handleClick = () => {
2420
+ if (typeof event.onClick === 'function') {
2421
+ event.onClick(event, {
2422
+ date,
2423
+ mode: 'schedule'
2424
+ });
2425
+ return;
2426
+ }
2427
+ onEventClick?.(event, {
2428
+ date,
2429
+ mode: 'schedule'
2430
+ });
2431
+ };
2432
+ const tagLabel = getEventTagLabel(event);
2433
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
2434
+ className: classNames(style$1['event-detail'], {
2435
+ [style$1['event-detail-clickable']]: clickable,
2436
+ [style$1['event-detail-disabled']]: event.disabled
2437
+ }),
2438
+ style: event.color ? {
2439
+ '--timeline-event-color': event.color
2440
+ } : undefined,
2441
+ role: clickable ? 'button' : undefined,
2442
+ tabIndex: clickable ? 0 : undefined,
2443
+ onClick: clickable ? handleClick : undefined,
2444
+ onKeyDown: clickable ? keyEvent => {
2445
+ if (keyEvent.key === 'Enter' || keyEvent.key === ' ') {
2446
+ keyEvent.preventDefault();
2447
+ handleClick();
2448
+ }
2449
+ } : undefined,
2450
+ children: [/*#__PURE__*/jsxRuntime.jsx("span", {
2451
+ className: style$1['event-detail-bar']
2452
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
2453
+ className: style$1['event-detail-body'],
2454
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
2455
+ className: style$1['event-detail-title'],
2456
+ children: event.title
2457
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
2458
+ className: style$1['event-detail-time'],
2459
+ children: formatRange(event.start, event.end)
2460
+ }), event.detail ? /*#__PURE__*/jsxRuntime.jsx("div", {
2461
+ className: style$1['event-detail-text'],
2462
+ children: event.detail
2463
+ }) : null, tagLabel ? /*#__PURE__*/jsxRuntime.jsx(antd.Tag, {
2464
+ className: style$1['event-detail-tag'],
2465
+ color: event.color,
2466
+ children: tagLabel
2467
+ }) : null]
2468
+ })]
2469
+ });
2470
+ };
1404
2471
 
1405
- const SlotItem = ({
1406
- slot,
2472
+ const DRAG_THRESHOLD_PX = 4;
2473
+ const TOUCH_SLOP_PX = 8;
2474
+ const LONG_PRESS_MS = 350;
2475
+ const Timeline = ({
1407
2476
  date,
1408
- selected,
2477
+ day,
2478
+ hourHeight,
2479
+ timeUnit,
2480
+ timeStep,
2481
+ eventItems,
2482
+ disabledRanges,
2483
+ blockers,
2484
+ conflicts,
2485
+ allowMultiSelect,
2486
+ selection,
2487
+ dragging,
1409
2488
  formPopover,
1410
2489
  popoverOpen,
2490
+ popoverPlacement,
2491
+ popoverAnchor,
1411
2492
  onPopoverOpenChange,
1412
- onContextMenu,
1413
- onMouseDown,
1414
- onPointerDown,
1415
- onMouseEnter,
1416
- onMouseUp,
1417
- onClick,
2493
+ onSelectionDrag,
2494
+ onSelectionCommit,
2495
+ onFreeSlotClick,
1418
2496
  onEventClick,
1419
- renderTimeSlot,
1420
- renderOccupiedSlot
2497
+ renderEventBlock,
2498
+ renderOccupiedSlot,
2499
+ interactionRef,
2500
+ isMobile
1421
2501
  }) => {
1422
- const {
1423
- formatMessage
1424
- } = reactIntl.useIntl();
1425
- const content = renderTimeSlot?.(slot, {
1426
- selected
1427
- });
1428
- if (content) {
1429
- const customNode = /*#__PURE__*/jsxRuntime.jsx("div", {
1430
- "data-slot-index": slot.index,
1431
- "data-slot-status": slot.status,
1432
- onContextMenu: onContextMenu,
1433
- onMouseDown: onMouseDown,
1434
- onPointerDown: onPointerDown,
1435
- onMouseEnter: onMouseEnter,
1436
- onMouseUp: onMouseUp,
1437
- onClick: onClick,
1438
- children: content
1439
- });
1440
- if (formPopover) {
1441
- return /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
1442
- rootClassName: style$1['schedule-popover'],
1443
- open: popoverOpen,
1444
- onOpenChange: onPopoverOpenChange,
1445
- content: formPopover,
1446
- trigger: "click",
1447
- placement: "bottom",
1448
- autoAdjustOverflow: true,
1449
- children: customNode
2502
+ const gridRef = react.useRef(null);
2503
+ const gestureRef = react.useRef(null);
2504
+ const suppressClickRef = react.useRef(false);
2505
+ const touchCommitRef = react.useRef(0);
2506
+ const [hover, setHover] = react.useState(null);
2507
+ const [activeEventKey, setActiveEventKey] = react.useState(null);
2508
+ const pxPerMs = hourHeight / (60 * MINUTE_MS$1);
2509
+ const height = (day.endMs - day.startMs) * pxPerMs;
2510
+ const unitMs = timeUnit * MINUTE_MS$1;
2511
+ const stepMs = timeStep * MINUTE_MS$1;
2512
+ const latest = react.useRef({});
2513
+ latest.current = {
2514
+ day,
2515
+ blockers,
2516
+ unitMs,
2517
+ stepMs,
2518
+ allowMultiSelect,
2519
+ selection,
2520
+ onSelectionDrag,
2521
+ onSelectionCommit,
2522
+ onFreeSlotClick,
2523
+ pxPerMs
2524
+ };
2525
+ const toY = ms => (ms - day.startMs) * pxPerMs;
2526
+ const lines = react.useMemo(() => {
2527
+ const list = [];
2528
+ for (let ms = day.startMs; ms <= day.endMs; ms += unitMs) {
2529
+ list.push({
2530
+ ms,
2531
+ hour: dayjs__default["default"](ms).minute() === 0
1450
2532
  });
1451
2533
  }
1452
- return customNode;
1453
- }
1454
- const wrapWithPopover = node => {
1455
- if (formPopover) {
1456
- return /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
1457
- rootClassName: style$1['schedule-popover'],
1458
- open: popoverOpen,
1459
- onOpenChange: onPopoverOpenChange,
1460
- content: formPopover,
1461
- trigger: "click",
1462
- placement: "bottom",
1463
- autoAdjustOverflow: true,
1464
- children: node
2534
+ return list;
2535
+ }, [day.endMs, day.startMs, unitMs]);
2536
+ const labels = react.useMemo(() => {
2537
+ const list = [];
2538
+ let cursor = dayjs__default["default"](day.startMs);
2539
+ if (cursor.minute() !== 0) {
2540
+ list.push(day.startMs);
2541
+ cursor = cursor.add(1, 'hour').startOf('hour');
2542
+ }
2543
+ for (; cursor.valueOf() <= day.endMs; cursor = cursor.add(1, 'hour')) {
2544
+ list.push(cursor.valueOf());
2545
+ }
2546
+ if (list[list.length - 1] !== day.endMs) {
2547
+ list.push(day.endMs);
2548
+ }
2549
+ return list;
2550
+ }, [day.endMs, day.startMs]);
2551
+ const [now, setNow] = react.useState(() => Date.now());
2552
+ react.useEffect(() => {
2553
+ const timer = setInterval(() => setNow(Date.now()), 60 * 1000);
2554
+ return () => clearInterval(timer);
2555
+ }, []);
2556
+ const showNow = now >= day.startMs && now <= day.endMs;
2557
+ const msAtClientY = clientY => {
2558
+ const rect = gridRef.current.getBoundingClientRect();
2559
+ const {
2560
+ day: currentDay,
2561
+ pxPerMs: currentPx
2562
+ } = latest.current;
2563
+ return Math.min(Math.max(currentDay.startMs + (clientY - rect.top) / currentPx, currentDay.startMs), currentDay.endMs - 1);
2564
+ };
2565
+ const finishGesture = () => {
2566
+ const gesture = gestureRef.current;
2567
+ if (!gesture) {
2568
+ return;
2569
+ }
2570
+ clearTimeout(gesture.timer);
2571
+ window.removeEventListener('pointermove', gesture.onMove);
2572
+ window.removeEventListener('pointerup', gesture.onUp);
2573
+ window.removeEventListener('pointercancel', gesture.onCancel);
2574
+ gestureRef.current = null;
2575
+ };
2576
+ react.useEffect(() => finishGesture, []);
2577
+ react.useEffect(() => {
2578
+ const grid = gridRef.current;
2579
+ if (!grid) {
2580
+ return undefined;
2581
+ }
2582
+ const preventScroll = event => {
2583
+ if (gestureRef.current?.active) {
2584
+ event.preventDefault();
2585
+ }
2586
+ };
2587
+ const preventGhostClick = event => {
2588
+ if (Date.now() - touchCommitRef.current < 300) {
2589
+ event.preventDefault();
2590
+ }
2591
+ };
2592
+ grid.addEventListener('touchmove', preventScroll, {
2593
+ passive: false
2594
+ });
2595
+ grid.addEventListener('touchend', preventGhostClick, {
2596
+ passive: false
2597
+ });
2598
+ return () => {
2599
+ grid.removeEventListener('touchmove', preventScroll);
2600
+ grid.removeEventListener('touchend', preventGhostClick);
2601
+ };
2602
+ }, []);
2603
+ const handlePointerDown = event => {
2604
+ if (!gridRef.current?.contains(event.target)) {
2605
+ return;
2606
+ }
2607
+ if (interactionRef) {
2608
+ interactionRef.current = Date.now();
2609
+ }
2610
+ if (event.pointerType === 'mouse' && event.button !== 0) {
2611
+ return;
2612
+ }
2613
+ const {
2614
+ day: currentDay,
2615
+ blockers: currentBlockers,
2616
+ selection: currentSelection,
2617
+ allowMultiSelect: multi
2618
+ } = latest.current;
2619
+ const edgeNode = event.target.closest('[data-selection-edge]');
2620
+ const onSelection = event.target.closest('[data-timeline-selection]');
2621
+ const eventNode = event.target.closest('[data-timeline-event]');
2622
+ const pointerMs = msAtClientY(event.clientY);
2623
+ let gesture;
2624
+ if (edgeNode && currentSelection && multi) {
2625
+ gesture = {
2626
+ type: 'resize',
2627
+ edge: edgeNode.dataset.selectionEdge,
2628
+ active: true
2629
+ };
2630
+ } else if (onSelection && currentSelection) {
2631
+ gesture = {
2632
+ type: 'move',
2633
+ active: true
2634
+ };
2635
+ } else {
2636
+ const gap = getFreeGap(pointerMs, currentBlockers, currentDay);
2637
+ if (!gap) {
2638
+ return;
2639
+ }
2640
+ gesture = {
2641
+ type: 'create',
2642
+ gap,
2643
+ active: event.pointerType === 'mouse',
2644
+ onEvent: !!eventNode
2645
+ };
2646
+ }
2647
+ if (gesture.type !== 'create') {
2648
+ const ownGap = getFreeGap(currentSelection.startMs, currentBlockers, currentDay) || currentDay;
2649
+ gesture.gap = ownGap;
2650
+ gesture.range0 = currentSelection;
2651
+ event.preventDefault();
2652
+ }
2653
+ Object.assign(gesture, {
2654
+ pointerId: event.pointerId,
2655
+ pointerType: event.pointerType,
2656
+ startX: event.clientX,
2657
+ startY: event.clientY,
2658
+ startMs: pointerMs,
2659
+ moved: false
2660
+ });
2661
+ setHover(null);
2662
+ gesture.onMove = moveEvent => {
2663
+ if (moveEvent.pointerId !== gesture.pointerId) {
2664
+ return;
2665
+ }
2666
+ const dx = moveEvent.clientX - gesture.startX;
2667
+ const dy = moveEvent.clientY - gesture.startY;
2668
+ if (!gesture.active) {
2669
+ if (Math.hypot(dx, dy) > TOUCH_SLOP_PX) {
2670
+ finishGesture();
2671
+ }
2672
+ return;
2673
+ }
2674
+ if (!gesture.moved && Math.abs(dy) < DRAG_THRESHOLD_PX) {
2675
+ return;
2676
+ }
2677
+ const {
2678
+ stepMs: step,
2679
+ day: d,
2680
+ allowMultiSelect: canResize
2681
+ } = latest.current;
2682
+ const ms = msAtClientY(moveEvent.clientY);
2683
+ let next;
2684
+ if (gesture.type === 'create') {
2685
+ if (!canResize) {
2686
+ return;
2687
+ }
2688
+ next = createSelectionRange({
2689
+ anchorMs: gesture.startMs,
2690
+ pointerMs: ms,
2691
+ gap: gesture.gap,
2692
+ originMs: d.startMs,
2693
+ stepMs: step
2694
+ });
2695
+ } else if (gesture.type === 'move') {
2696
+ next = moveSelectionRange({
2697
+ range: gesture.range0,
2698
+ deltaMs: ms - gesture.startMs,
2699
+ gap: gesture.gap,
2700
+ originMs: d.startMs,
2701
+ stepMs: step
2702
+ });
2703
+ } else {
2704
+ next = resizeSelectionRange({
2705
+ range: gesture.range0,
2706
+ edge: gesture.edge,
2707
+ pointerMs: ms,
2708
+ gap: gesture.gap,
2709
+ originMs: d.startMs,
2710
+ stepMs: step
2711
+ });
2712
+ }
2713
+ gesture.moved = true;
2714
+ gesture.range = next;
2715
+ latest.current.onSelectionDrag(next);
2716
+ };
2717
+ gesture.onUp = upEvent => {
2718
+ if (upEvent.pointerId !== gesture.pointerId) {
2719
+ return;
2720
+ }
2721
+ finishGesture();
2722
+ if (gesture.pointerType !== 'mouse') {
2723
+ touchCommitRef.current = Date.now();
2724
+ }
2725
+ const {
2726
+ unitMs: unit,
2727
+ day: d,
2728
+ onSelectionCommit: commit,
2729
+ onFreeSlotClick: onFreeClick
2730
+ } = latest.current;
2731
+ if (gesture.moved) {
2732
+ suppressClickRef.current = true;
2733
+ setTimeout(() => {
2734
+ suppressClickRef.current = false;
2735
+ }, 0);
2736
+ commit(gesture.range);
2737
+ return;
2738
+ }
2739
+ if (gesture.type !== 'create') {
2740
+ commit(gesture.range0);
2741
+ return;
2742
+ }
2743
+ if (gesture.onEvent) {
2744
+ return;
2745
+ }
2746
+ const range = clickSelectionRange({
2747
+ pointerMs: gesture.startMs,
2748
+ gap: gesture.gap,
2749
+ originMs: d.startMs,
2750
+ unitMs: unit
2751
+ });
2752
+ const slotStart = snapToStep(gesture.startMs, {
2753
+ originMs: d.startMs,
2754
+ stepMs: unit,
2755
+ mode: 'floor'
2756
+ });
2757
+ onFreeClick?.({
2758
+ start: new Date(slotStart),
2759
+ end: new Date(Math.min(slotStart + unit, d.endMs))
1465
2760
  });
2761
+ commit(range);
2762
+ };
2763
+ gesture.onCancel = cancelEvent => {
2764
+ if (cancelEvent.pointerId !== gesture.pointerId) {
2765
+ return;
2766
+ }
2767
+ const wasMoved = gesture.moved;
2768
+ finishGesture();
2769
+ if (wasMoved) {
2770
+ latest.current.onSelectionCommit(gesture.range);
2771
+ }
2772
+ };
2773
+ if (!gesture.active) {
2774
+ gesture.timer = setTimeout(() => {
2775
+ gesture.active = true;
2776
+ navigator.vibrate?.(10);
2777
+ const {
2778
+ day: d,
2779
+ unitMs: unit
2780
+ } = latest.current;
2781
+ const range = clickSelectionRange({
2782
+ pointerMs: gesture.startMs,
2783
+ gap: gesture.gap,
2784
+ originMs: d.startMs,
2785
+ unitMs: unit
2786
+ });
2787
+ gesture.moved = true;
2788
+ gesture.range = range;
2789
+ gesture.startMs = range.startMs;
2790
+ latest.current.onSelectionDrag(range);
2791
+ }, LONG_PRESS_MS);
1466
2792
  }
1467
- if (slot.status === 'occupied') {
1468
- return /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
1469
- rootClassName: style$1['schedule-popover'],
1470
- open: popoverOpen,
1471
- onOpenChange: onPopoverOpenChange,
1472
- content: /*#__PURE__*/jsxRuntime.jsx(SlotEvents, {
1473
- slot: slot,
1474
- date: date,
1475
- onEventClick: onEventClick
1476
- }),
1477
- trigger: "click",
1478
- placement: "bottom",
1479
- autoAdjustOverflow: true,
1480
- children: node
2793
+ finishGesture();
2794
+ gestureRef.current = gesture;
2795
+ window.addEventListener('pointermove', gesture.onMove);
2796
+ window.addEventListener('pointerup', gesture.onUp);
2797
+ window.addEventListener('pointercancel', gesture.onCancel);
2798
+ };
2799
+ const handleHover = event => {
2800
+ if (event.pointerType !== 'mouse' || gestureRef.current || !gridRef.current?.contains(event.target)) {
2801
+ return;
2802
+ }
2803
+ if (event.target.closest('[data-timeline-selection]')) {
2804
+ setHover(null);
2805
+ return;
2806
+ }
2807
+ const ms = msAtClientY(event.clientY);
2808
+ const gap = getFreeGap(ms, blockers, day);
2809
+ if (!gap) {
2810
+ setHover(null);
2811
+ return;
2812
+ }
2813
+ const range = clickSelectionRange({
2814
+ pointerMs: ms,
2815
+ gap,
2816
+ originMs: day.startMs,
2817
+ unitMs
2818
+ });
2819
+ setHover(current => current && current.startMs === range.startMs && current.endMs === range.endMs ? current : range);
2820
+ };
2821
+ const renderEventContent = item => {
2822
+ const {
2823
+ event
2824
+ } = item;
2825
+ if (renderEventBlock) {
2826
+ return renderEventBlock(event, {
2827
+ start: new Date(item.startMs),
2828
+ end: new Date(item.endMs)
1481
2829
  });
1482
2830
  }
1483
- if (slot.disabledReason) {
1484
- return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
1485
- title: slot.disabledReason,
1486
- children: node
2831
+ if (renderOccupiedSlot) {
2832
+ return renderOccupiedSlot({
2833
+ start: new Date(item.startMs),
2834
+ end: new Date(item.endMs),
2835
+ status: 'occupied',
2836
+ events: [event]
2837
+ }, {
2838
+ events: [event]
1487
2839
  });
1488
2840
  }
1489
- return node;
2841
+ return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2842
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
2843
+ className: style$1['timeline-event-title'],
2844
+ children: event.title
2845
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
2846
+ className: style$1['timeline-event-time'],
2847
+ children: formatRange(event.start, event.end)
2848
+ })]
2849
+ });
1490
2850
  };
1491
- const slotClassName = classNames(style$1['slot-item'], style$1[`slot-${slot.status}`], {
1492
- [style$1['slot-selected']]: selected
1493
- });
1494
- const slotChildren = /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1495
- children: [/*#__PURE__*/jsxRuntime.jsx("span", {
1496
- className: style$1['slot-dot']
1497
- }), /*#__PURE__*/jsxRuntime.jsx("span", {
1498
- className: style$1['slot-time'],
1499
- children: formatRange(slot.start, slot.end)
1500
- }), slot.status === 'disabled' ? /*#__PURE__*/jsxRuntime.jsx(antd.Tag, {
1501
- className: style$1['slot-tag'],
1502
- children: formatMessage({
1503
- id: 'SlotItem.disabled'
1504
- })
1505
- }) : null, slot.status === 'occupied' ? /*#__PURE__*/jsxRuntime.jsx(antd.Tag, {
1506
- className: style$1['slot-tag'],
1507
- color: "red",
1508
- children: formatMessage({
1509
- id: 'SlotItem.occupied'
1510
- })
1511
- }) : null, slot.status === 'occupied' && renderOccupiedSlot ? renderOccupiedSlot(slot, {
1512
- events: slot.events
1513
- }) : null, slot.status === 'occupied' && !renderOccupiedSlot ? /*#__PURE__*/jsxRuntime.jsx("span", {
1514
- className: style$1['slot-title'],
1515
- children: slot.events.map(event => event.title).join('、')
2851
+ const selectionBlock = selection ? /*#__PURE__*/jsxRuntime.jsxs("div", {
2852
+ "data-timeline-selection": "",
2853
+ className: classNames(style$1['timeline-selection'], {
2854
+ [style$1['timeline-selection-dragging']]: dragging,
2855
+ [style$1['timeline-selection-conflict']]: conflicts.length > 0,
2856
+ [style$1['timeline-selection-compact']]: (selection.endMs - selection.startMs) * pxPerMs < 34
2857
+ }),
2858
+ style: {
2859
+ top: toY(selection.startMs),
2860
+ height: Math.max((selection.endMs - selection.startMs) * pxPerMs, 6)
2861
+ },
2862
+ children: [allowMultiSelect ? /*#__PURE__*/jsxRuntime.jsx("span", {
2863
+ "data-selection-edge": "start",
2864
+ className: classNames(style$1['timeline-selection-edge'], style$1['timeline-selection-edge-start'])
2865
+ }) : null, /*#__PURE__*/jsxRuntime.jsxs("div", {
2866
+ className: style$1['timeline-selection-label'],
2867
+ children: [/*#__PURE__*/jsxRuntime.jsx("span", {
2868
+ children: formatRange(selection.startMs, selection.endMs)
2869
+ }), conflicts.length ? /*#__PURE__*/jsxRuntime.jsx("span", {
2870
+ className: style$1['timeline-selection-badge'],
2871
+ children: conflicts.length
2872
+ }) : null]
2873
+ }), allowMultiSelect ? /*#__PURE__*/jsxRuntime.jsx("span", {
2874
+ "data-selection-edge": "end",
2875
+ className: classNames(style$1['timeline-selection-edge'], style$1['timeline-selection-edge-end'])
1516
2876
  }) : null]
2877
+ }) : null;
2878
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
2879
+ className: style$1['timeline'],
2880
+ style: {
2881
+ height
2882
+ },
2883
+ children: [/*#__PURE__*/jsxRuntime.jsx("div", {
2884
+ className: style$1['timeline-axis'],
2885
+ "aria-hidden": true,
2886
+ children: labels.map(ms => /*#__PURE__*/jsxRuntime.jsx("span", {
2887
+ className: style$1['timeline-axis-label'],
2888
+ style: {
2889
+ top: toY(ms)
2890
+ },
2891
+ children: dayjs__default["default"](ms).format(TIME_FORMAT)
2892
+ }, ms))
2893
+ }), /*#__PURE__*/jsxRuntime.jsxs("div", {
2894
+ ref: gridRef,
2895
+ className: classNames(style$1['timeline-grid'], {
2896
+ [style$1['timeline-grid-dragging']]: dragging
2897
+ }),
2898
+ onPointerDown: handlePointerDown,
2899
+ onPointerMove: handleHover,
2900
+ onPointerLeave: () => setHover(null),
2901
+ onContextMenu: event => {
2902
+ if (gridRef.current?.contains(event.target)) {
2903
+ event.preventDefault();
2904
+ }
2905
+ },
2906
+ onClickCapture: event => {
2907
+ if (suppressClickRef.current && gridRef.current?.contains(event.target)) {
2908
+ suppressClickRef.current = false;
2909
+ event.stopPropagation();
2910
+ }
2911
+ },
2912
+ children: [lines.map(line => /*#__PURE__*/jsxRuntime.jsx("span", {
2913
+ className: classNames(style$1['timeline-line'], {
2914
+ [style$1['timeline-line-hour']]: line.hour
2915
+ }),
2916
+ style: {
2917
+ top: toY(line.ms)
2918
+ }
2919
+ }, line.ms)), disabledRanges.map(range => {
2920
+ const block = /*#__PURE__*/jsxRuntime.jsx("div", {
2921
+ className: style$1['timeline-disabled'],
2922
+ style: {
2923
+ top: toY(range.startMs),
2924
+ height: (range.endMs - range.startMs) * pxPerMs
2925
+ },
2926
+ children: range.reason && (range.endMs - range.startMs) * pxPerMs >= 22 ? /*#__PURE__*/jsxRuntime.jsx("span", {
2927
+ className: style$1['timeline-disabled-reason'],
2928
+ children: range.reason
2929
+ }) : null
2930
+ }, range.startMs);
2931
+ return range.reason ? /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
2932
+ title: range.reason,
2933
+ children: block
2934
+ }, range.startMs) : block;
2935
+ }), eventItems.map(item => {
2936
+ const key = `${item.event.id}-${item.startMs}`;
2937
+ const blockHeight = (item.endMs - item.startMs) * pxPerMs;
2938
+ return /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
2939
+ arrow: false,
2940
+ trigger: "click",
2941
+ placement: isMobile ? 'bottom' : 'left',
2942
+ rootClassName: style$1['schedule-popover'],
2943
+ open: activeEventKey === key,
2944
+ onOpenChange: open => setActiveEventKey(open ? key : null),
2945
+ content: /*#__PURE__*/jsxRuntime.jsx(EventDetail, {
2946
+ event: item.event,
2947
+ date: date,
2948
+ onEventClick: onEventClick
2949
+ }),
2950
+ children: /*#__PURE__*/jsxRuntime.jsx("div", {
2951
+ "data-timeline-event": "",
2952
+ className: classNames(style$1['timeline-event'], {
2953
+ [style$1['timeline-event-disabled']]: item.event.disabled,
2954
+ [style$1['timeline-event-compact']]: blockHeight < 34
2955
+ }),
2956
+ style: {
2957
+ top: toY(item.startMs),
2958
+ height: Math.max(blockHeight - 2, 4),
2959
+ left: `calc(${item.column / item.columns * 100}% + 2px)`,
2960
+ width: `calc(${100 / item.columns}% - 4px)`,
2961
+ ...(item.event.color ? {
2962
+ '--timeline-event-color': item.event.color
2963
+ } : null)
2964
+ },
2965
+ children: renderEventContent(item)
2966
+ })
2967
+ }, key);
2968
+ }), hover && !selection ? /*#__PURE__*/jsxRuntime.jsxs("div", {
2969
+ className: style$1['timeline-ghost'],
2970
+ style: {
2971
+ top: toY(hover.startMs),
2972
+ height: (hover.endMs - hover.startMs) * pxPerMs
2973
+ },
2974
+ children: ["+ ", dayjs__default["default"](hover.startMs).format(TIME_FORMAT)]
2975
+ }) : null, showNow ? /*#__PURE__*/jsxRuntime.jsx("span", {
2976
+ className: style$1['timeline-now'],
2977
+ style: {
2978
+ top: toY(now)
2979
+ }
2980
+ }) : null, selectionBlock, formPopover && popoverAnchor ? /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
2981
+ arrow: false,
2982
+ trigger: "click",
2983
+ placement: popoverPlacement,
2984
+ rootClassName: style$1['schedule-popover'],
2985
+ open: popoverOpen && !dragging,
2986
+ onOpenChange: onPopoverOpenChange,
2987
+ content: formPopover,
2988
+ destroyOnHidden: true,
2989
+ children: /*#__PURE__*/jsxRuntime.jsx("span", {
2990
+ "aria-hidden": true,
2991
+ className: style$1['timeline-popover-anchor'],
2992
+ style: {
2993
+ top: toY(popoverAnchor.startMs),
2994
+ height: (popoverAnchor.endMs - popoverAnchor.startMs) * pxPerMs
2995
+ }
2996
+ })
2997
+ }) : null]
2998
+ })]
1517
2999
  });
1518
- const slotHandlers = {
1519
- onContextMenu,
1520
- onMouseDown,
1521
- onPointerDown,
1522
- onMouseEnter,
1523
- onMouseUp,
1524
- onClick
1525
- };
1526
- const inner = slot.status === 'disabled' ? /*#__PURE__*/jsxRuntime.jsx("button", {
1527
- type: "button",
1528
- className: slotClassName,
1529
- "data-slot-index": slot.index,
1530
- "data-slot-status": slot.status,
1531
- disabled: true,
1532
- onContextMenu: onContextMenu,
1533
- onMouseDown: onMouseDown,
1534
- onPointerDown: onPointerDown,
1535
- onMouseEnter: onMouseEnter,
1536
- onMouseUp: onMouseUp,
1537
- onClick: onClick,
1538
- children: slotChildren
1539
- }) : /*#__PURE__*/jsxRuntime.jsx("div", {
1540
- role: "button",
1541
- tabIndex: 0,
1542
- className: slotClassName,
1543
- "data-slot-index": slot.index,
1544
- "data-slot-status": slot.status,
1545
- ...slotHandlers,
1546
- children: slotChildren
1547
- });
1548
- return wrapWithPopover(inner);
1549
3000
  };
1550
3001
 
1551
3002
  function _catch(body, recover) {
@@ -1559,79 +3010,158 @@ function _catch(body, recover) {
1559
3010
  }
1560
3011
  return result;
1561
3012
  }
1562
- const SchedulePanel = ({
1563
- date,
1564
- slots,
1565
- loading,
1566
- allowMultiSelect,
1567
- selectedSlots,
1568
- setSelectedSlots,
1569
- onSlotRangeChange,
1570
- onFreeSlotClick,
1571
- onCreate,
1572
- formInner,
1573
- formProps,
1574
- defaultFormValues,
1575
- renderTimeSlot,
1576
- renderOccupiedSlot,
1577
- renderPanelHeader,
1578
- onEventClick
1579
- }) => {
3013
+ const SchedulePanel = _ref => {
3014
+ let {
3015
+ date,
3016
+ slots,
3017
+ events: dayEvents,
3018
+ dayStart,
3019
+ dayEnd,
3020
+ timeUnit,
3021
+ timeStep,
3022
+ hourHeight,
3023
+ allowConflict,
3024
+ loading,
3025
+ allowMultiSelect,
3026
+ onSlotRangeChange,
3027
+ onFreeSlotClick,
3028
+ onCreate,
3029
+ formInner,
3030
+ formProps,
3031
+ defaultFormValues,
3032
+ renderEventBlock,
3033
+ renderOccupiedSlot,
3034
+ renderPanelHeader,
3035
+ onEventClick
3036
+ } = _ref;
1580
3037
  const {
1581
3038
  formatMessage
1582
3039
  } = reactIntl.useIntl();
1583
- const events = react.useMemo(() => sortEventsByStart(dedupeEventsById(slots.flatMap(slot => slot.events || []))), [slots]);
3040
+ const isMobile = responsiveUtils.useIsMobile();
3041
+ const scrollRef = react.useRef(null);
3042
+ const interactionRef = react.useRef(0);
3043
+ const openedAtRef = react.useRef(0);
3044
+ const [selection, setSelection] = react.useState(null);
3045
+ const [dragging, setDragging] = react.useState(false);
3046
+ const [popoverOpen, setPopoverOpen] = react.useState(false);
3047
+ const [popoverPlacement, setPopoverPlacement] = react.useState('left');
3048
+ const [popoverAnchor, setPopoverAnchor] = react.useState(null);
3049
+ const day = react.useMemo(() => ({
3050
+ startMs: parseDayTime(date, dayStart).valueOf(),
3051
+ endMs: parseDayTime(date, dayEnd).valueOf()
3052
+ }), [date, dayEnd, dayStart]);
3053
+ const events = react.useMemo(() => sortEventsByStart(dayEvents || []), [dayEvents]);
3054
+ const eventItems = react.useMemo(() => layoutTimelineEvents(events, day), [day, events]);
3055
+ const disabledRanges = react.useMemo(() => mergeDisabledSlots(slots), [slots]);
3056
+ const blockers = react.useMemo(() => toBlockers(allowConflict ? disabledRanges : [...disabledRanges, ...eventItems]), [allowConflict, disabledRanges, eventItems]);
3057
+ const conflicts = react.useMemo(() => findOverlappingEvents(selection, eventItems.map(item => item.event)), [eventItems, selection]);
3058
+ const gap = selection && getFreeGap(selection.startMs, blockers, day) || day;
3059
+ const windowRange = react.useMemo(() => [new Date(gap.startMs), new Date(gap.endMs)], [gap.startMs, gap.endMs]);
1584
3060
  const panelHeader = renderPanelHeader?.({
1585
3061
  date: date.toDate(),
1586
3062
  mode: 'schedule',
1587
3063
  events
1588
3064
  });
1589
- const {
1590
- activePopoverKey,
1591
- dragging,
1592
- formPopoverKey,
1593
- selectedAnchorKey,
1594
- selectedKeys,
1595
- selectSlots,
1596
- slotsRef,
1597
- handleMouseDown,
1598
- handleMouseEnter,
1599
- handleMouseUp,
1600
- handleMouseLeave,
1601
- handlePopoverOpenChange
1602
- } = useScheduleSelectionGesture({
1603
- slots,
1604
- selectedSlots,
1605
- setSelectedSlots,
1606
- allowMultiSelect,
1607
- onSlotRangeChange,
1608
- onFreeSlotClick
1609
- });
1610
3065
  const {
1611
3066
  scrolled,
1612
3067
  onScroll
1613
- } = usePanelScrollShadow(date.format(DATE_FORMAT), slotsRef);
1614
- const formContext = {
3068
+ } = usePanelScrollShadow(date.format(DATE_FORMAT), scrollRef);
3069
+ const getFormContext = range => ({
1615
3070
  date: date.toDate(),
1616
- slots: selectedSlots
3071
+ start: range ? new Date(range.startMs) : null,
3072
+ end: range ? new Date(range.endMs) : null,
3073
+ slots: findOverlappingSlots(range, slots),
3074
+ conflicts: findOverlappingEvents(range, eventItems.map(item => item.event))
3075
+ });
3076
+ const emitRange = range => {
3077
+ if (range) {
3078
+ onSlotRangeChange?.(findOverlappingSlots(range, slots), {
3079
+ action: 'select',
3080
+ start: new Date(range.startMs),
3081
+ end: new Date(range.endMs)
3082
+ });
3083
+ } else {
3084
+ onSlotRangeChange?.([], {
3085
+ action: 'clear'
3086
+ });
3087
+ }
3088
+ };
3089
+ const clearSelection = () => {
3090
+ setPopoverOpen(false);
3091
+ setPopoverAnchor(null);
3092
+ setDragging(false);
3093
+ if (selection) {
3094
+ setSelection(null);
3095
+ emitRange(null);
3096
+ }
1617
3097
  };
1618
- const formPopover = selectedSlots.length && !dragging ? /*#__PURE__*/jsxRuntime.jsxs("div", {
1619
- className: style$1['form-popover'],
3098
+ const commitSelection = range => {
3099
+ setDragging(false);
3100
+ if (!range) {
3101
+ return;
3102
+ }
3103
+ if (!popoverOpen) {
3104
+ const stacked = !!scrollRef.current?.closest('[data-schedule-layout="stacked"]');
3105
+ setPopoverPlacement(stacked ? 'bottom' : 'left');
3106
+ setPopoverAnchor(range);
3107
+ }
3108
+ setSelection(range);
3109
+ openedAtRef.current = Date.now();
3110
+ setPopoverOpen(true);
3111
+ emitRange(range);
3112
+ };
3113
+ const formContext = getFormContext(selection);
3114
+ const formPopover = selection ? /*#__PURE__*/jsxRuntime.jsxs("div", {
3115
+ className: classNames(style$1['form-popover'], {
3116
+ [style$1['form-sheet-content']]: isMobile
3117
+ }),
1620
3118
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
1621
3119
  className: style$1['form-popover-title'],
1622
3120
  children: formatMessage({
1623
- id: 'SchedulePanel.newSchedule'
1624
- }, {
1625
- range: formatRange(selectedSlots[0].start, selectedSlots[selectedSlots.length - 1].end)
3121
+ id: 'SchedulePanel.newScheduleTitle'
1626
3122
  })
1627
- }), /*#__PURE__*/jsxRuntime.jsxs(FormInfo.Form, {
3123
+ }), /*#__PURE__*/jsxRuntime.jsx(TimeWindow, {
3124
+ className: style$1['form-time-window'],
3125
+ size: isMobile ? 'default' : 'small',
3126
+ value: [new Date(selection.startMs), new Date(selection.endMs)],
3127
+ range: windowRange,
3128
+ step: timeStep,
3129
+ minDuration: timeStep,
3130
+ resizable: allowMultiSelect,
3131
+ debounceWait: 120,
3132
+ format: "HH:mm",
3133
+ separator: " - ",
3134
+ onChange: value => {
3135
+ if (!value) {
3136
+ return;
3137
+ }
3138
+ const range = {
3139
+ startMs: value[0].getTime(),
3140
+ endMs: value[1].getTime()
3141
+ };
3142
+ setSelection(range);
3143
+ emitRange(range);
3144
+ }
3145
+ }), conflicts.length ? /*#__PURE__*/jsxRuntime.jsx(antd.Alert, {
3146
+ className: style$1['form-conflict'],
3147
+ type: "warning",
3148
+ showIcon: true,
3149
+ title: `${formatMessage({
3150
+ id: 'SchedulePanel.conflict'
3151
+ }, {
3152
+ count: conflicts.length
3153
+ })}:${conflicts.map(event => event.title).join('、')}`
3154
+ }) : null, /*#__PURE__*/jsxRuntime.jsxs(FormInfo.Form, {
1628
3155
  ...formProps,
3156
+ ...(isMobile ? {
3157
+ size: 'large'
3158
+ } : null),
1629
3159
  data: getDefaultFormValues(defaultFormValues, formContext),
1630
3160
  onSubmit: function (values) {
1631
3161
  try {
1632
3162
  const _temp = _catch(function () {
1633
- return Promise.resolve(onCreate?.(values, formContext)).then(function () {
1634
- selectSlots([]);
3163
+ return Promise.resolve(onCreate?.(values, getFormContext(selection))).then(function () {
3164
+ clearSelection();
1635
3165
  });
1636
3166
  }, function () {});
1637
3167
  return Promise.resolve(_temp && _temp.then ? _temp.then(function () {}) : void 0);
@@ -1642,7 +3172,7 @@ const SchedulePanel = ({
1642
3172
  children: [formInner || /*#__PURE__*/jsxRuntime.jsx(DefaultFormInner, {}), /*#__PURE__*/jsxRuntime.jsxs("div", {
1643
3173
  className: style$1['form-actions'],
1644
3174
  children: [/*#__PURE__*/jsxRuntime.jsx(antd.Button, {
1645
- onClick: () => selectSlots([]),
3175
+ onClick: clearSelection,
1646
3176
  children: formatMessage({
1647
3177
  id: 'SchedulePanel.cancel'
1648
3178
  })
@@ -1654,7 +3184,7 @@ const SchedulePanel = ({
1654
3184
  })]
1655
3185
  })]
1656
3186
  }) : null;
1657
- return /*#__PURE__*/jsxRuntime.jsx(antd.Card, {
3187
+ return /*#__PURE__*/jsxRuntime.jsxs(antd.Card, {
1658
3188
  bordered: false,
1659
3189
  title: panelHeader || /*#__PURE__*/jsxRuntime.jsx(PanelTitle, {
1660
3190
  date: date,
@@ -1663,69 +3193,93 @@ const SchedulePanel = ({
1663
3193
  })
1664
3194
  }),
1665
3195
  className: panelStyle['panel-card'],
1666
- children: /*#__PURE__*/jsxRuntime.jsx(antd.Spin, {
3196
+ children: [/*#__PURE__*/jsxRuntime.jsx(antd.Spin, {
1667
3197
  spinning: !!loading,
1668
3198
  classNames: {
1669
3199
  root: panelStyle['panel-spin'],
1670
3200
  container: panelStyle['panel-spin-container']
1671
3201
  },
1672
3202
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
1673
- ref: slotsRef,
1674
- className: classNames(style$1['slots'], {
1675
- [style$1['slots-dragging']]: dragging
1676
- }),
3203
+ ref: scrollRef,
3204
+ className: style$1['slots'],
1677
3205
  onScroll: onScroll,
1678
- onMouseUp: handleMouseUp,
1679
- onMouseLeave: handleMouseLeave,
1680
3206
  children: [/*#__PURE__*/jsxRuntime.jsx(PanelScrollShadow, {
1681
3207
  visible: scrolled
1682
- }), /*#__PURE__*/jsxRuntime.jsx("div", {
1683
- className: style$1['slots-content'],
1684
- children: slots.map((slot, index) => {
1685
- const slotWithIndex = {
1686
- ...slot,
1687
- index
1688
- };
1689
- const occupiedPopoverKey = `occupied:${slot.key}`;
1690
- const currentPopoverKey = slot.key === selectedAnchorKey && formPopover ? formPopoverKey : slot.status === 'occupied' ? occupiedPopoverKey : null;
1691
- return /*#__PURE__*/jsxRuntime.jsx(SlotItem, {
1692
- slot: slotWithIndex,
1693
- date: date.toDate(),
1694
- selected: selectedKeys.has(slot.key),
1695
- formPopover: slot.key === selectedAnchorKey ? formPopover : null,
1696
- popoverOpen: !!currentPopoverKey && activePopoverKey === currentPopoverKey,
1697
- onPopoverOpenChange: open => handlePopoverOpenChange(open, currentPopoverKey, {
1698
- isForm: currentPopoverKey === formPopoverKey
1699
- }),
1700
- renderTimeSlot: renderTimeSlot,
1701
- renderOccupiedSlot: renderOccupiedSlot,
1702
- onEventClick: onEventClick,
1703
- onContextMenu: event => {
1704
- if (slot.status === 'free') {
1705
- event.preventDefault();
1706
- }
1707
- },
1708
- onMouseDown: event => handleMouseDown(event, slot, index),
1709
- onMouseEnter: () => handleMouseEnter(index),
1710
- onMouseUp: handleMouseUp,
1711
- onClick: undefined
1712
- }, slot.key);
1713
- })
3208
+ }), /*#__PURE__*/jsxRuntime.jsx(Timeline, {
3209
+ date: date.toDate(),
3210
+ day: day,
3211
+ hourHeight: hourHeight,
3212
+ timeUnit: timeUnit,
3213
+ timeStep: timeStep,
3214
+ eventItems: eventItems,
3215
+ disabledRanges: disabledRanges,
3216
+ blockers: blockers,
3217
+ conflicts: conflicts,
3218
+ allowMultiSelect: allowMultiSelect,
3219
+ selection: selection,
3220
+ dragging: dragging,
3221
+ formPopover: isMobile ? null : formPopover,
3222
+ isMobile: isMobile,
3223
+ popoverOpen: popoverOpen,
3224
+ popoverPlacement: popoverPlacement,
3225
+ popoverAnchor: popoverAnchor,
3226
+ interactionRef: interactionRef,
3227
+ onPopoverOpenChange: open => {
3228
+ if (open) {
3229
+ setPopoverOpen(true);
3230
+ return;
3231
+ }
3232
+ if (Date.now() - interactionRef.current < 400) {
3233
+ return;
3234
+ }
3235
+ clearSelection();
3236
+ },
3237
+ onSelectionDrag: range => {
3238
+ setDragging(true);
3239
+ setSelection(range);
3240
+ },
3241
+ onSelectionCommit: commitSelection,
3242
+ onFreeSlotClick: slot => onFreeSlotClick?.({
3243
+ ...slot,
3244
+ key: `${date.format(DATE_FORMAT)}-${slot.start.getTime()}`,
3245
+ date: date.format(DATE_FORMAT),
3246
+ status: 'free',
3247
+ events: []
3248
+ }),
3249
+ onEventClick: onEventClick,
3250
+ renderEventBlock: renderEventBlock,
3251
+ renderOccupiedSlot: renderOccupiedSlot
1714
3252
  })]
1715
3253
  })
1716
- })
3254
+ }), isMobile ? /*#__PURE__*/jsxRuntime.jsx(antd.Drawer, {
3255
+ placement: "bottom",
3256
+ size: "auto",
3257
+ closable: false,
3258
+ rootClassName: style$1['form-sheet'],
3259
+ open: !!selection && popoverOpen && !dragging,
3260
+ onClose: () => {
3261
+ if (Date.now() - openedAtRef.current < 500) {
3262
+ return;
3263
+ }
3264
+ clearSelection();
3265
+ },
3266
+ destroyOnHidden: true,
3267
+ children: formPopover
3268
+ }) : null]
1717
3269
  });
1718
3270
  };
1719
3271
 
1720
- 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-event-list-expanded":"kne-react-calendar_Av0q4","schedule-calendar-stacked":"kne-react-calendar_zGXr5"};
3272
+ 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"};
1721
3273
 
1722
3274
  dayjs__default["default"].extend(customParseFormat__default["default"]);
1723
3275
  const buildMonthRuleCache = _ref => {
1724
- let currentDate = _ref.currentDate,
1725
- mode = _ref.mode,
1726
- disabledDate = _ref.disabledDate,
1727
- disabledDateTime = _ref.disabledDateTime,
1728
- availableDateTime = _ref.availableDateTime;
3276
+ let {
3277
+ currentDate,
3278
+ mode,
3279
+ disabledDate,
3280
+ disabledDateTime,
3281
+ availableDateTime
3282
+ } = _ref;
1729
3283
  const cache = new Map();
1730
3284
  const monthStart = currentDate.startOf('month');
1731
3285
  const monthEnd = currentDate.endOf('month');
@@ -1743,65 +3297,54 @@ const buildMonthRuleCache = _ref => {
1743
3297
  return cache;
1744
3298
  };
1745
3299
  const ScheduleCalendarInner = props => {
1746
- const _props$mode = props.mode,
1747
- mode = _props$mode === void 0 ? 'view' : _props$mode,
1748
- _props$events = props.events,
1749
- events = _props$events === void 0 ? [] : _props$events,
1750
- loading = props.loading,
1751
- disabledDate = props.disabledDate,
1752
- disabledDateTime = props.disabledDateTime,
1753
- availableDateTime = props.availableDateTime,
1754
- _props$badgeMaxCount = props.badgeMaxCount,
1755
- badgeMaxCount = _props$badgeMaxCount === void 0 ? 9 : _props$badgeMaxCount,
1756
- _props$timeUnit = props.timeUnit,
1757
- timeUnit = _props$timeUnit === void 0 ? 30 : _props$timeUnit,
1758
- _props$dayStart = props.dayStart,
1759
- dayStart = _props$dayStart === void 0 ? '09:00' : _props$dayStart,
1760
- _props$dayEnd = props.dayEnd,
1761
- dayEnd = _props$dayEnd === void 0 ? '18:00' : _props$dayEnd,
1762
- _props$allowMultiSele = props.allowMultiSelect,
1763
- allowMultiSelect = _props$allowMultiSele === void 0 ? mode === 'schedule' : _props$allowMultiSele,
1764
- renderDateCell = props.renderDateCell,
1765
- renderEventItem = props.renderEventItem,
1766
- renderTimeSlot = props.renderTimeSlot,
1767
- renderOccupiedSlot = props.renderOccupiedSlot,
1768
- renderPanelHeader = props.renderPanelHeader,
1769
- formInner = props.formInner,
1770
- formProps = props.formProps,
1771
- defaultFormValues = props.defaultFormValues,
1772
- onDateChange = props.onDateChange,
1773
- onEventClick = props.onEventClick,
1774
- onFreeSlotClick = props.onFreeSlotClick,
1775
- onSlotRangeChange = props.onSlotRangeChange,
1776
- onCreate = props.onCreate,
1777
- className = props.className,
1778
- propsStyle = props.style,
1779
- _props$stackAt = props.stackAt,
1780
- stackAt = _props$stackAt === void 0 ? LAYOUT_STACK_BREAKPOINT : _props$stackAt,
1781
- _props$eventListScrol = props.eventListScrollable,
1782
- eventListScrollable = _props$eventListScrol === void 0 ? true : _props$eventListScrol,
1783
- calendarProps = props.calendarProps,
1784
- panelProps = props.panelProps;
3300
+ const {
3301
+ mode = 'view',
3302
+ events = [],
3303
+ loading,
3304
+ disabledDate,
3305
+ disabledDateTime,
3306
+ availableDateTime,
3307
+ badgeMaxCount = 9,
3308
+ timeUnit = 30,
3309
+ dayStart = '09:00',
3310
+ dayEnd = '18:00',
3311
+ timeStep = 5,
3312
+ hourHeight = 56,
3313
+ allowConflict = false,
3314
+ allowMultiSelect = mode === 'schedule',
3315
+ renderDateCell,
3316
+ renderEventItem,
3317
+ renderEventBlock,
3318
+ renderOccupiedSlot,
3319
+ renderPanelHeader,
3320
+ formInner,
3321
+ formProps,
3322
+ defaultFormValues,
3323
+ onDateChange,
3324
+ onEventClick,
3325
+ onFreeSlotClick,
3326
+ onSlotRangeChange,
3327
+ onCreate,
3328
+ className,
3329
+ style: propsStyle,
3330
+ stackAt = LAYOUT_STACK_BREAKPOINT,
3331
+ eventListScrollable = true,
3332
+ calendarProps,
3333
+ panelProps
3334
+ } = props;
1785
3335
  const rootRef = react.useRef(null);
1786
3336
  const calendarRef = react.useRef(null);
1787
3337
  const stacked = useStackedLayout(rootRef, stackAt);
1788
3338
  const eventListExpanded = mode === 'view' && !eventListScrollable;
1789
3339
  const stackedLayoutStyle = useStackedPanelLayout(rootRef, calendarRef, stacked && !eventListExpanded);
1790
- const _useControlValue = useControlValue__default["default"](props),
1791
- value = _useControlValue[0],
1792
- setValue = _useControlValue[1];
3340
+ const [value, setValue] = useControlValue__default["default"](props);
1793
3341
  const selectedDate = react.useMemo(() => toDayjs(value) || dayjs__default["default"](), [value]);
1794
- const _useControlledState = useControlledState({
1795
- value: props.current,
1796
- defaultValue: props.defaultCurrent || selectedDate,
1797
- onChange: props.onCurrentChange
1798
- }),
1799
- current = _useControlledState[0],
1800
- setCurrent = _useControlledState[1];
3342
+ const [current, setCurrent] = useControlledState({
3343
+ value: props.current,
3344
+ defaultValue: props.defaultCurrent || selectedDate,
3345
+ onChange: props.onCurrentChange
3346
+ });
1801
3347
  const currentDate = react.useMemo(() => toDayjs(current) || selectedDate, [current, selectedDate]);
1802
- const _useState = react.useState([]),
1803
- selectedSlots = _useState[0],
1804
- setSelectedSlots = _useState[1];
1805
3348
  const eventGroup = react.useMemo(() => groupEventsByDate(events), [events]);
1806
3349
  const selectedEvents = react.useMemo(() => eventGroup[selectedDate.format(DATE_FORMAT)] || [], [eventGroup, selectedDate]);
1807
3350
  const ruleContext = react.useMemo(() => ({
@@ -1810,17 +3353,19 @@ const ScheduleCalendarInner = props => {
1810
3353
  disabledDateTime,
1811
3354
  availableDateTime
1812
3355
  }), [availableDateTime, disabledDate, disabledDateTime, mode]);
1813
- const monthRuleCache = react.useMemo(() => buildMonthRuleCache(_extends({
1814
- currentDate
1815
- }, ruleContext)), [currentDate, ruleContext]);
3356
+ const monthRuleCache = react.useMemo(() => buildMonthRuleCache({
3357
+ currentDate,
3358
+ ...ruleContext
3359
+ }), [currentDate, ruleContext]);
1816
3360
  const getDateRule = react.useCallback(date => {
1817
3361
  const cached = monthRuleCache.get(date.format(DATE_FORMAT));
1818
3362
  if (cached !== undefined) {
1819
3363
  return cached;
1820
3364
  }
1821
- return checkRule(_extends({
1822
- date
1823
- }, ruleContext));
3365
+ return checkRule({
3366
+ date,
3367
+ ...ruleContext
3368
+ });
1824
3369
  }, [monthRuleCache, ruleContext]);
1825
3370
  const slots = react.useMemo(() => generateTimeSlots({
1826
3371
  date: selectedDate,
@@ -1836,8 +3381,7 @@ const ScheduleCalendarInner = props => {
1836
3381
  const handleSelectDate = (date, info) => {
1837
3382
  const nextDate = date.startOf('day').toDate();
1838
3383
  setValue(nextDate, info);
1839
- onDateChange == null || onDateChange(nextDate, info);
1840
- setSelectedSlots([]);
3384
+ onDateChange?.(nextDate, info);
1841
3385
  };
1842
3386
  const handleCurrentChange = (date, info) => {
1843
3387
  setCurrent(date.startOf('month').toDate(), info);
@@ -1879,7 +3423,8 @@ const ScheduleCalendarInner = props => {
1879
3423
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
1880
3424
  ref: calendarRef,
1881
3425
  className: style['calendar-side'],
1882
- children: /*#__PURE__*/jsxRuntime.jsx(antd.Calendar, _extends({}, calendarProps, {
3426
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Calendar, {
3427
+ ...calendarProps,
1883
3428
  fullscreen: false,
1884
3429
  value: currentDate,
1885
3430
  headerRender: () => {
@@ -1905,28 +3450,33 @@ const ScheduleCalendarInner = props => {
1905
3450
  onPanelChange: date => handleCurrentChange(date, {
1906
3451
  source: 'picker'
1907
3452
  })
1908
- }))
1909
- }), /*#__PURE__*/jsxRuntime.jsx("div", _extends({
1910
- className: style['panel-side']
1911
- }, panelProps, {
3453
+ })
3454
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
3455
+ className: style['panel-side'],
3456
+ ...panelProps,
1912
3457
  children: mode === 'schedule' ? /*#__PURE__*/jsxRuntime.jsx(SchedulePanel, {
1913
3458
  date: selectedDate,
1914
3459
  slots: slots,
3460
+ events: selectedEvents,
3461
+ dayStart: dayStart,
3462
+ dayEnd: dayEnd,
3463
+ timeUnit: timeUnit,
3464
+ timeStep: timeStep,
3465
+ hourHeight: hourHeight,
3466
+ allowConflict: allowConflict,
1915
3467
  loading: loading,
1916
3468
  allowMultiSelect: allowMultiSelect,
1917
- selectedSlots: selectedSlots,
1918
- setSelectedSlots: setSelectedSlots,
1919
3469
  onSlotRangeChange: onSlotRangeChange,
1920
3470
  onFreeSlotClick: onFreeSlotClick,
1921
3471
  onCreate: onCreate,
1922
3472
  formInner: formInner,
1923
3473
  formProps: formProps,
1924
3474
  defaultFormValues: defaultFormValues,
1925
- renderTimeSlot: renderTimeSlot,
3475
+ renderEventBlock: renderEventBlock,
1926
3476
  renderOccupiedSlot: renderOccupiedSlot,
1927
3477
  renderPanelHeader: renderPanelHeader,
1928
3478
  onEventClick: onEventClick
1929
- }) : /*#__PURE__*/jsxRuntime.jsx(ViewPanel, {
3479
+ }, selectedDate.format(DATE_FORMAT)) : /*#__PURE__*/jsxRuntime.jsx(ViewPanel, {
1930
3480
  date: selectedDate,
1931
3481
  events: selectedEvents,
1932
3482
  loading: loading,
@@ -1935,7 +3485,7 @@ const ScheduleCalendarInner = props => {
1935
3485
  onEventClick: onEventClick,
1936
3486
  scrollable: eventListScrollable
1937
3487
  })
1938
- }))]
3488
+ })]
1939
3489
  })
1940
3490
  });
1941
3491
  };
@@ -1943,6 +3493,7 @@ const ScheduleCalendar = withLocale(ScheduleCalendarInner);
1943
3493
 
1944
3494
  exports.ReactCalendar = ScheduleCalendar;
1945
3495
  exports.ScheduleCalendar = ScheduleCalendar;
3496
+ exports.TimeWindow = TimeWindow;
1946
3497
  exports.dedupeEventsById = dedupeEventsById;
1947
3498
  exports["default"] = ScheduleCalendar;
1948
3499
  exports.generateTimeSlots = generateTimeSlots;