react-modal-sheet 5.4.1 → 5.5.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.mjs CHANGED
@@ -130,7 +130,7 @@ var REDUCED_MOTION_TWEEN_CONFIG = {
130
130
  duration: 0.01
131
131
  };
132
132
  var DEFAULT_DRAG_CLOSE_THRESHOLD = 0.6;
133
- var DEFAULT_DRAG_VELOCITY_THRESHOLD = 500;
133
+ var DEFAULT_DRAG_VELOCITY_THRESHOLD = 1200;
134
134
 
135
135
  // src/utils.ts
136
136
  function applyStyles(styles2, unstyled) {
@@ -1150,73 +1150,145 @@ function computeSnapPoints({
1150
1150
  // Y value is inverted as `y = 0` means sheet is at the top
1151
1151
  }));
1152
1152
  }
1153
- function findClosestSnapPoint({
1154
- snapPoints,
1155
- currentY
1156
- }) {
1153
+ function clamp(value, min, max) {
1154
+ return Math.min(Math.max(value, min), max);
1155
+ }
1156
+ function sign(value) {
1157
+ if (value > 0) return 1;
1158
+ if (value < 0) return -1;
1159
+ return 0;
1160
+ }
1161
+ function toAscendingYSnaps(snapPoints) {
1162
+ return snapPoints.slice().sort((a, b) => a.snapValueY - b.snapValueY);
1163
+ }
1164
+ function findNearestSnapByY(snapPoints, y) {
1157
1165
  return snapPoints.reduce(
1158
- (closest, snap) => Math.abs(snap.snapValueY - currentY) < Math.abs(closest.snapValueY - currentY) ? snap : closest
1166
+ (closest, snap) => Math.abs(snap.snapValueY - y) < Math.abs(closest.snapValueY - y) ? snap : closest
1159
1167
  );
1160
1168
  }
1161
- function findNextSnapPointInDirection({
1169
+ function findNearestSnapAboveY(snapPoints, y) {
1170
+ const snaps = toAscendingYSnaps(snapPoints);
1171
+ for (let i = snaps.length - 1; i >= 0; i--) {
1172
+ if (snaps[i].snapValueY < y) {
1173
+ return snaps[i];
1174
+ }
1175
+ }
1176
+ return null;
1177
+ }
1178
+ function findNearestSnapBelowY(snapPoints, y) {
1179
+ const snaps = toAscendingYSnaps(snapPoints);
1180
+ for (let i = 0; i < snaps.length; i++) {
1181
+ if (snaps[i].snapValueY > y) {
1182
+ return snaps[i];
1183
+ }
1184
+ }
1185
+ return null;
1186
+ }
1187
+ function resolveDirection({
1188
+ offsetY,
1189
+ velocityY,
1190
+ deltaY
1191
+ }) {
1192
+ if (Math.abs(velocityY) > 50) return sign(velocityY);
1193
+ const offsetDirection = sign(offsetY);
1194
+ if (offsetDirection !== 0) return offsetDirection;
1195
+ return sign(deltaY);
1196
+ }
1197
+ function classifyDragEnd({
1162
1198
  y,
1199
+ info,
1200
+ sheetHeight,
1163
1201
  snapPoints,
1164
- dragDirection
1202
+ dragVelocityThreshold,
1203
+ dragCloseThreshold
1165
1204
  }) {
1166
- if (dragDirection === "down") {
1167
- return snapPoints.slice().reverse().find((s) => s.snapValueY > y);
1168
- } else {
1169
- return snapPoints.find((s) => s.snapValueY < y);
1205
+ const flickVelocity = dragVelocityThreshold;
1206
+ const strongFlickVelocity = Math.max(2e3, flickVelocity);
1207
+ const distanceCommitRatio = 0.35;
1208
+ const predictionTime = 0.2;
1209
+ const minDragDistance = 20;
1210
+ const snapStickiness = 24;
1211
+ const minY = 0;
1212
+ const maxY = sheetHeight;
1213
+ const offsetY = info.offset.y;
1214
+ const deltaY = info.delta.y;
1215
+ const velocityY = info.velocity.y;
1216
+ const predictedY = clamp(y + velocityY * predictionTime, minY, maxY);
1217
+ const absVelocity = Math.abs(velocityY);
1218
+ const direction = resolveDirection({ offsetY, velocityY, deltaY });
1219
+ const isStrongFlick = absVelocity > strongFlickVelocity;
1220
+ const isFlick = absVelocity > flickVelocity;
1221
+ if (snapPoints.length === 0) {
1222
+ if (isFlick) {
1223
+ return {
1224
+ yTo: direction < 0 ? minY : maxY,
1225
+ snapIndex: void 0
1226
+ };
1227
+ }
1228
+ const midpoint = minY + (maxY - minY) * dragCloseThreshold;
1229
+ return {
1230
+ yTo: predictedY < midpoint ? minY : maxY,
1231
+ snapIndex: void 0
1232
+ };
1170
1233
  }
1171
- }
1172
- function handleHighVelocityDrag({
1173
- dragDirection,
1174
- snapPoints
1175
- }) {
1176
- const bottomSnapPoint = snapPoints[0];
1177
- const topSnapPoint = snapPoints[snapPoints.length - 1];
1178
- if (dragDirection === "down") {
1234
+ const nearestSnap = findNearestSnapByY(snapPoints, y);
1235
+ if (!isFlick && Math.abs(offsetY) < minDragDistance) {
1179
1236
  return {
1180
- yTo: bottomSnapPoint.snapValueY,
1181
- snapIndex: bottomSnapPoint.snapIndex
1237
+ yTo: nearestSnap.snapValueY,
1238
+ snapIndex: nearestSnap.snapIndex
1182
1239
  };
1183
1240
  }
1184
- return {
1185
- yTo: topSnapPoint.snapValueY,
1186
- snapIndex: topSnapPoint.snapIndex
1187
- };
1188
- }
1189
- function handleLowVelocityDrag({
1190
- currentSnapPoint,
1191
- currentY,
1192
- dragDirection,
1193
- snapPoints,
1194
- velocity
1195
- }) {
1196
- const closestSnapRelativeToCurrentY = findClosestSnapPoint({
1197
- snapPoints,
1198
- currentY
1199
- });
1200
- if (Math.abs(velocity) < 20) {
1241
+ if (!isFlick && Math.abs(y - nearestSnap.snapValueY) < snapStickiness) {
1201
1242
  return {
1202
- yTo: closestSnapRelativeToCurrentY.snapValueY,
1203
- snapIndex: closestSnapRelativeToCurrentY.snapIndex
1243
+ yTo: nearestSnap.snapValueY,
1244
+ snapIndex: nearestSnap.snapIndex
1204
1245
  };
1205
1246
  }
1206
- const nextSnapInDirectionRelativeToCurrentY = findNextSnapPointInDirection({
1207
- y: currentY,
1208
- snapPoints,
1209
- dragDirection
1210
- });
1211
- if (nextSnapInDirectionRelativeToCurrentY) {
1247
+ if (isStrongFlick) {
1248
+ const target = direction < 0 ? minY : maxY;
1249
+ const targetSnap = findNearestSnapByY(snapPoints, target);
1250
+ return {
1251
+ yTo: target,
1252
+ snapIndex: targetSnap.snapIndex
1253
+ };
1254
+ }
1255
+ if (isFlick) {
1256
+ const nextSnap2 = direction < 0 ? findNearestSnapAboveY(snapPoints, y) : findNearestSnapBelowY(snapPoints, y);
1257
+ const fallbackY = direction < 0 ? minY : maxY;
1258
+ const fallbackSnap = findNearestSnapByY(snapPoints, fallbackY);
1259
+ const targetSnap = nextSnap2 ?? fallbackSnap;
1260
+ return {
1261
+ yTo: targetSnap.snapValueY,
1262
+ snapIndex: targetSnap.snapIndex
1263
+ };
1264
+ }
1265
+ const firstSnap = toAscendingYSnaps(snapPoints)[0];
1266
+ const lastSnap = toAscendingYSnaps(snapPoints)[snapPoints.length - 1];
1267
+ const prevSnap = findNearestSnapAboveY(snapPoints, y) ?? firstSnap;
1268
+ const nextSnap = findNearestSnapBelowY(snapPoints, y) ?? lastSnap;
1269
+ if (prevSnap.snapIndex === nextSnap.snapIndex) {
1212
1270
  return {
1213
- yTo: nextSnapInDirectionRelativeToCurrentY.snapValueY,
1214
- snapIndex: nextSnapInDirectionRelativeToCurrentY.snapIndex
1271
+ yTo: prevSnap.snapValueY,
1272
+ snapIndex: prevSnap.snapIndex
1215
1273
  };
1216
1274
  }
1275
+ const range = nextSnap.snapValueY - prevSnap.snapValueY;
1276
+ const progress = range > 0 ? (y - prevSnap.snapValueY) / range : 0;
1277
+ let selectedSnap = nearestSnap;
1278
+ if (direction > 0) {
1279
+ selectedSnap = progress > distanceCommitRatio ? nextSnap : prevSnap;
1280
+ } else if (direction < 0) {
1281
+ selectedSnap = progress < 1 - distanceCommitRatio ? prevSnap : nextSnap;
1282
+ }
1283
+ if (absVelocity > 200) {
1284
+ const predictedSnap = findNearestSnapByY(snapPoints, predictedY);
1285
+ if (Math.abs(predictedSnap.snapValueY - y) < Math.abs(selectedSnap.snapValueY - y)) {
1286
+ selectedSnap = predictedSnap;
1287
+ }
1288
+ }
1217
1289
  return {
1218
- yTo: currentSnapPoint.snapValueY,
1219
- snapIndex: currentSnapPoint.snapIndex
1290
+ yTo: selectedSnap.snapValueY,
1291
+ snapIndex: selectedSnap.snapIndex
1220
1292
  };
1221
1293
  }
1222
1294
 
@@ -1363,45 +1435,25 @@ var Sheet = forwardRef(
1363
1435
  blurActiveInput();
1364
1436
  onDragEndProp == null ? void 0 : onDragEndProp(event, info);
1365
1437
  const currentY = y.get();
1366
- let yTo = 0;
1367
- const currentSnapPoint = currentSnap !== void 0 ? getSnapPoint(currentSnap) : null;
1368
- if (currentSnapPoint) {
1369
- const dragOffsetDirection = info.offset.y > 0 ? "down" : "up";
1370
- const dragVelocityDirection = info.velocity.y > 0 ? "down" : "up";
1371
- const isHighVelocity = Math.abs(info.velocity.y) > dragVelocityThreshold;
1372
- let result;
1373
- if (isHighVelocity) {
1374
- result = handleHighVelocityDrag({
1375
- snapPoints,
1376
- dragDirection: dragVelocityDirection
1377
- });
1378
- } else {
1379
- result = handleLowVelocityDrag({
1380
- currentSnapPoint,
1381
- currentY,
1382
- dragDirection: dragOffsetDirection,
1383
- snapPoints,
1384
- velocity: info.velocity.y
1385
- });
1386
- }
1387
- yTo = result.yTo;
1388
- if (disableDismiss && yTo + 1 >= sheetHeight) {
1389
- const bottomSnapPoint = snapPoints.find((s) => s.snapValue > 0);
1390
- if (bottomSnapPoint) {
1391
- yTo = bottomSnapPoint.snapValueY;
1392
- updateSnap(bottomSnapPoint.snapIndex);
1393
- } else {
1394
- yTo = currentY;
1395
- }
1396
- } else if (result.snapIndex !== void 0) {
1397
- updateSnap(result.snapIndex);
1398
- }
1399
- } else if (info.velocity.y > dragVelocityThreshold || currentY > sheetHeight * dragCloseThreshold) {
1400
- if (disableDismiss) {
1401
- yTo = 0;
1438
+ const result = classifyDragEnd({
1439
+ y: currentY,
1440
+ info,
1441
+ sheetHeight,
1442
+ dragCloseThreshold,
1443
+ snapPoints,
1444
+ dragVelocityThreshold
1445
+ });
1446
+ let yTo = result.yTo;
1447
+ if (disableDismiss && yTo + 1 >= sheetHeight) {
1448
+ const bottomSnapPoint = snapPoints.find((s) => s.snapValue > 0);
1449
+ if (bottomSnapPoint) {
1450
+ yTo = bottomSnapPoint.snapValueY;
1451
+ updateSnap(bottomSnapPoint.snapIndex);
1402
1452
  } else {
1403
- yTo = closedY;
1453
+ yTo = currentY;
1404
1454
  }
1455
+ } else if (result.snapIndex !== void 0) {
1456
+ updateSnap(result.snapIndex);
1405
1457
  }
1406
1458
  animate(y, yTo, animationOptions);
1407
1459
  if (yTo + 1 >= sheetHeight && !disableDismiss) {