@kne/react-calendar 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +14 -2
- package/dist/index.css.map +1 -1
- package/dist/index.js +291 -104
- package/dist/index.js.map +1 -1
- package/dist/index.modern.js +297 -110
- package/dist/index.modern.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -909,18 +909,44 @@ const ViewPanel = ({
|
|
|
909
909
|
});
|
|
910
910
|
};
|
|
911
911
|
|
|
912
|
-
const LONG_PRESS_DELAY =
|
|
913
|
-
const
|
|
912
|
+
const LONG_PRESS_DELAY = 400;
|
|
913
|
+
const SCROLL_CANCEL_THRESHOLD = 12;
|
|
914
|
+
const TAP_MOVE_THRESHOLD = 8;
|
|
914
915
|
const getPopoverKey = slot => slot ? `form:${slot.key}` : null;
|
|
915
|
-
const
|
|
916
|
-
const
|
|
917
|
-
|
|
918
|
-
if (!slotElement || !container?.contains(slotElement)) {
|
|
916
|
+
const getSlotIndexFromPoint = (container, clientX, clientY) => {
|
|
917
|
+
const slotElements = container?.querySelectorAll?.('[data-slot-index]');
|
|
918
|
+
if (!slotElements?.length) {
|
|
919
919
|
return null;
|
|
920
920
|
}
|
|
921
|
-
|
|
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;
|
|
928
|
+
}
|
|
929
|
+
if (clientY >= rect.top && clientY <= rect.bottom && clientX >= rect.left && clientX <= rect.right) {
|
|
930
|
+
return index;
|
|
931
|
+
}
|
|
932
|
+
if (clientX < rect.left || clientX > rect.right) {
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
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;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return closestIndex;
|
|
943
|
+
};
|
|
944
|
+
const clearDocumentTextSelection = () => {
|
|
945
|
+
const selection = document.getSelection?.();
|
|
946
|
+
if (selection && !selection.isCollapsed) {
|
|
947
|
+
selection.removeAllRanges();
|
|
948
|
+
}
|
|
922
949
|
};
|
|
923
|
-
const getTouchPoint = event => event.changedTouches?.[0] || event.touches?.[0];
|
|
924
950
|
const useScheduleSelectionGesture = ({
|
|
925
951
|
slots,
|
|
926
952
|
selectedSlots,
|
|
@@ -938,9 +964,14 @@ const useScheduleSelectionGesture = ({
|
|
|
938
964
|
const touchStateRef = react.useRef(null);
|
|
939
965
|
const slotsDataRef = react.useRef(slots);
|
|
940
966
|
const selectedSlotsRef = react.useRef(selectedSlots);
|
|
967
|
+
const allowMultiSelectRef = react.useRef(allowMultiSelect);
|
|
941
968
|
const suppressTriggerUntilRef = react.useRef(0);
|
|
969
|
+
const suppressMouseUntilRef = react.useRef(0);
|
|
970
|
+
const scrollLockRef = react.useRef(null);
|
|
971
|
+
const handlersRef = react.useRef({});
|
|
942
972
|
slotsDataRef.current = slots;
|
|
943
973
|
selectedSlotsRef.current = selectedSlots;
|
|
974
|
+
allowMultiSelectRef.current = allowMultiSelect;
|
|
944
975
|
const selectedKeys = react.useMemo(() => new Set(selectedSlots.map(slot => slot.key)), [selectedSlots]);
|
|
945
976
|
const selectedAnchorKey = selectedSlots[selectedSlots.length - 1]?.key;
|
|
946
977
|
const formPopoverKey = selectedAnchorKey ? `form:${selectedAnchorKey}` : null;
|
|
@@ -950,16 +981,55 @@ const useScheduleSelectionGesture = ({
|
|
|
950
981
|
longPressTimerRef.current = null;
|
|
951
982
|
}
|
|
952
983
|
}, []);
|
|
984
|
+
const unlockContainerScroll = react.useCallback(() => {
|
|
985
|
+
const container = slotsRef.current;
|
|
986
|
+
if (!container || scrollLockRef.current == null) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
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;
|
|
999
|
+
}
|
|
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.
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
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]);
|
|
953
1022
|
const selectSlots = react.useCallback(nextSlots => {
|
|
954
1023
|
selectedSlotsRef.current = nextSlots;
|
|
955
1024
|
if (!nextSlots.length) {
|
|
956
1025
|
setActivePopoverKey(null);
|
|
1026
|
+
resetGestureState();
|
|
957
1027
|
}
|
|
958
1028
|
setSelectedSlots(nextSlots);
|
|
959
1029
|
onSlotRangeChange?.(nextSlots, {
|
|
960
1030
|
action: nextSlots.length ? 'select' : 'clear'
|
|
961
1031
|
});
|
|
962
|
-
}, [onSlotRangeChange, setSelectedSlots]);
|
|
1032
|
+
}, [onSlotRangeChange, resetGestureState, setSelectedSlots]);
|
|
963
1033
|
const getSelectableRange = react.useCallback((startIndex, endIndex) => {
|
|
964
1034
|
const [start, end] = [Math.min(startIndex, endIndex), Math.max(startIndex, endIndex)];
|
|
965
1035
|
return slotsDataRef.current.slice(start, end + 1).filter(slot => slot.status === 'free');
|
|
@@ -971,12 +1041,17 @@ const useScheduleSelectionGesture = ({
|
|
|
971
1041
|
}
|
|
972
1042
|
}, []);
|
|
973
1043
|
const startSlotSelection = react.useCallback((slot, index, {
|
|
974
|
-
showPopover = true
|
|
1044
|
+
showPopover = true,
|
|
1045
|
+
lockScroll = false
|
|
975
1046
|
} = {}) => {
|
|
1047
|
+
clearDocumentTextSelection();
|
|
976
1048
|
setActivePopoverKey(getPopoverKey(slot));
|
|
977
1049
|
draggingRef.current = true;
|
|
978
1050
|
dragStartIndexRef.current = index;
|
|
979
1051
|
slotsRef.current?.classList.add('schedule-calendar-touch-dragging');
|
|
1052
|
+
if (lockScroll) {
|
|
1053
|
+
lockContainerScroll();
|
|
1054
|
+
}
|
|
980
1055
|
setDragging(true);
|
|
981
1056
|
selectSlots([slot]);
|
|
982
1057
|
if (!showPopover) {
|
|
@@ -984,43 +1059,58 @@ const useScheduleSelectionGesture = ({
|
|
|
984
1059
|
setActivePopoverKey(null);
|
|
985
1060
|
}
|
|
986
1061
|
onFreeSlotClick?.(slot);
|
|
987
|
-
}, [onFreeSlotClick, selectSlots]);
|
|
1062
|
+
}, [lockContainerScroll, onFreeSlotClick, selectSlots]);
|
|
988
1063
|
const finishSelection = react.useCallback(({
|
|
989
1064
|
fromTouch = false
|
|
990
1065
|
} = {}) => {
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
slotsRef.current?.classList.remove('schedule-calendar-touch-dragging');
|
|
995
|
-
setDragging(false);
|
|
996
|
-
if (selectedSlotsRef.current.length) {
|
|
1066
|
+
const hadSelection = selectedSlotsRef.current.length > 0;
|
|
1067
|
+
resetGestureState();
|
|
1068
|
+
if (hadSelection) {
|
|
997
1069
|
if (fromTouch) {
|
|
998
1070
|
suppressTriggerUntilRef.current = Date.now() + 500;
|
|
999
1071
|
}
|
|
1000
1072
|
showFormPopoverForSlots(selectedSlotsRef.current);
|
|
1001
1073
|
}
|
|
1002
|
-
}, [
|
|
1074
|
+
}, [resetGestureState, showFormPopoverForSlots]);
|
|
1003
1075
|
const updateSelectionRange = react.useCallback(index => {
|
|
1004
1076
|
const startIndex = dragStartIndexRef.current;
|
|
1005
|
-
if (startIndex === null || !
|
|
1077
|
+
if (startIndex === null || !allowMultiSelectRef.current) {
|
|
1006
1078
|
return;
|
|
1007
1079
|
}
|
|
1008
1080
|
const nextSlots = getSelectableRange(startIndex, index);
|
|
1009
1081
|
selectSlots(nextSlots);
|
|
1010
|
-
}, [
|
|
1082
|
+
}, [getSelectableRange, selectSlots]);
|
|
1083
|
+
const shouldIgnoreMouseSelectionEvent = react.useCallback(() => {
|
|
1084
|
+
return !!touchStateRef.current || Date.now() < suppressMouseUntilRef.current;
|
|
1085
|
+
}, []);
|
|
1011
1086
|
const handleMouseDown = react.useCallback((event, slot, index) => {
|
|
1087
|
+
if (shouldIgnoreMouseSelectionEvent()) {
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1012
1090
|
if (slot.status !== 'free') {
|
|
1013
1091
|
return;
|
|
1014
1092
|
}
|
|
1015
1093
|
event.preventDefault();
|
|
1016
1094
|
startSlotSelection(slot, index);
|
|
1017
|
-
}, [startSlotSelection]);
|
|
1095
|
+
}, [shouldIgnoreMouseSelectionEvent, startSlotSelection]);
|
|
1018
1096
|
const handleMouseEnter = react.useCallback(index => {
|
|
1019
|
-
if (!draggingRef.current) {
|
|
1097
|
+
if (shouldIgnoreMouseSelectionEvent() || !draggingRef.current) {
|
|
1020
1098
|
return;
|
|
1021
1099
|
}
|
|
1022
1100
|
updateSelectionRange(index);
|
|
1023
|
-
}, [updateSelectionRange]);
|
|
1101
|
+
}, [shouldIgnoreMouseSelectionEvent, updateSelectionRange]);
|
|
1102
|
+
const handleMouseUp = react.useCallback(() => {
|
|
1103
|
+
if (shouldIgnoreMouseSelectionEvent()) {
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
finishSelection();
|
|
1107
|
+
}, [finishSelection, shouldIgnoreMouseSelectionEvent]);
|
|
1108
|
+
const handleMouseLeave = react.useCallback(() => {
|
|
1109
|
+
if (shouldIgnoreMouseSelectionEvent()) {
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
finishSelection();
|
|
1113
|
+
}, [finishSelection, shouldIgnoreMouseSelectionEvent]);
|
|
1024
1114
|
const handlePopoverOpenChange = react.useCallback((open, currentPopoverKey, {
|
|
1025
1115
|
isForm = false
|
|
1026
1116
|
} = {}) => {
|
|
@@ -1038,25 +1128,77 @@ const useScheduleSelectionGesture = ({
|
|
|
1038
1128
|
return prevKey === currentPopoverKey ? null : prevKey;
|
|
1039
1129
|
});
|
|
1040
1130
|
}, [selectSlots]);
|
|
1131
|
+
handlersRef.current = {
|
|
1132
|
+
clearLongPress,
|
|
1133
|
+
finishSelection,
|
|
1134
|
+
onFreeSlotClick,
|
|
1135
|
+
resetGestureState,
|
|
1136
|
+
selectSlots,
|
|
1137
|
+
showFormPopoverForSlots,
|
|
1138
|
+
startSlotSelection,
|
|
1139
|
+
updateSelectionRange
|
|
1140
|
+
};
|
|
1041
1141
|
react.useEffect(() => {
|
|
1042
1142
|
const container = slotsRef.current;
|
|
1043
1143
|
if (!container) {
|
|
1044
1144
|
return undefined;
|
|
1045
1145
|
}
|
|
1046
|
-
const
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1146
|
+
const releasePointerCapture = pointerId => {
|
|
1147
|
+
if (pointerId == null || !container.hasPointerCapture?.(pointerId)) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
try {
|
|
1151
|
+
container.releasePointerCapture(pointerId);
|
|
1152
|
+
} catch {
|
|
1153
|
+
// Ignore capture release errors on stale pointers.
|
|
1050
1154
|
}
|
|
1155
|
+
};
|
|
1156
|
+
const clearPendingTouch = () => {
|
|
1157
|
+
handlersRef.current.clearLongPress();
|
|
1051
1158
|
touchStateRef.current = null;
|
|
1052
1159
|
};
|
|
1053
|
-
const
|
|
1054
|
-
|
|
1160
|
+
const isStillOnSameSlot = (touchState, clientX, clientY) => {
|
|
1161
|
+
const currentIndex = getSlotIndexFromPoint(container, clientX, clientY);
|
|
1162
|
+
return currentIndex === touchState.index;
|
|
1163
|
+
};
|
|
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;
|
|
1175
|
+
}
|
|
1176
|
+
return false;
|
|
1177
|
+
};
|
|
1178
|
+
const activateLongPress = touchState => {
|
|
1179
|
+
if (!isStillOnSameSlot(touchState, touchState.lastX, touchState.lastY)) {
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
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.
|
|
1189
|
+
}
|
|
1190
|
+
slotsRef.current?.classList.add('schedule-calendar-touch-dragging');
|
|
1191
|
+
handlersRef.current.startSlotSelection(touchState.slot, touchState.index, {
|
|
1192
|
+
showPopover: false,
|
|
1193
|
+
lockScroll: true
|
|
1194
|
+
});
|
|
1195
|
+
};
|
|
1196
|
+
const handlePointerDown = event => {
|
|
1197
|
+
if (event.pointerType === 'mouse' || event.isPrimary === false) {
|
|
1055
1198
|
return;
|
|
1056
1199
|
}
|
|
1057
|
-
const
|
|
1058
|
-
|
|
1059
|
-
if (!slotElement) {
|
|
1200
|
+
const slotElement = event.target?.closest?.('[data-slot-index]');
|
|
1201
|
+
if (!slotElement || !container.contains(slotElement)) {
|
|
1060
1202
|
return;
|
|
1061
1203
|
}
|
|
1062
1204
|
const index = Number(slotElement.getAttribute('data-slot-index'));
|
|
@@ -1064,114 +1206,134 @@ const useScheduleSelectionGesture = ({
|
|
|
1064
1206
|
if (Number.isNaN(index) || slot?.status !== 'free') {
|
|
1065
1207
|
return;
|
|
1066
1208
|
}
|
|
1067
|
-
|
|
1209
|
+
handlersRef.current.resetGestureState();
|
|
1068
1210
|
touchStateRef.current = {
|
|
1069
|
-
|
|
1070
|
-
x:
|
|
1071
|
-
y:
|
|
1211
|
+
pointerId: event.pointerId,
|
|
1212
|
+
x: event.clientX,
|
|
1213
|
+
y: event.clientY,
|
|
1214
|
+
lastX: event.clientX,
|
|
1215
|
+
lastY: event.clientY,
|
|
1072
1216
|
slot,
|
|
1073
1217
|
index,
|
|
1074
1218
|
moved: false,
|
|
1075
1219
|
longPressed: false,
|
|
1076
|
-
active: false
|
|
1220
|
+
active: false,
|
|
1221
|
+
selectionTriggered: false
|
|
1077
1222
|
};
|
|
1223
|
+
if (!allowMultiSelectRef.current) {
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1078
1226
|
longPressTimerRef.current = window.setTimeout(() => {
|
|
1079
1227
|
const touchState = touchStateRef.current;
|
|
1080
|
-
if (!touchState) {
|
|
1228
|
+
if (!touchState || touchState.pointerId !== event.pointerId || touchState.moved) {
|
|
1081
1229
|
return;
|
|
1082
1230
|
}
|
|
1083
|
-
touchState
|
|
1084
|
-
touchState.active = true;
|
|
1085
|
-
slotsRef.current?.classList.add('schedule-calendar-touch-dragging');
|
|
1086
|
-
startSlotSelection(slot, index, {
|
|
1087
|
-
showPopover: false
|
|
1088
|
-
});
|
|
1231
|
+
activateLongPress(touchState);
|
|
1089
1232
|
longPressTimerRef.current = null;
|
|
1090
1233
|
}, LONG_PRESS_DELAY);
|
|
1091
1234
|
};
|
|
1092
|
-
const
|
|
1235
|
+
const handlePointerMove = event => {
|
|
1093
1236
|
const touchState = touchStateRef.current;
|
|
1094
|
-
if (!touchState) {
|
|
1095
|
-
return;
|
|
1096
|
-
}
|
|
1097
|
-
const touch = Array.from(event.touches).find(item => item.identifier === touchState.identifier) || event.touches[0];
|
|
1098
|
-
if (!touch) {
|
|
1237
|
+
if (!touchState || event.pointerId !== touchState.pointerId) {
|
|
1099
1238
|
return;
|
|
1100
1239
|
}
|
|
1240
|
+
touchState.lastX = event.clientX;
|
|
1241
|
+
touchState.lastY = event.clientY;
|
|
1101
1242
|
if (!touchState.active) {
|
|
1102
|
-
|
|
1103
|
-
const moveY = Math.abs(touch.clientY - touchState.y);
|
|
1104
|
-
if (moveX > MOVE_THRESHOLD || moveY > MOVE_THRESHOLD) {
|
|
1243
|
+
if (shouldCancelPendingGesture(touchState, event.clientX, event.clientY)) {
|
|
1105
1244
|
touchState.moved = true;
|
|
1106
|
-
|
|
1245
|
+
clearPendingTouch();
|
|
1107
1246
|
}
|
|
1108
1247
|
return;
|
|
1109
1248
|
}
|
|
1110
1249
|
event.preventDefault();
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
if (!Number.isNaN(index)) {
|
|
1117
|
-
updateSelectionRange(index);
|
|
1250
|
+
event.stopPropagation();
|
|
1251
|
+
clearDocumentTextSelection();
|
|
1252
|
+
const index = getSlotIndexFromPoint(container, event.clientX, event.clientY);
|
|
1253
|
+
if (index !== null) {
|
|
1254
|
+
handlersRef.current.updateSelectionRange(index);
|
|
1118
1255
|
}
|
|
1119
1256
|
};
|
|
1120
|
-
const
|
|
1257
|
+
const handlePointerUp = event => {
|
|
1121
1258
|
const touchState = touchStateRef.current;
|
|
1122
|
-
if (!touchState) {
|
|
1123
|
-
return;
|
|
1124
|
-
}
|
|
1125
|
-
const touch = Array.from(event.changedTouches || []).find(item => item.identifier === touchState.identifier) || getTouchPoint(event);
|
|
1126
|
-
if (!touch) {
|
|
1127
|
-
clearTouchState();
|
|
1259
|
+
if (!touchState || event.pointerId !== touchState.pointerId) {
|
|
1128
1260
|
return;
|
|
1129
1261
|
}
|
|
1262
|
+
releasePointerCapture(event.pointerId);
|
|
1263
|
+
suppressMouseUntilRef.current = Date.now() + 700;
|
|
1130
1264
|
if (touchState.active) {
|
|
1131
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
|
+
}
|
|
1132
1271
|
suppressTriggerUntilRef.current = Date.now() + 500;
|
|
1133
|
-
finishSelection({
|
|
1272
|
+
handlersRef.current.finishSelection({
|
|
1134
1273
|
fromTouch: true
|
|
1135
1274
|
});
|
|
1136
|
-
touchStateRef.current = null;
|
|
1137
1275
|
return;
|
|
1138
1276
|
}
|
|
1139
|
-
|
|
1140
|
-
|
|
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;
|
|
1141
1283
|
if (quickTap) {
|
|
1284
|
+
touchState.selectionTriggered = true;
|
|
1142
1285
|
suppressTriggerUntilRef.current = Date.now() + 500;
|
|
1143
|
-
selectSlots([
|
|
1144
|
-
showFormPopoverForSlots([
|
|
1145
|
-
onFreeSlotClick?.(
|
|
1286
|
+
handlersRef.current.selectSlots([slot]);
|
|
1287
|
+
handlersRef.current.showFormPopoverForSlots([slot]);
|
|
1288
|
+
handlersRef.current.onFreeSlotClick?.(slot);
|
|
1289
|
+
return;
|
|
1146
1290
|
}
|
|
1291
|
+
handlersRef.current.resetGestureState();
|
|
1147
1292
|
};
|
|
1148
1293
|
const handleContextMenu = event => {
|
|
1149
|
-
if (touchStateRef.current || draggingRef.current) {
|
|
1294
|
+
if (touchStateRef.current?.active || draggingRef.current) {
|
|
1150
1295
|
event.preventDefault();
|
|
1151
1296
|
}
|
|
1152
1297
|
};
|
|
1153
|
-
|
|
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;
|
|
1307
|
+
}
|
|
1308
|
+
event.preventDefault();
|
|
1309
|
+
};
|
|
1310
|
+
const touchOptions = {
|
|
1311
|
+
passive: false,
|
|
1312
|
+
capture: true
|
|
1313
|
+
};
|
|
1314
|
+
container.addEventListener('pointerdown', handlePointerDown, {
|
|
1154
1315
|
passive: true
|
|
1155
1316
|
});
|
|
1156
|
-
container.addEventListener('
|
|
1317
|
+
container.addEventListener('pointermove', handlePointerMove, {
|
|
1157
1318
|
passive: false
|
|
1158
1319
|
});
|
|
1159
|
-
container.addEventListener('
|
|
1320
|
+
container.addEventListener('pointerup', handlePointerUp, {
|
|
1160
1321
|
passive: false
|
|
1161
1322
|
});
|
|
1162
|
-
container.addEventListener('
|
|
1323
|
+
container.addEventListener('pointercancel', handlePointerUp, {
|
|
1163
1324
|
passive: false
|
|
1164
1325
|
});
|
|
1326
|
+
container.addEventListener('touchmove', handleTouchMove, touchOptions);
|
|
1165
1327
|
container.addEventListener('contextmenu', handleContextMenu);
|
|
1328
|
+
container.addEventListener('selectstart', handleSelectStart);
|
|
1166
1329
|
return () => {
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
container.removeEventListener('
|
|
1172
|
-
container.removeEventListener('contextmenu', handleContextMenu);
|
|
1330
|
+
releasePointerCapture(touchStateRef.current?.pointerId);
|
|
1331
|
+
handlersRef.current.clearLongPress();
|
|
1332
|
+
handlersRef.current.resetGestureState();
|
|
1333
|
+
longPressTimerRef.current = null;
|
|
1334
|
+
container.removeEventListener('touchmove', handleTouchMove, touchOptions);
|
|
1173
1335
|
};
|
|
1174
|
-
}, [
|
|
1336
|
+
}, []);
|
|
1175
1337
|
return {
|
|
1176
1338
|
activePopoverKey,
|
|
1177
1339
|
dragging,
|
|
@@ -1184,6 +1346,8 @@ const useScheduleSelectionGesture = ({
|
|
|
1184
1346
|
finishSelection,
|
|
1185
1347
|
handleMouseDown,
|
|
1186
1348
|
handleMouseEnter,
|
|
1349
|
+
handleMouseUp,
|
|
1350
|
+
handleMouseLeave,
|
|
1187
1351
|
handlePopoverOpenChange
|
|
1188
1352
|
};
|
|
1189
1353
|
};
|
|
@@ -1263,6 +1427,7 @@ const SlotItem = ({
|
|
|
1263
1427
|
if (content) {
|
|
1264
1428
|
const customNode = /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
1265
1429
|
"data-slot-index": slot.index,
|
|
1430
|
+
"data-slot-status": slot.status,
|
|
1266
1431
|
onContextMenu: onContextMenu,
|
|
1267
1432
|
onMouseDown: onMouseDown,
|
|
1268
1433
|
onPointerDown: onPointerDown,
|
|
@@ -1322,19 +1487,10 @@ const SlotItem = ({
|
|
|
1322
1487
|
}
|
|
1323
1488
|
return node;
|
|
1324
1489
|
};
|
|
1325
|
-
const
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
}),
|
|
1330
|
-
"data-slot-index": slot.index,
|
|
1331
|
-
disabled: slot.status === 'disabled',
|
|
1332
|
-
onContextMenu: onContextMenu,
|
|
1333
|
-
onMouseDown: onMouseDown,
|
|
1334
|
-
onPointerDown: onPointerDown,
|
|
1335
|
-
onMouseEnter: onMouseEnter,
|
|
1336
|
-
onMouseUp: onMouseUp,
|
|
1337
|
-
onClick: onClick,
|
|
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, {
|
|
1338
1494
|
children: [/*#__PURE__*/jsxRuntime.jsx("span", {
|
|
1339
1495
|
className: style$1['slot-dot']
|
|
1340
1496
|
}), /*#__PURE__*/jsxRuntime.jsx("span", {
|
|
@@ -1358,6 +1514,36 @@ const SlotItem = ({
|
|
|
1358
1514
|
children: slot.events.map(event => event.title).join('、')
|
|
1359
1515
|
}) : null]
|
|
1360
1516
|
});
|
|
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
|
+
});
|
|
1361
1547
|
return wrapWithPopover(inner);
|
|
1362
1548
|
};
|
|
1363
1549
|
|
|
@@ -1407,9 +1593,10 @@ const SchedulePanel = ({
|
|
|
1407
1593
|
selectedKeys,
|
|
1408
1594
|
selectSlots,
|
|
1409
1595
|
slotsRef,
|
|
1410
|
-
finishSelection,
|
|
1411
1596
|
handleMouseDown,
|
|
1412
1597
|
handleMouseEnter,
|
|
1598
|
+
handleMouseUp,
|
|
1599
|
+
handleMouseLeave,
|
|
1413
1600
|
handlePopoverOpenChange
|
|
1414
1601
|
} = useScheduleSelectionGesture({
|
|
1415
1602
|
slots,
|
|
@@ -1487,8 +1674,8 @@ const SchedulePanel = ({
|
|
|
1487
1674
|
[style$1['slots-dragging']]: dragging
|
|
1488
1675
|
}),
|
|
1489
1676
|
onScroll: onScroll,
|
|
1490
|
-
onMouseUp:
|
|
1491
|
-
onMouseLeave:
|
|
1677
|
+
onMouseUp: handleMouseUp,
|
|
1678
|
+
onMouseLeave: handleMouseLeave,
|
|
1492
1679
|
children: [/*#__PURE__*/jsxRuntime.jsx(PanelScrollShadow, {
|
|
1493
1680
|
visible: scrolled
|
|
1494
1681
|
}), /*#__PURE__*/jsxRuntime.jsx("div", {
|
|
@@ -1519,7 +1706,7 @@ const SchedulePanel = ({
|
|
|
1519
1706
|
},
|
|
1520
1707
|
onMouseDown: event => handleMouseDown(event, slot, index),
|
|
1521
1708
|
onMouseEnter: () => handleMouseEnter(index),
|
|
1522
|
-
onMouseUp:
|
|
1709
|
+
onMouseUp: handleMouseUp,
|
|
1523
1710
|
onClick: undefined
|
|
1524
1711
|
}, slot.key);
|
|
1525
1712
|
})
|