@kne/react-calendar 1.0.5 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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,27 +779,27 @@ 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
  })
794
799
  });
795
800
  };
796
801
 
797
- var panelStyle = {"panel-card":"kne-react-calendar_uQsUE","panel-scroll-body":"kne-react-calendar_ujAl5","panel-scroll-content":"kne-react-calendar_t2e0j","panel-spin":"kne-react-calendar_tLux2","panel-spin-container":"kne-react-calendar_iW4mn","panel-title":"kne-react-calendar_C6NB3","panel-scroll-shadow":"kne-react-calendar_3ecxf","panel-scroll-shadow-visible":"kne-react-calendar_TvxhV","detail-card":"kne-react-calendar_3sEou"};
802
+ var panelStyle = {"panel-card":"kne-react-calendar_uQsUE","panel-scroll-body":"kne-react-calendar_ujAl5","panel-scroll-content":"kne-react-calendar_t2e0j","panel-spin":"kne-react-calendar_tLux2","panel-spin-container":"kne-react-calendar_iW4mn","panel-card-expanded":"kne-react-calendar_OzKbW","panel-scroll-body-expanded":"kne-react-calendar_jmktP","panel-title":"kne-react-calendar_C6NB3","panel-scroll-shadow":"kne-react-calendar_3ecxf","panel-scroll-shadow-visible":"kne-react-calendar_TvxhV","detail-card":"kne-react-calendar_3sEou"};
798
803
 
