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