799
804
  const PanelTitle = ({
800
805
  date,
@@ -846,14 +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
- }) => {
854
+ const ViewPanel = _ref => {
855
+ let {
856
+ date,
857
+ events,
858
+ loading,
859
+ renderEventItem,
860
+ renderPanelHeader,
861
+ onEventClick,
862
+ scrollable = true
863
+ } = _ref;
857
864
  const {
858
865
  formatMessage
859
866
  } = reactIntl.useIntl();
@@ -874,7 +881,7 @@ const ViewPanel = ({
874
881
  date: date,
875
882
  count: sortedEvents.length
876
883
  }),
877
- className: panelStyle['panel-card'],
884
+ className: classNames(panelStyle['panel-card'], !scrollable && panelStyle['panel-card-expanded']),
878
885
  children: /*#__PURE__*/jsxRuntime.jsx(antd.Spin, {
879
886
  spinning: !!loading,
880
887
  classNames: {
@@ -883,11 +890,11 @@ const ViewPanel = ({
883
890
  },
884
891
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
885
892
  ref: scrollRef,
886
- className: panelStyle['panel-scroll-body'],
887
- onScroll: onScroll,
888
- children: [/*#__PURE__*/jsxRuntime.jsx(PanelScrollShadow, {
893
+ className: classNames(panelStyle['panel-scroll-body'], !scrollable && panelStyle['panel-scroll-body-expanded']),
894
+ onScroll: scrollable ? onScroll : undefined,
895
+ children: [scrollable ? /*#__PURE__*/jsxRuntime.jsx(PanelScrollShadow, {
889
896
  visible: scrolled
890
- }), /*#__PURE__*/jsxRuntime.jsx("div", {
897
+ }) : null, /*#__PURE__*/jsxRuntime.jsx("div", {
891
898
  className: panelStyle['panel-scroll-content'],
892
899
  children: sortedEvents.length ? /*#__PURE__*/jsxRuntime.jsx(antd.List, {
893
900
  dataSource: sortedEvents,
@@ -909,448 +916,1468 @@ const ViewPanel = ({
909
916
  });
910
917
  };
911
918
 
912
- const LONG_PRESS_DELAY = 400;
913
- const SCROLL_CANCEL_THRESHOLD = 12;
914
- const TAP_MOVE_THRESHOLD = 8;
915
- const getPopoverKey = slot => slot ? `form:${slot.key}` : null;
916
- const getSlotIndexFromPoint = (container, clientX, clientY) => {
917
- const slotElements = container?.querySelectorAll?.('[data-slot-index]');
918
- if (!slotElements?.length) {
919
- return null;
919
+ const MINUTE_MS$1 = 60 * 1000;
920
+ const toMs = value => {
921
+ if (value === null || typeof value === 'undefined') {
922
+ return NaN;
920
923
  }
921
- let closestIndex = null;
922
- let closestDistance = Infinity;
923
- for (const candidate of slotElements) {
924
- const rect = candidate.getBoundingClientRect();
925
- const index = Number(candidate.getAttribute('data-slot-index'));
926
- if (Number.isNaN(index)) {
927
- 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;
928
971
  }
929
- if (clientY >= rect.top && clientY <= rect.bottom && clientX >= rect.left && clientX <= rect.right) {
930
- 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;
931
978
  }
932
- if (clientX < rect.left || clientX > rect.right) {
933
- 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;
934
1000
  }
935
- const centerY = (rect.top + rect.bottom) / 2;
936
- const distance = Math.abs(clientY - centerY);
937
- if (distance < closestDistance) {
938
- closestDistance = distance;
939
- closestIndex = index;
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;
1027
+ }
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);
940
1032
  }
941
1033
  }
942
- return closestIndex;
1034
+ if (ms < startMs || ms >= endMs || gapEnd <= gapStart) {
1035
+ return null;
1036
+ }
1037
+ return {
1038
+ startMs: gapStart,
1039
+ endMs: gapEnd
1040
+ };
943
1041
  };
944
- const clearDocumentTextSelection = () => {
945
- const selection = document.getSelection?.();
946
- if (selection && !selection.isCollapsed) {
947
- 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
+ };
948
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
+ };
949
1078
  };
950
- const useScheduleSelectionGesture = ({
951
- slots,
952
- selectedSlots,
953
- setSelectedSlots,
954
- allowMultiSelect,
955
- onSlotRangeChange,
956
- 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
957
1086
  }) => {
958
- const [dragging, setDragging] = react.useState(false);
959
- const [activePopoverKey, setActivePopoverKey] = react.useState(null);
960
- const slotsRef = react.useRef(null);
961
- const draggingRef = react.useRef(false);
962
- const dragStartIndexRef = react.useRef(null);
963
- const longPressTimerRef = react.useRef(null);
964
- const touchStateRef = react.useRef(null);
965
- const slotsDataRef = react.useRef(slots);
966
- const selectedSlotsRef = react.useRef(selectedSlots);
967
- const allowMultiSelectRef = react.useRef(allowMultiSelect);
968
- const suppressTriggerUntilRef = react.useRef(0);
969
- const suppressMouseUntilRef = react.useRef(0);
970
- const scrollLockRef = react.useRef(null);
971
- const handlersRef = react.useRef({});
972
- slotsDataRef.current = slots;
973
- selectedSlotsRef.current = selectedSlots;
974
- allowMultiSelectRef.current = allowMultiSelect;
975
- const selectedKeys = react.useMemo(() => new Set(selectedSlots.map(slot => slot.key)), [selectedSlots]);
976
- const selectedAnchorKey = selectedSlots[selectedSlots.length - 1]?.key;
977
- const formPopoverKey = selectedAnchorKey ? `form:${selectedAnchorKey}` : null;
978
- const clearLongPress = react.useCallback(() => {
979
- if (longPressTimerRef.current) {
980
- window.clearTimeout(longPressTimerRef.current);
981
- longPressTimerRef.current = null;
982
- }
983
- }, []);
984
- const unlockContainerScroll = react.useCallback(() => {
985
- const container = slotsRef.current;
986
- if (!container || scrollLockRef.current == null) {
987
- 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;
988
1586
  }
989
- const scrollTop = scrollLockRef.current;
990
- container.style.overflow = '';
991
- container.style.touchAction = '';
992
- container.scrollTop = scrollTop;
993
- scrollLockRef.current = null;
994
- }, []);
995
- const lockContainerScroll = react.useCallback(() => {
996
- const container = slotsRef.current;
997
- if (!container || scrollLockRef.current != null) {
998
- return;
1587
+ setWidth(node.getBoundingClientRect().width);
1588
+ if (typeof ResizeObserver === 'undefined') {
1589
+ return undefined;
999
1590
  }
1000
- scrollLockRef.current = container.scrollTop;
1001
- container.style.overflow = 'hidden';
1002
- container.style.touchAction = 'none';
1003
- }, []);
1004
- const resetGestureState = react.useCallback(() => {
1005
- clearLongPress();
1006
- const container = slotsRef.current;
1007
- const touchState = touchStateRef.current;
1008
- if (container && touchState?.pointerId != null && container.hasPointerCapture?.(touchState.pointerId)) {
1009
- try {
1010
- container.releasePointerCapture(touchState.pointerId);
1011
- } catch {
1012
- // 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);
1013
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;
1014
1635
  }
1015
- draggingRef.current = false;
1016
- dragStartIndexRef.current = null;
1017
- touchStateRef.current = null;
1018
- container?.classList.remove('schedule-calendar-touch-dragging');
1019
- unlockContainerScroll();
1020
- setDragging(false);
1021
- }, [clearLongPress, unlockContainerScroll]);
1022
- const selectSlots = react.useCallback(nextSlots => {
1023
- selectedSlotsRef.current = nextSlots;
1024
- if (!nextSlots.length) {
1025
- setActivePopoverKey(null);
1026
- resetGestureState();
1027
- }
1028
- setSelectedSlots(nextSlots);
1029
- onSlotRangeChange?.(nextSlots, {
1030
- 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
1031
1690
  });
1032
- }, [onSlotRangeChange, resetGestureState, setSelectedSlots]);
1033
- const getSelectableRange = react.useCallback((startIndex, endIndex) => {
1034
- const [start, end] = [Math.min(startIndex, endIndex), Math.max(startIndex, endIndex)];
1035
- return slotsDataRef.current.slice(start, end + 1).filter(slot => slot.status === 'free');
1036
- }, []);
1037
- const showFormPopoverForSlots = react.useCallback(slotsList => {
1038
- const anchorSlot = slotsList[slotsList.length - 1];
1039
- if (anchorSlot) {
1040
- 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);
1041
1723
  }
1042
- }, []);
1043
- const startSlotSelection = react.useCallback((slot, index, {
1044
- showPopover = true,
1045
- lockScroll = false
1046
- } = {}) => {
1047
- clearDocumentTextSelection();
1048
- setActivePopoverKey(getPopoverKey(slot));
1049
- draggingRef.current = true;
1050
- dragStartIndexRef.current = index;
1051
- slotsRef.current?.classList.add('schedule-calendar-touch-dragging');
1052
- if (lockScroll) {
1053
- lockContainerScroll();
1054
- }
1055
- setDragging(true);
1056
- selectSlots([slot]);
1057
- if (!showPopover) {
1058
- suppressTriggerUntilRef.current = Date.now() + 700;
1059
- setActivePopoverKey(null);
1060
- }
1061
- onFreeSlotClick?.(slot);
1062
- }, [lockContainerScroll, onFreeSlotClick, selectSlots]);
1063
- const finishSelection = react.useCallback(({
1064
- fromTouch = false
1065
- } = {}) => {
1066
- const hadSelection = selectedSlotsRef.current.length > 0;
1067
- resetGestureState();
1068
- if (hadSelection) {
1069
- if (fromTouch) {
1070
- suppressTriggerUntilRef.current = Date.now() + 500;
1071
- }
1072
- showFormPopoverForSlots(selectedSlotsRef.current);
1724
+ if (rangeStartMs != null) {
1725
+ next = Math.max(next, rangeStartMs);
1073
1726
  }
1074
- }, [resetGestureState, showFormPopoverForSlots]);
1075
- const updateSelectionRange = react.useCallback(index => {
1076
- const startIndex = dragStartIndexRef.current;
1077
- if (startIndex === null || !allowMultiSelectRef.current) {
1078
- 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);
1079
1743
  }
1080
- const nextSlots = getSelectableRange(startIndex, index);
1081
- selectSlots(nextSlots);
1082
- }, [getSelectableRange, selectSlots]);
1083
- const shouldIgnoreMouseSelectionEvent = react.useCallback(() => {
1084
- return !!touchStateRef.current || Date.now() < suppressMouseUntilRef.current;
1085
- }, []);
1086
- const handleMouseDown = react.useCallback((event, slot, index) => {
1087
- if (shouldIgnoreMouseSelectionEvent()) {
1744
+ if (settled) {
1745
+ frameRef.current = null;
1746
+ lastFrameRef.current = 0;
1747
+ easeRef.current = VISUAL_EASE_MS;
1748
+ glidingRef.current = false;
1088
1749
  return;
1089
1750
  }
1090
- if (slot.status !== 'free') {
1091
- return;
1751
+ frameRef.current = requestAnimationFrame(runFrame);
1752
+ };
1753
+ const stopGlide = () => {
1754
+ if (!glidingRef.current) {
1755
+ return false;
1092
1756
  }
1093
- event.preventDefault();
1094
- startSlotSelection(slot, index);
1095
- }, [shouldIgnoreMouseSelectionEvent, startSlotSelection]);
1096
- const handleMouseEnter = react.useCallback(index => {
1097
- if (shouldIgnoreMouseSelectionEvent() || !draggingRef.current) {
1098
- 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;
1099
1766
  }
1100
- updateSelectionRange(index);
1101
- }, [shouldIgnoreMouseSelectionEvent, updateSelectionRange]);
1102
- const handleMouseUp = react.useCallback(() => {
1103
- if (shouldIgnoreMouseSelectionEvent()) {
1104
- return;
1767
+ visualTargetRef.current = visualRef.current;
1768
+ return true;
1769
+ };
1770
+ const glideTo = function (ms, easeMs) {
1771
+ if (easeMs === void 0) {
1772
+ easeMs = inertiaTau;
1105
1773
  }
1106
- finishSelection();
1107
- }, [finishSelection, shouldIgnoreMouseSelectionEvent]);
1108
- const handleMouseLeave = react.useCallback(() => {
1109
- 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);
1110
1796
  return;
1111
1797
  }
1112
- finishSelection();
1113
- }, [finishSelection, shouldIgnoreMouseSelectionEvent]);
1114
- const handlePopoverOpenChange = react.useCallback((open, currentPopoverKey, {
1115
- isForm = false
1116
- } = {}) => {
1117
- 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) {
1118
1816
  return;
1119
1817
  }
1120
- if (!open && isForm) {
1121
- selectSlots([]);
1818
+ if (visualRef.current == null) {
1819
+ visualRef.current = baseStartMs;
1820
+ visualTargetRef.current = baseStartMs;
1122
1821
  return;
1123
1822
  }
1124
- setActivePopoverKey(prevKey => {
1125
- if (open) {
1126
- return currentPopoverKey;
1127
- }
1128
- 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
1129
1853
  });
1130
- }, [selectSlots]);
1131
- handlersRef.current = {
1132
- clearLongPress,
1133
- finishSelection,
1134
- onFreeSlotClick,
1135
- resetGestureState,
1136
- selectSlots,
1137
- showFormPopoverForSlots,
1138
- startSlotSelection,
1139
- 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
+ }
1140
1898
  };
1899
+ react.useEffect(() => clearTimer, []);
1141
1900
  react.useEffect(() => {
1142
- const container = slotsRef.current;
1143
- if (!container) {
1144
- return undefined;
1901
+ if (interactingRef.current || draftMs == null) {
1902
+ return;
1145
1903
  }
1146
- const releasePointerCapture = pointerId => {
1147
- if (pointerId == null || !container.hasPointerCapture?.(pointerId)) {
1148
- 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);
1149
1922
  }
1150
- try {
1151
- container.releasePointerCapture(pointerId);
1152
- } catch {
1153
- // Ignore capture release errors on stale pointers.
1923
+ if (onChangeRef.current) {
1924
+ onChangeRef.current(nextValue);
1154
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
1155
1958
  };
1156
- const clearPendingTouch = () => {
1157
- handlersRef.current.clearLongPress();
1158
- 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
1159
1976
  };
1160
- const isStillOnSameSlot = (touchState, clientX, clientY) => {
1161
- const currentIndex = getSlotIndexFromPoint(container, clientX, clientY);
1162
- 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: []
1163
2040
  };
1164
- const shouldCancelPendingGesture = (touchState, clientX, clientY) => {
1165
- const moveX = Math.abs(clientX - touchState.x);
1166
- const moveY = Math.abs(clientY - touchState.y);
1167
- if (moveY > SCROLL_CANCEL_THRESHOLD) {
1168
- return true;
1169
- }
1170
- if (!isStillOnSameSlot(touchState, clientX, clientY)) {
1171
- return true;
1172
- }
1173
- if (!allowMultiSelectRef.current && (moveX > TAP_MOVE_THRESHOLD || moveY > TAP_MOVE_THRESHOLD)) {
1174
- 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);
1175
2052
  }
1176
- 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
1177
2069
  };
1178
- const activateLongPress = touchState => {
1179
- if (!isStillOnSameSlot(touchState, touchState.lastX, touchState.lastY)) {
1180
- 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();
1181
2088
  }
1182
- touchState.longPressed = true;
1183
- touchState.active = true;
1184
- clearDocumentTextSelection();
1185
- try {
1186
- container.setPointerCapture(touchState.pointerId);
1187
- } catch {
1188
- // 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());
1189
2105
  }
1190
- slotsRef.current?.classList.add('schedule-calendar-touch-dragging');
1191
- handlersRef.current.startSlotSelection(touchState.slot, touchState.index, {
1192
- showPopover: false,
1193
- 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
1194
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
1195
2140
  };
1196
- const handlePointerDown = event => {
1197
- if (event.pointerType === 'mouse' || event.isPrimary === false) {
1198
- return;
1199
- }
1200
- const slotElement = event.target?.closest?.('[data-slot-index]');
1201
- if (!slotElement || !container.contains(slotElement)) {
1202
- return;
1203
- }
1204
- const index = Number(slotElement.getAttribute('data-slot-index'));
1205
- const slot = slotsDataRef.current[index];
1206
- 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) {
1207
2165
  return;
1208
2166
  }
1209
- handlersRef.current.resetGestureState();
1210
- touchStateRef.current = {
1211
- pointerId: event.pointerId,
1212
- x: event.clientX,
1213
- y: event.clientY,
1214
- lastX: event.clientX,
1215
- lastY: event.clientY,
1216
- slot,
1217
- index,
1218
- moved: false,
1219
- longPressed: false,
1220
- active: false,
1221
- selectionTriggered: false
1222
- };
1223
- if (!allowMultiSelectRef.current) {
2167
+ const dominant = Math.abs(event.deltaX) > Math.abs(event.deltaY) ? event.deltaX : event.deltaY;
2168
+ if (!dominant) {
1224
2169
  return;
1225
2170
  }
1226
- longPressTimerRef.current = window.setTimeout(() => {
1227
- const touchState = touchStateRef.current;
1228
- if (!touchState || touchState.pointerId !== event.pointerId || touchState.moved) {
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());
2202
+ }
2203
+ }, WHEEL_IDLE_MS);
2204
+ if (discrete && inertiaRef.current) {
2205
+ if (!steps) {
1229
2206
  return;
1230
2207
  }
1231
- activateLongPress(touchState);
1232
- longPressTimerRef.current = null;
1233
- }, LONG_PRESS_DELAY);
1234
- };
1235
- const handlePointerMove = event => {
1236
- const touchState = touchStateRef.current;
1237
- if (!touchState || event.pointerId !== touchState.pointerId) {
1238
- return;
1239
- }
1240
- touchState.lastX = event.clientX;
1241
- touchState.lastY = event.clientY;
1242
- if (!touchState.active) {
1243
- if (shouldCancelPendingGesture(touchState, event.clientX, event.clientY)) {
1244
- touchState.moved = true;
1245
- clearPendingTouch();
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);
1246
2213
  }
1247
- return;
1248
- }
1249
- event.preventDefault();
1250
- event.stopPropagation();
1251
- clearDocumentTextSelection();
1252
- const index = getSlotIndexFromPoint(container, event.clientX, event.clientY);
1253
- if (index !== null) {
1254
- handlersRef.current.updateSelectionRange(index);
1255
- }
1256
- };
1257
- const handlePointerUp = event => {
1258
- const touchState = touchStateRef.current;
1259
- if (!touchState || event.pointerId !== touchState.pointerId) {
1260
- return;
1261
- }
1262
- releasePointerCapture(event.pointerId);
1263
- suppressMouseUntilRef.current = Date.now() + 700;
1264
- if (touchState.active) {
1265
- event.preventDefault();
1266
- touchState.selectionTriggered = true;
1267
- const index = getSlotIndexFromPoint(container, event.clientX, event.clientY);
1268
- if (index !== null) {
1269
- handlersRef.current.updateSelectionRange(index);
1270
- }
1271
- suppressTriggerUntilRef.current = Date.now() + 500;
1272
- handlersRef.current.finishSelection({
1273
- 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
1274
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);
1275
2234
  return;
1276
2235
  }
1277
- handlersRef.current.clearLongPress();
1278
- 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);
1279
- const {
1280
- slot
1281
- } = touchState;
1282
- touchStateRef.current = null;
1283
- if (quickTap) {
1284
- touchState.selectionTriggered = true;
1285
- suppressTriggerUntilRef.current = Date.now() + 500;
1286
- handlersRef.current.selectSlots([slot]);
1287
- handlersRef.current.showFormPopoverForSlots([slot]);
1288
- handlersRef.current.onFreeSlotClick?.(slot);
1289
- return;
1290
- }
1291
- handlersRef.current.resetGestureState();
1292
- };
1293
- const handleContextMenu = event => {
1294
- if (touchStateRef.current?.active || draggingRef.current) {
1295
- event.preventDefault();
1296
- }
1297
- };
1298
- const handleSelectStart = event => {
1299
- if (!touchStateRef.current?.active && !draggingRef.current) {
1300
- return;
1301
- }
1302
- event.preventDefault();
1303
- };
1304
- const handleTouchMove = event => {
1305
- if (!draggingRef.current && !touchStateRef.current?.active) {
1306
- return;
2236
+ if (steps) {
2237
+ moveToRef.current(current.start.valueOf() + steps * stepMs);
1307
2238
  }
1308
- event.preventDefault();
2239
+ const residualMs = pxPerStep > 0 ? accumulatedPx / pxPerStep * stepMs : 0;
2240
+ setVisualTargetRef.current(baseWindowRef.current.start.valueOf() + residualMs);
1309
2241
  };
1310
- const touchOptions = {
1311
- passive: false,
1312
- capture: true
1313
- };
1314
- container.addEventListener('pointerdown', handlePointerDown, {
1315
- passive: true
1316
- });
1317
- container.addEventListener('pointermove', handlePointerMove, {
1318
- passive: false
1319
- });
1320
- container.addEventListener('pointerup', handlePointerUp, {
1321
- passive: false
1322
- });
1323
- container.addEventListener('pointercancel', handlePointerUp, {
2242
+ node.addEventListener('wheel', onWheel, {
1324
2243
  passive: false
1325
2244
  });
1326
- container.addEventListener('touchmove', handleTouchMove, touchOptions);
1327
- container.addEventListener('contextmenu', handleContextMenu);
1328
- container.addEventListener('selectstart', handleSelectStart);
1329
- return () => {
1330
- releasePointerCapture(touchStateRef.current?.pointerId);
1331
- handlersRef.current.clearLongPress();
1332
- handlersRef.current.resetGestureState();
1333
- longPressTimerRef.current = null;
1334
- container.removeEventListener('touchmove', handleTouchMove, touchOptions);
1335
- };
2245
+ return () => node.removeEventListener('wheel', onWheel);
1336
2246
  }, []);
1337
- return {
1338
- activePopoverKey,
1339
- dragging,
1340
- formPopoverKey,
1341
- selectedAnchorKey,
1342
- selectedKeys,
1343
- setActivePopoverKey,
1344
- selectSlots,
1345
- slotsRef,
1346
- finishSelection,
1347
- handleMouseDown,
1348
- handleMouseEnter,
1349
- handleMouseUp,
1350
- handleMouseLeave,
1351
- 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
+ }
1352
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
+ });
1353
2379
  };
2380
+ const TimeWindow = withLocale(TimeWindowInner);
1354
2381
 
1355
2382
  const DefaultFormInner = () => {
1356
2383
  const {
@@ -1381,170 +2408,595 @@ const DefaultFormInner = () => {
1381
2408
  });
1382
2409
  };
1383
2410
 
1384
- 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"};
1385
2412
 
1386
- const SlotEvents = ({
1387
- slot,
2413
+ const EventDetail = ({
2414
+ event,
1388
2415
  date,
1389
2416
  onEventClick
1390
- }) => /*#__PURE__*/jsxRuntime.jsx("div", {
1391
- className: style$1['slot-popover-content'],
1392
- children: /*#__PURE__*/jsxRuntime.jsx(antd.List, {
1393
- size: "small",
1394
- dataSource: slot.events,
1395
- renderItem: event => /*#__PURE__*/jsxRuntime.jsx(EventItem, {
1396
- event: event,
1397
- date: date,
1398
- mode: "schedule",
1399
- onEventClick: onEventClick
1400
- })
1401
- })
1402
- });
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
+ };
1403
2471
 
1404
- const SlotItem = ({
1405
- slot,
2472
+ const DRAG_THRESHOLD_PX = 4;
2473
+ const TOUCH_SLOP_PX = 8;
2474
+ const LONG_PRESS_MS = 350;
2475
+ const Timeline = ({
1406
2476
  date,
1407
- selected,
2477
+ day,
2478
+ hourHeight,
2479
+ timeUnit,
2480
+ timeStep,
2481
+ eventItems,
2482
+ disabledRanges,
2483
+ blockers,
2484
+ conflicts,
2485
+ allowMultiSelect,
2486
+ selection,
2487
+ dragging,
1408
2488
  formPopover,
1409
2489
  popoverOpen,
2490
+ popoverPlacement,
2491
+ popoverAnchor,
1410
2492
  onPopoverOpenChange,
1411
- onContextMenu,
1412
- onMouseDown,
1413
- onPointerDown,
1414
- onMouseEnter,
1415
- onMouseUp,
1416
- onClick,
2493
+ onSelectionDrag,
2494
+ onSelectionCommit,
2495
+ onFreeSlotClick,
1417
2496
  onEventClick,
1418
- renderTimeSlot,
1419
- renderOccupiedSlot
2497
+ renderEventBlock,
2498
+ renderOccupiedSlot,
2499
+ interactionRef,
2500
+ isMobile
1420
2501
  }) => {
1421
- const {
1422
- formatMessage
1423
- } = reactIntl.useIntl();
1424
- const content = renderTimeSlot?.(slot, {
1425
- selected
1426
- });
1427
- if (content) {
1428
- const customNode = /*#__PURE__*/jsxRuntime.jsx("div", {
1429
- "data-slot-index": slot.index,
1430
- "data-slot-status": slot.status,
1431
- onContextMenu: onContextMenu,
1432
- onMouseDown: onMouseDown,
1433
- onPointerDown: onPointerDown,
1434
- onMouseEnter: onMouseEnter,
1435
- onMouseUp: onMouseUp,
1436
- onClick: onClick,
1437
- children: content
1438
- });
1439
- if (formPopover) {
1440
- return /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
1441
- rootClassName: style$1['schedule-popover'],
1442
- open: popoverOpen,
1443
- onOpenChange: onPopoverOpenChange,
1444
- content: formPopover,
1445
- trigger: "click",
1446
- placement: "bottom",
1447
- autoAdjustOverflow: true,
1448
- 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
1449
2532
  });
1450
2533
  }
1451
- return customNode;
1452
- }
1453
- const wrapWithPopover = node => {
1454
- if (formPopover) {
1455
- return /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
1456
- rootClassName: style$1['schedule-popover'],
1457
- open: popoverOpen,
1458
- onOpenChange: onPopoverOpenChange,
1459
- content: formPopover,
1460
- trigger: "click",
1461
- placement: "bottom",
1462
- autoAdjustOverflow: true,
1463
- 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'
1464
2756
  });
2757
+ onFreeClick?.({
2758
+ start: new Date(slotStart),
2759
+ end: new Date(Math.min(slotStart + unit, d.endMs))
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);
1465
2792
  }
1466
- if (slot.status === 'occupied') {
1467
- return /*#__PURE__*/jsxRuntime.jsx(antd.Popover, {
1468
- rootClassName: style$1['schedule-popover'],
1469
- open: popoverOpen,
1470
- onOpenChange: onPopoverOpenChange,
1471
- content: /*#__PURE__*/jsxRuntime.jsx(SlotEvents, {
1472
- slot: slot,
1473
- date: date,
1474
- onEventClick: onEventClick
1475
- }),
1476
- trigger: "click",
1477
- placement: "bottom",
1478
- autoAdjustOverflow: true,
1479
- 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)
1480
2829
  });
1481
2830
  }
1482
- if (slot.disabledReason) {
1483
- return /*#__PURE__*/jsxRuntime.jsx(antd.Tooltip, {
1484
- title: slot.disabledReason,
1485
- 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]
1486
2839
  });
1487
2840
  }
1488
- 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
+ });
1489
2850
  };
1490
- const slotClassName = classNames(style$1['slot-item'], style$1[`slot-${slot.status}`], {
1491
- [style$1['slot-selected']]: selected
1492
- });
1493
- const slotChildren = /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
1494
- children: [/*#__PURE__*/jsxRuntime.jsx("span", {
1495
- className: style$1['slot-dot']
1496
- }), /*#__PURE__*/jsxRuntime.jsx("span", {
1497
- className: style$1['slot-time'],
1498
- children: formatRange(slot.start, slot.end)
1499
- }), slot.status === 'disabled' ? /*#__PURE__*/jsxRuntime.jsx(antd.Tag, {
1500
- className: style$1['slot-tag'],
1501
- children: formatMessage({
1502
- id: 'SlotItem.disabled'
1503
- })
1504
- }) : null, slot.status === 'occupied' ? /*#__PURE__*/jsxRuntime.jsx(antd.Tag, {
1505
- className: style$1['slot-tag'],
1506
- color: "red",
1507
- children: formatMessage({
1508
- id: 'SlotItem.occupied'
1509
- })
1510
- }) : null, slot.status === 'occupied' && renderOccupiedSlot ? renderOccupiedSlot(slot, {
1511
- events: slot.events
1512
- }) : null, slot.status === 'occupied' && !renderOccupiedSlot ? /*#__PURE__*/jsxRuntime.jsx("span", {
1513
- className: style$1['slot-title'],
1514
- 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'])
1515
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
+ })]
1516
2999
  });
1517
- const slotHandlers = {
1518
- onContextMenu,
1519
- onMouseDown,
1520
- onPointerDown,
1521
- onMouseEnter,
1522
- onMouseUp,
1523
- onClick
1524
- };
1525
- const inner = slot.status === 'disabled' ? /*#__PURE__*/jsxRuntime.jsx("button", {
1526
- type: "button",
1527
- className: slotClassName,
1528
- "data-slot-index": slot.index,
1529
- "data-slot-status": slot.status,
1530
- disabled: true,
1531
- onContextMenu: onContextMenu,
1532
- onMouseDown: onMouseDown,
1533
- onPointerDown: onPointerDown,
1534
- onMouseEnter: onMouseEnter,
1535
- onMouseUp: onMouseUp,
1536
- onClick: onClick,
1537
- children: slotChildren
1538
- }) : /*#__PURE__*/jsxRuntime.jsx("div", {
1539
- role: "button",
1540
- tabIndex: 0,
1541
- className: slotClassName,
1542
- "data-slot-index": slot.index,
1543
- "data-slot-status": slot.status,
1544
- ...slotHandlers,
1545
- children: slotChildren
1546
- });
1547
- return wrapWithPopover(inner);
1548
3000
  };
1549
3001
 
1550
3002
  function _catch(body, recover) {
@@ -1558,79 +3010,158 @@ function _catch(body, recover) {
1558
3010
  }
1559
3011
  return result;
1560
3012
  }
1561
- const SchedulePanel = ({
1562
- date,
1563
- slots,
1564
- loading,
1565
- allowMultiSelect,
1566
- selectedSlots,
1567
- setSelectedSlots,
1568
- onSlotRangeChange,
1569
- onFreeSlotClick,
1570
- onCreate,
1571
- formInner,
1572
- formProps,
1573
- defaultFormValues,
1574
- renderTimeSlot,
1575
- renderOccupiedSlot,
1576
- renderPanelHeader,
1577
- onEventClick
1578
- }) => {
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;
1579
3037
  const {
1580
3038
  formatMessage
1581
3039
  } = reactIntl.useIntl();
1582
- 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]);
1583
3060
  const panelHeader = renderPanelHeader?.({
1584
3061
  date: date.toDate(),
1585
3062
  mode: 'schedule',
1586
3063
  events
1587
3064
  });
1588
- const {
1589
- activePopoverKey,
1590
- dragging,
1591
- formPopoverKey,
1592
- selectedAnchorKey,
1593
- selectedKeys,
1594
- selectSlots,
1595
- slotsRef,
1596
- handleMouseDown,
1597
- handleMouseEnter,
1598
- handleMouseUp,
1599
- handleMouseLeave,
1600
- handlePopoverOpenChange
1601
- } = useScheduleSelectionGesture({
1602
- slots,
1603
- selectedSlots,
1604
- setSelectedSlots,
1605
- allowMultiSelect,
1606
- onSlotRangeChange,
1607
- onFreeSlotClick
1608
- });
1609
3065
  const {
1610
3066
  scrolled,
1611
3067
  onScroll
1612
- } = usePanelScrollShadow(date.format(DATE_FORMAT), slotsRef);
1613
- const formContext = {
3068
+ } = usePanelScrollShadow(date.format(DATE_FORMAT), scrollRef);
3069
+ const getFormContext = range => ({
1614
3070
  date: date.toDate(),
1615
- 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
+ }
1616
3097
  };
1617
- const formPopover = selectedSlots.length && !dragging ? /*#__PURE__*/jsxRuntime.jsxs("div", {
1618
- 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
+ }),
1619
3118
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
1620
3119
  className: style$1['form-popover-title'],
1621
3120
  children: formatMessage({
1622
- id: 'SchedulePanel.newSchedule'
1623
- }, {
1624
- range: formatRange(selectedSlots[0].start, selectedSlots[selectedSlots.length - 1].end)
3121
+ id: 'SchedulePanel.newScheduleTitle'
1625
3122
  })
1626
- }), /*#__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, {
1627
3155
  ...formProps,
3156
+ ...(isMobile ? {
3157
+ size: 'large'
3158
+ } : null),
1628
3159
  data: getDefaultFormValues(defaultFormValues, formContext),
1629
3160
  onSubmit: function (values) {
1630
3161
  try {
1631
3162
  const _temp = _catch(function () {
1632
- return Promise.resolve(onCreate?.(values, formContext)).then(function () {
1633
- selectSlots([]);
3163
+ return Promise.resolve(onCreate?.(values, getFormContext(selection))).then(function () {
3164
+ clearSelection();
1634
3165
  });
1635
3166
  }, function () {});
1636
3167
  return Promise.resolve(_temp && _temp.then ? _temp.then(function () {}) : void 0);
@@ -1641,7 +3172,7 @@ const SchedulePanel = ({
1641
3172
  children: [formInner || /*#__PURE__*/jsxRuntime.jsx(DefaultFormInner, {}), /*#__PURE__*/jsxRuntime.jsxs("div", {
1642
3173
  className: style$1['form-actions'],
1643
3174
  children: [/*#__PURE__*/jsxRuntime.jsx(antd.Button, {
1644
- onClick: () => selectSlots([]),
3175
+ onClick: clearSelection,
1645
3176
  children: formatMessage({
1646
3177
  id: 'SchedulePanel.cancel'
1647
3178
  })
@@ -1653,7 +3184,7 @@ const SchedulePanel = ({
1653
3184
  })]
1654
3185
  })]
1655
3186
  }) : null;
1656
- return /*#__PURE__*/jsxRuntime.jsx(antd.Card, {
3187
+ return /*#__PURE__*/jsxRuntime.jsxs(antd.Card, {
1657
3188
  bordered: false,
1658
3189
  title: panelHeader || /*#__PURE__*/jsxRuntime.jsx(PanelTitle, {
1659
3190
  date: date,
@@ -1662,69 +3193,93 @@ const SchedulePanel = ({
1662
3193
  })
1663
3194
  }),
1664
3195
  className: panelStyle['panel-card'],
1665
- children: /*#__PURE__*/jsxRuntime.jsx(antd.Spin, {
3196
+ children: [/*#__PURE__*/jsxRuntime.jsx(antd.Spin, {
1666
3197
  spinning: !!loading,
1667
3198
  classNames: {
1668
3199
  root: panelStyle['panel-spin'],
1669
3200
  container: panelStyle['panel-spin-container']
1670
3201
  },
1671
3202
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
1672
- ref: slotsRef,
1673
- className: classNames(style$1['slots'], {
1674
- [style$1['slots-dragging']]: dragging
1675
- }),
3203
+ ref: scrollRef,
3204
+ className: style$1['slots'],
1676
3205
  onScroll: onScroll,
1677
- onMouseUp: handleMouseUp,
1678
- onMouseLeave: handleMouseLeave,
1679
3206
  children: [/*#__PURE__*/jsxRuntime.jsx(PanelScrollShadow, {
1680
3207
  visible: scrolled
1681
- }), /*#__PURE__*/jsxRuntime.jsx("div", {
1682
- className: style$1['slots-content'],
1683
- children: slots.map((slot, index) => {
1684
- const slotWithIndex = {
1685
- ...slot,
1686
- index
1687
- };
1688
- const occupiedPopoverKey = `occupied:${slot.key}`;
1689
- const currentPopoverKey = slot.key === selectedAnchorKey && formPopover ? formPopoverKey : slot.status === 'occupied' ? occupiedPopoverKey : null;
1690
- return /*#__PURE__*/jsxRuntime.jsx(SlotItem, {
1691
- slot: slotWithIndex,
1692
- date: date.toDate(),
1693
- selected: selectedKeys.has(slot.key),
1694
- formPopover: slot.key === selectedAnchorKey ? formPopover : null,
1695
- popoverOpen: !!currentPopoverKey && activePopoverKey === currentPopoverKey,
1696
- onPopoverOpenChange: open => handlePopoverOpenChange(open, currentPopoverKey, {
1697
- isForm: currentPopoverKey === formPopoverKey
1698
- }),
1699
- renderTimeSlot: renderTimeSlot,
1700
- renderOccupiedSlot: renderOccupiedSlot,
1701
- onEventClick: onEventClick,
1702
- onContextMenu: event => {
1703
- if (slot.status === 'free') {
1704
- event.preventDefault();
1705
- }
1706
- },
1707
- onMouseDown: event => handleMouseDown(event, slot, index),
1708
- onMouseEnter: () => handleMouseEnter(index),
1709
- onMouseUp: handleMouseUp,
1710
- onClick: undefined
1711
- }, slot.key);
1712
- })
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
1713
3252
  })]
1714
3253
  })
1715
- })
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]
1716
3269
  });
1717
3270
  };
1718
3271
 
1719
- var style = {"schedule-calendar-shell":"kne-react-calendar_csu63","schedule-calendar":"kne-react-calendar_5p1TK","calendar-side":"kne-react-calendar_nd7DK","panel-side":"kne-react-calendar_rWvHE","schedule-calendar-stacked":"kne-react-calendar_zGXr5"};
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"};
1720
3273
 
1721
3274
  dayjs__default["default"].extend(customParseFormat__default["default"]);
1722
3275
  const buildMonthRuleCache = _ref => {
1723
- let currentDate = _ref.currentDate,
1724
- mode = _ref.mode,
1725
- disabledDate = _ref.disabledDate,
1726
- disabledDateTime = _ref.disabledDateTime,
1727
- availableDateTime = _ref.availableDateTime;
3276
+ let {
3277
+ currentDate,
3278
+ mode,
3279
+ disabledDate,
3280
+ disabledDateTime,
3281
+ availableDateTime
3282
+ } = _ref;
1728
3283
  const cache = new Map();
1729
3284
  const monthStart = currentDate.startOf('month');
1730
3285
  const monthEnd = currentDate.endOf('month');
@@ -1742,62 +3297,54 @@ const buildMonthRuleCache = _ref => {
1742
3297
  return cache;
1743
3298
  };
1744
3299
  const ScheduleCalendarInner = props => {
1745
- const _props$mode = props.mode,
1746
- mode = _props$mode === void 0 ? 'view' : _props$mode,
1747
- _props$events = props.events,
1748
- events = _props$events === void 0 ? [] : _props$events,
1749
- loading = props.loading,
1750
- disabledDate = props.disabledDate,
1751
- disabledDateTime = props.disabledDateTime,
1752
- availableDateTime = props.availableDateTime,
1753
- _props$badgeMaxCount = props.badgeMaxCount,
1754
- badgeMaxCount = _props$badgeMaxCount === void 0 ? 9 : _props$badgeMaxCount,
1755
- _props$timeUnit = props.timeUnit,
1756
- timeUnit = _props$timeUnit === void 0 ? 30 : _props$timeUnit,
1757
- _props$dayStart = props.dayStart,
1758
- dayStart = _props$dayStart === void 0 ? '09:00' : _props$dayStart,
1759
- _props$dayEnd = props.dayEnd,
1760
- dayEnd = _props$dayEnd === void 0 ? '18:00' : _props$dayEnd,
1761
- _props$allowMultiSele = props.allowMultiSelect,
1762
- allowMultiSelect = _props$allowMultiSele === void 0 ? mode === 'schedule' : _props$allowMultiSele,
1763
- renderDateCell = props.renderDateCell,
1764
- renderEventItem = props.renderEventItem,
1765
- renderTimeSlot = props.renderTimeSlot,
1766
- renderOccupiedSlot = props.renderOccupiedSlot,
1767
- renderPanelHeader = props.renderPanelHeader,
1768
- formInner = props.formInner,
1769
- formProps = props.formProps,
1770
- defaultFormValues = props.defaultFormValues,
1771
- onDateChange = props.onDateChange,
1772
- onEventClick = props.onEventClick,
1773
- onFreeSlotClick = props.onFreeSlotClick,
1774
- onSlotRangeChange = props.onSlotRangeChange,
1775
- onCreate = props.onCreate,
1776
- className = props.className,
1777
- propsStyle = props.style,
1778
- _props$stackAt = props.stackAt,
1779
- stackAt = _props$stackAt === void 0 ? LAYOUT_STACK_BREAKPOINT : _props$stackAt,
1780
- calendarProps = props.calendarProps,
1781
- 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;
1782
3335
  const rootRef = react.useRef(null);
1783
3336
  const calendarRef = react.useRef(null);
1784
3337
  const stacked = useStackedLayout(rootRef, stackAt);
1785
- const stackedLayoutStyle = useStackedPanelLayout(rootRef, calendarRef, stacked);
1786
- const _useControlValue = useControlValue__default["default"](props),
1787
- value = _useControlValue[0],
1788
- setValue = _useControlValue[1];
3338
+ const eventListExpanded = mode === 'view' && !eventListScrollable;
3339
+ const stackedLayoutStyle = useStackedPanelLayout(rootRef, calendarRef, stacked && !eventListExpanded);
3340
+ const [value, setValue] = useControlValue__default["default"](props);
1789
3341
  const selectedDate = react.useMemo(() => toDayjs(value) || dayjs__default["default"](), [value]);
1790
- const _useControlledState = useControlledState({
1791
- value: props.current,
1792
- defaultValue: props.defaultCurrent || selectedDate,
1793
- onChange: props.onCurrentChange
1794
- }),
1795
- current = _useControlledState[0],
1796
- setCurrent = _useControlledState[1];
3342
+ const [current, setCurrent] = useControlledState({
3343
+ value: props.current,
3344
+ defaultValue: props.defaultCurrent || selectedDate,
3345
+ onChange: props.onCurrentChange
3346
+ });
1797
3347
  const currentDate = react.useMemo(() => toDayjs(current) || selectedDate, [current, selectedDate]);
1798
- const _useState = react.useState([]),
1799
- selectedSlots = _useState[0],
1800
- setSelectedSlots = _useState[1];
1801
3348
  const eventGroup = react.useMemo(() => groupEventsByDate(events), [events]);
1802
3349
  const selectedEvents = react.useMemo(() => eventGroup[selectedDate.format(DATE_FORMAT)] || [], [eventGroup, selectedDate]);
1803
3350
  const ruleContext = react.useMemo(() => ({
@@ -1806,17 +3353,19 @@ const ScheduleCalendarInner = props => {
1806
3353
  disabledDateTime,
1807
3354
  availableDateTime
1808
3355
  }), [availableDateTime, disabledDate, disabledDateTime, mode]);
1809
- const monthRuleCache = react.useMemo(() => buildMonthRuleCache(_extends({
1810
- currentDate
1811
- }, ruleContext)), [currentDate, ruleContext]);
3356
+ const monthRuleCache = react.useMemo(() => buildMonthRuleCache({
3357
+ currentDate,
3358
+ ...ruleContext
3359
+ }), [currentDate, ruleContext]);
1812
3360
  const getDateRule = react.useCallback(date => {
1813
3361
  const cached = monthRuleCache.get(date.format(DATE_FORMAT));
1814
3362
  if (cached !== undefined) {
1815
3363
  return cached;
1816
3364
  }
1817
- return checkRule(_extends({
1818
- date
1819
- }, ruleContext));
3365
+ return checkRule({
3366
+ date,
3367
+ ...ruleContext
3368
+ });
1820
3369
  }, [monthRuleCache, ruleContext]);
1821
3370
  const slots = react.useMemo(() => generateTimeSlots({
1822
3371
  date: selectedDate,
@@ -1832,8 +3381,7 @@ const ScheduleCalendarInner = props => {
1832
3381
  const handleSelectDate = (date, info) => {
1833
3382
  const nextDate = date.startOf('day').toDate();
1834
3383
  setValue(nextDate, info);
1835
- onDateChange == null || onDateChange(nextDate, info);
1836
- setSelectedSlots([]);
3384
+ onDateChange?.(nextDate, info);
1837
3385
  };
1838
3386
  const handleCurrentChange = (date, info) => {
1839
3387
  setCurrent(date.startOf('month').toDate(), info);
@@ -1869,13 +3417,14 @@ const ScheduleCalendarInner = props => {
1869
3417
  style: propsStyle,
1870
3418
  children: /*#__PURE__*/jsxRuntime.jsxs("div", {
1871
3419
  ref: rootRef,
1872
- className: classNames(style['schedule-calendar'], stacked && style['schedule-calendar-stacked'], className),
3420
+ className: classNames(style['schedule-calendar'], stacked && style['schedule-calendar-stacked'], eventListExpanded && style['schedule-calendar-event-list-expanded'], className),
1873
3421
  "data-schedule-layout": stacked ? 'stacked' : 'row',
1874
- style: stacked ? stackedLayoutStyle : undefined,
3422
+ style: stacked && !eventListExpanded ? stackedLayoutStyle : undefined,
1875
3423
  children: [/*#__PURE__*/jsxRuntime.jsx("div", {
1876
3424
  ref: calendarRef,
1877
3425
  className: style['calendar-side'],
1878
- children: /*#__PURE__*/jsxRuntime.jsx(antd.Calendar, _extends({}, calendarProps, {
3426
+ children: /*#__PURE__*/jsxRuntime.jsx(antd.Calendar, {
3427
+ ...calendarProps,
1879
3428
  fullscreen: false,
1880
3429
  value: currentDate,
1881
3430
  headerRender: () => {
@@ -1901,36 +3450,42 @@ const ScheduleCalendarInner = props => {
1901
3450
  onPanelChange: date => handleCurrentChange(date, {
1902
3451
  source: 'picker'
1903
3452
  })
1904
- }))
1905
- }), /*#__PURE__*/jsxRuntime.jsx("div", _extends({
1906
- className: style['panel-side']
1907
- }, panelProps, {
3453
+ })
3454
+ }), /*#__PURE__*/jsxRuntime.jsx("div", {
3455
+ className: style['panel-side'],
3456
+ ...panelProps,
1908
3457
  children: mode === 'schedule' ? /*#__PURE__*/jsxRuntime.jsx(SchedulePanel, {
1909
3458
  date: selectedDate,
1910
3459
  slots: slots,
3460
+ events: selectedEvents,
3461
+ dayStart: dayStart,
3462
+ dayEnd: dayEnd,
3463
+ timeUnit: timeUnit,
3464
+ timeStep: timeStep,
3465
+ hourHeight: hourHeight,
3466
+ allowConflict: allowConflict,
1911
3467
  loading: loading,
1912
3468
  allowMultiSelect: allowMultiSelect,
1913
- selectedSlots: selectedSlots,
1914
- setSelectedSlots: setSelectedSlots,
1915
3469
  onSlotRangeChange: onSlotRangeChange,
1916
3470
  onFreeSlotClick: onFreeSlotClick,
1917
3471
  onCreate: onCreate,
1918
3472
  formInner: formInner,
1919
3473
  formProps: formProps,
1920
3474
  defaultFormValues: defaultFormValues,
1921
- renderTimeSlot: renderTimeSlot,
3475
+ renderEventBlock: renderEventBlock,
1922
3476
  renderOccupiedSlot: renderOccupiedSlot,
1923
3477
  renderPanelHeader: renderPanelHeader,
1924
3478
  onEventClick: onEventClick
1925
- }) : /*#__PURE__*/jsxRuntime.jsx(ViewPanel, {
3479
+ }, selectedDate.format(DATE_FORMAT)) : /*#__PURE__*/jsxRuntime.jsx(ViewPanel, {
1926
3480
  date: selectedDate,
1927
3481
  events: selectedEvents,
1928
3482
  loading: loading,
1929
3483
  renderEventItem: renderEventItem,
1930
3484
  renderPanelHeader: renderPanelHeader,
1931
- onEventClick: onEventClick
3485
+ onEventClick: onEventClick,
3486
+ scrollable: eventListScrollable
1932
3487
  })
1933
- }))]
3488
+ })]
1934
3489
  })
1935
3490
  });
1936
3491
  };
@@ -1938,6 +3493,7 @@ const ScheduleCalendar = withLocale(ScheduleCalendarInner);
1938
3493
 
1939
3494
  exports.ReactCalendar = ScheduleCalendar;
1940
3495
  exports.ScheduleCalendar = ScheduleCalendar;
3496
+ exports.TimeWindow = TimeWindow;
1941
3497
  exports.dedupeEventsById = dedupeEventsById;
1942
3498
  exports["default"] = ScheduleCalendar;
1943
3499
  exports.generateTimeSlots = generateTimeSlots;