tekiyo-physics 1.0.0 → 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.cjs CHANGED
@@ -3,7 +3,7 @@ var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
- const react = require("react");
6
+ const React = require("react");
7
7
  const jsxRuntime = require("react/jsx-runtime");
8
8
  class Vector2 {
9
9
  constructor(x = 0, y = 0) {
@@ -696,7 +696,7 @@ const defaultContext = {
696
696
  shadowIntensity: 1
697
697
  }
698
698
  };
699
- const PhysicsContext = react.createContext(defaultContext);
699
+ const PhysicsContext = React.createContext(defaultContext);
700
700
  function PhysicsProvider({
701
701
  children,
702
702
  preset = "ios",
@@ -705,7 +705,7 @@ function PhysicsProvider({
705
705
  stretch,
706
706
  lift
707
707
  }) {
708
- const value = react.useMemo(() => {
708
+ const value = React.useMemo(() => {
709
709
  const basePreset = typeof preset === "string" ? presets[preset] ?? ios : preset;
710
710
  return {
711
711
  spring: { ...basePreset.spring, ...spring },
@@ -717,45 +717,45 @@ function PhysicsProvider({
717
717
  return /* @__PURE__ */ jsxRuntime.jsx(PhysicsContext.Provider, { value, children });
718
718
  }
719
719
  function usePhysicsContext() {
720
- return react.useContext(PhysicsContext);
720
+ return React.useContext(PhysicsContext);
721
721
  }
722
722
  function usePhysicsConfig() {
723
- const context = react.useContext(PhysicsContext);
723
+ const context = React.useContext(PhysicsContext);
724
724
  return context.spring;
725
725
  }
726
726
  function useFrictionConfig() {
727
- const context = react.useContext(PhysicsContext);
727
+ const context = React.useContext(PhysicsContext);
728
728
  return context.friction;
729
729
  }
730
730
  function useStretchConfig() {
731
- const context = react.useContext(PhysicsContext);
731
+ const context = React.useContext(PhysicsContext);
732
732
  return context.stretch;
733
733
  }
734
734
  function useLiftConfig() {
735
- const context = react.useContext(PhysicsContext);
735
+ const context = React.useContext(PhysicsContext);
736
736
  return context.lift;
737
737
  }
738
738
  function useSpring(options) {
739
739
  const { to, from, config, onRest, immediate = true } = options;
740
740
  const globalConfig = usePhysicsConfig();
741
- const resolvedConfig = react.useMemo(() => {
741
+ const resolvedConfig = React.useMemo(() => {
742
742
  if (!config) return { ...DEFAULT_SPRING_CONFIG, ...globalConfig };
743
743
  if (typeof config === "string") {
744
744
  return { ...DEFAULT_SPRING_CONFIG, ...globalConfig };
745
745
  }
746
746
  return { ...DEFAULT_SPRING_CONFIG, ...globalConfig, ...config };
747
747
  }, [config, globalConfig]);
748
- const springRef = react.useRef(null);
749
- const idRef = react.useRef(generatePhysicsId("spring"));
750
- const onRestRef = react.useRef(onRest);
748
+ const springRef = React.useRef(null);
749
+ const idRef = React.useRef(generatePhysicsId("spring"));
750
+ const onRestRef = React.useRef(onRest);
751
751
  onRestRef.current = onRest;
752
- const [state, setState] = react.useState({
752
+ const [state, setState] = React.useState({
753
753
  x: (from == null ? void 0 : from.x) ?? to.x,
754
754
  y: (from == null ? void 0 : from.y) ?? to.y,
755
755
  isAnimating: false,
756
756
  isSettled: true
757
757
  });
758
- react.useEffect(() => {
758
+ React.useEffect(() => {
759
759
  const initialPos = from ? Vector2.from(from.x, from.y) : Vector2.from(to.x, to.y);
760
760
  springRef.current = new Spring(initialPos, resolvedConfig);
761
761
  if (immediate) {
@@ -765,7 +765,7 @@ function useSpring(options) {
765
765
  PhysicsEngine.unregister(idRef.current);
766
766
  };
767
767
  }, []);
768
- react.useEffect(() => {
768
+ React.useEffect(() => {
769
769
  if (springRef.current && immediate) {
770
770
  springRef.current.setTarget(Vector2.from(to.x, to.y));
771
771
  if (!PhysicsEngine.has(idRef.current)) {
@@ -787,11 +787,11 @@ function useSpring(options) {
787
787
  }
788
788
  }
789
789
  }, [to.x, to.y, immediate]);
790
- react.useEffect(() => {
790
+ React.useEffect(() => {
791
791
  var _a;
792
792
  (_a = springRef.current) == null ? void 0 : _a.updateConfig(resolvedConfig);
793
793
  }, [resolvedConfig]);
794
- const setTarget = react.useCallback((target) => {
794
+ const setTarget = React.useCallback((target) => {
795
795
  if (!springRef.current) return;
796
796
  springRef.current.setTarget(Vector2.from(target.x, target.y));
797
797
  if (!PhysicsEngine.has(idRef.current)) {
@@ -812,7 +812,7 @@ function useSpring(options) {
812
812
  });
813
813
  }
814
814
  }, []);
815
- const snapTo = react.useCallback((position) => {
815
+ const snapTo = React.useCallback((position) => {
816
816
  if (!springRef.current) return;
817
817
  springRef.current.setPosition(Vector2.from(position.x, position.y));
818
818
  springRef.current.setTarget(Vector2.from(position.x, position.y));
@@ -825,7 +825,7 @@ function useSpring(options) {
825
825
  });
826
826
  PhysicsEngine.unregister(idRef.current);
827
827
  }, []);
828
- const impulse = react.useCallback((velocity) => {
828
+ const impulse = React.useCallback((velocity) => {
829
829
  if (!springRef.current) return;
830
830
  const currentState = springRef.current.getState();
831
831
  springRef.current.setVelocity(
@@ -849,7 +849,7 @@ function useSpring(options) {
849
849
  });
850
850
  }
851
851
  }, []);
852
- const style = react.useMemo(
852
+ const style = React.useMemo(
853
853
  () => ({
854
854
  transform: `translate3d(${state.x}px, ${state.y}px, 0)`,
855
855
  willChange: state.isAnimating ? "transform" : "auto"
@@ -870,20 +870,20 @@ function useSpring(options) {
870
870
  function useSpring1D(options) {
871
871
  const { to, from, config, onRest, immediate = true } = options;
872
872
  const globalConfig = usePhysicsConfig();
873
- const resolvedConfig = react.useMemo(
873
+ const resolvedConfig = React.useMemo(
874
874
  () => ({ ...DEFAULT_SPRING_CONFIG, ...globalConfig, ...config }),
875
875
  [config, globalConfig]
876
876
  );
877
- const springRef = react.useRef(null);
878
- const idRef = react.useRef(generatePhysicsId("spring1d"));
879
- const onRestRef = react.useRef(onRest);
877
+ const springRef = React.useRef(null);
878
+ const idRef = React.useRef(generatePhysicsId("spring1d"));
879
+ const onRestRef = React.useRef(onRest);
880
880
  onRestRef.current = onRest;
881
- const [state, setState] = react.useState({
881
+ const [state, setState] = React.useState({
882
882
  value: from ?? to,
883
883
  isAnimating: false,
884
884
  isSettled: true
885
885
  });
886
- react.useEffect(() => {
886
+ React.useEffect(() => {
887
887
  springRef.current = new Spring1D(from ?? to, resolvedConfig);
888
888
  if (immediate) {
889
889
  springRef.current.setTarget(to);
@@ -892,7 +892,7 @@ function useSpring1D(options) {
892
892
  PhysicsEngine.unregister(idRef.current);
893
893
  };
894
894
  }, []);
895
- react.useEffect(() => {
895
+ React.useEffect(() => {
896
896
  if (springRef.current && immediate) {
897
897
  springRef.current.setTarget(to);
898
898
  if (!PhysicsEngine.has(idRef.current)) {
@@ -913,7 +913,7 @@ function useSpring1D(options) {
913
913
  }
914
914
  }
915
915
  }, [to, immediate]);
916
- const setTarget = react.useCallback((target) => {
916
+ const setTarget = React.useCallback((target) => {
917
917
  if (!springRef.current) return;
918
918
  springRef.current.setTarget(target);
919
919
  if (!PhysicsEngine.has(idRef.current)) {
@@ -952,16 +952,16 @@ function useGesture(options) {
952
952
  flickThreshold = 500,
953
953
  preventTouch = true
954
954
  } = options;
955
- const velocityTracker = react.useRef(new VelocityTracker());
956
- const startPosition = react.useRef(Vector2.zero());
957
- const lastPosition = react.useRef(Vector2.zero());
958
- const isDragging = react.useRef(false);
959
- const hasExceededThreshold = react.useRef(false);
960
- const pointerId = react.useRef(null);
961
- const elementRef = react.useRef(null);
962
- const handlersRef = react.useRef({ onDragStart, onDrag, onDragEnd, onFlick });
955
+ const velocityTracker = React.useRef(new VelocityTracker());
956
+ const startPosition = React.useRef(Vector2.zero());
957
+ const lastPosition = React.useRef(Vector2.zero());
958
+ const isDragging = React.useRef(false);
959
+ const hasExceededThreshold = React.useRef(false);
960
+ const pointerId = React.useRef(null);
961
+ const elementRef = React.useRef(null);
962
+ const handlersRef = React.useRef({ onDragStart, onDrag, onDragEnd, onFlick });
963
963
  handlersRef.current = { onDragStart, onDrag, onDragEnd, onFlick };
964
- const createGestureState = react.useCallback(
964
+ const createGestureState = React.useCallback(
965
965
  (event, first, last) => {
966
966
  const position = Vector2.from(event.clientX, event.clientY);
967
967
  const delta = position.subtract(lastPosition.current);
@@ -982,7 +982,7 @@ function useGesture(options) {
982
982
  },
983
983
  []
984
984
  );
985
- const handlePointerMove = react.useCallback(
985
+ const handlePointerMove = React.useCallback(
986
986
  (event) => {
987
987
  var _a, _b, _c, _d;
988
988
  if (!isDragging.current || pointerId.current !== event.pointerId) return;
@@ -1001,7 +1001,7 @@ function useGesture(options) {
1001
1001
  },
1002
1002
  [threshold, createGestureState]
1003
1003
  );
1004
- const handlePointerUp = react.useCallback(
1004
+ const handlePointerUp = React.useCallback(
1005
1005
  (event) => {
1006
1006
  var _a, _b, _c, _d;
1007
1007
  if (!isDragging.current || pointerId.current !== event.pointerId) return;
@@ -1023,7 +1023,7 @@ function useGesture(options) {
1023
1023
  },
1024
1024
  [handlePointerMove, createGestureState, flickThreshold]
1025
1025
  );
1026
- const onPointerDown = react.useCallback(
1026
+ const onPointerDown = React.useCallback(
1027
1027
  (e) => {
1028
1028
  if (isDragging.current) return;
1029
1029
  const element = e.currentTarget;
@@ -1043,19 +1043,19 @@ function useGesture(options) {
1043
1043
  },
1044
1044
  [handlePointerMove, handlePointerUp]
1045
1045
  );
1046
- const onPointerEnter = react.useCallback(() => {
1046
+ const onPointerEnter = React.useCallback(() => {
1047
1047
  onHover == null ? void 0 : onHover(true);
1048
1048
  }, [onHover]);
1049
- const onPointerLeave = react.useCallback(() => {
1049
+ const onPointerLeave = React.useCallback(() => {
1050
1050
  onHover == null ? void 0 : onHover(false);
1051
1051
  }, [onHover]);
1052
- const handleFocus = react.useCallback(() => {
1052
+ const handleFocus = React.useCallback(() => {
1053
1053
  onFocus == null ? void 0 : onFocus(true);
1054
1054
  }, [onFocus]);
1055
- const handleBlur = react.useCallback(() => {
1055
+ const handleBlur = React.useCallback(() => {
1056
1056
  onFocus == null ? void 0 : onFocus(false);
1057
1057
  }, [onFocus]);
1058
- react.useEffect(() => {
1058
+ React.useEffect(() => {
1059
1059
  return () => {
1060
1060
  document.removeEventListener("pointermove", handlePointerMove);
1061
1061
  document.removeEventListener("pointerup", handlePointerUp);
@@ -1089,38 +1089,38 @@ function useDrag(options = {}) {
1089
1089
  onRest
1090
1090
  } = options;
1091
1091
  const globalConfig = usePhysicsConfig();
1092
- const resolvedSpringConfig = react.useMemo(
1092
+ const resolvedSpringConfig = React.useMemo(
1093
1093
  () => ({ ...DEFAULT_SPRING_CONFIG, ...globalConfig, ...springConfig }),
1094
1094
  [globalConfig, springConfig]
1095
1095
  );
1096
- const resolvedFrictionConfig = react.useMemo(
1096
+ const resolvedFrictionConfig = React.useMemo(
1097
1097
  () => ({ ...DEFAULT_FRICTION_CONFIG, ...frictionConfig }),
1098
1098
  [frictionConfig]
1099
1099
  );
1100
- const [state, setState] = react.useState({
1100
+ const [state, setState] = React.useState({
1101
1101
  x: initial.x,
1102
1102
  y: initial.y,
1103
1103
  isDragging: false,
1104
1104
  isMomentum: false
1105
1105
  });
1106
- const positionRef = react.useRef(Vector2.from(initial.x, initial.y));
1107
- const startPositionRef = react.useRef(Vector2.zero());
1108
- const dragOffsetRef = react.useRef(Vector2.zero());
1109
- const velocityTracker = react.useRef(new VelocityTracker());
1110
- const springRef = react.useRef(null);
1111
- const momentumRef = react.useRef(null);
1112
- const idRef = react.useRef(generatePhysicsId("drag"));
1113
- const pointerIdRef = react.useRef(null);
1114
- const handlersRef = react.useRef({ onDragStart, onDrag, onDragEnd, onRest });
1106
+ const positionRef = React.useRef(Vector2.from(initial.x, initial.y));
1107
+ const startPositionRef = React.useRef(Vector2.zero());
1108
+ const dragOffsetRef = React.useRef(Vector2.zero());
1109
+ const velocityTracker = React.useRef(new VelocityTracker());
1110
+ const springRef = React.useRef(null);
1111
+ const momentumRef = React.useRef(null);
1112
+ const idRef = React.useRef(generatePhysicsId("drag"));
1113
+ const pointerIdRef = React.useRef(null);
1114
+ const handlersRef = React.useRef({ onDragStart, onDrag, onDragEnd, onRest });
1115
1115
  handlersRef.current = { onDragStart, onDrag, onDragEnd, onRest };
1116
- const boundsVec = react.useMemo(() => {
1116
+ const boundsVec = React.useMemo(() => {
1117
1117
  if (!bounds) return null;
1118
1118
  return {
1119
1119
  min: Vector2.from(bounds.left ?? -Infinity, bounds.top ?? -Infinity),
1120
1120
  max: Vector2.from(bounds.right ?? Infinity, bounds.bottom ?? Infinity)
1121
1121
  };
1122
1122
  }, [bounds]);
1123
- const applyRubberBand = react.useCallback(
1123
+ const applyRubberBand = React.useCallback(
1124
1124
  (pos) => {
1125
1125
  if (!boundsVec || !rubberBand) return pos;
1126
1126
  let x = pos.x;
@@ -1143,14 +1143,14 @@ function useDrag(options = {}) {
1143
1143
  },
1144
1144
  [boundsVec, rubberBand, rubberBandFactor]
1145
1145
  );
1146
- const clampToBounds = react.useCallback(
1146
+ const clampToBounds = React.useCallback(
1147
1147
  (pos) => {
1148
1148
  if (!boundsVec) return pos;
1149
1149
  return pos.clampComponents(boundsVec.min, boundsVec.max);
1150
1150
  },
1151
1151
  [boundsVec]
1152
1152
  );
1153
- const findSnapPoint = react.useCallback(
1153
+ const findSnapPoint = React.useCallback(
1154
1154
  (pos, velocity) => {
1155
1155
  if (!snapTo || snapTo.length === 0) return null;
1156
1156
  const projectedPos = pos.add(velocity.multiply(0.1));
@@ -1168,7 +1168,7 @@ function useDrag(options = {}) {
1168
1168
  },
1169
1169
  [snapTo, snapThreshold]
1170
1170
  );
1171
- const animateToTarget = react.useCallback(
1171
+ const animateToTarget = React.useCallback(
1172
1172
  (target) => {
1173
1173
  springRef.current = new Spring(positionRef.current, resolvedSpringConfig);
1174
1174
  springRef.current.setTarget(target);
@@ -1191,7 +1191,7 @@ function useDrag(options = {}) {
1191
1191
  },
1192
1192
  [resolvedSpringConfig]
1193
1193
  );
1194
- const handlePointerMove = react.useCallback(
1194
+ const handlePointerMove = React.useCallback(
1195
1195
  (e) => {
1196
1196
  var _a, _b;
1197
1197
  if (pointerIdRef.current !== e.pointerId) return;
@@ -1214,7 +1214,7 @@ function useDrag(options = {}) {
1214
1214
  },
1215
1215
  [axis, applyRubberBand]
1216
1216
  );
1217
- const handlePointerUp = react.useCallback(
1217
+ const handlePointerUp = React.useCallback(
1218
1218
  (e) => {
1219
1219
  var _a, _b, _c, _d;
1220
1220
  if (pointerIdRef.current !== e.pointerId) return;
@@ -1283,7 +1283,7 @@ function useDrag(options = {}) {
1283
1283
  resolvedFrictionConfig
1284
1284
  ]
1285
1285
  );
1286
- const onPointerDown = react.useCallback(
1286
+ const onPointerDown = React.useCallback(
1287
1287
  (e) => {
1288
1288
  var _a, _b;
1289
1289
  if (pointerIdRef.current !== null) return;
@@ -1304,7 +1304,7 @@ function useDrag(options = {}) {
1304
1304
  },
1305
1305
  [handlePointerMove, handlePointerUp]
1306
1306
  );
1307
- const setPosition = react.useCallback((position) => {
1307
+ const setPosition = React.useCallback((position) => {
1308
1308
  PhysicsEngine.unregister(idRef.current);
1309
1309
  positionRef.current = Vector2.from(position.x, position.y);
1310
1310
  setState((prev) => ({
@@ -1315,13 +1315,13 @@ function useDrag(options = {}) {
1315
1315
  isMomentum: false
1316
1316
  }));
1317
1317
  }, []);
1318
- const snapToPosition = react.useCallback(
1318
+ const snapToPosition = React.useCallback(
1319
1319
  (position) => {
1320
1320
  animateToTarget(Vector2.from(position.x, position.y));
1321
1321
  },
1322
1322
  [animateToTarget]
1323
1323
  );
1324
- react.useEffect(() => {
1324
+ React.useEffect(() => {
1325
1325
  return () => {
1326
1326
  PhysicsEngine.unregister(idRef.current);
1327
1327
  document.removeEventListener("pointermove", handlePointerMove);
@@ -1329,7 +1329,7 @@ function useDrag(options = {}) {
1329
1329
  document.removeEventListener("pointercancel", handlePointerUp);
1330
1330
  };
1331
1331
  }, [handlePointerMove, handlePointerUp]);
1332
- const style = react.useMemo(
1332
+ const style = React.useMemo(
1333
1333
  () => ({
1334
1334
  transform: `translate3d(${state.x}px, ${state.y}px, 0)`,
1335
1335
  willChange: state.isDragging || state.isMomentum ? "transform" : "auto",
@@ -1358,7 +1358,7 @@ function useStretch(options) {
1358
1358
  intensity = 0.12,
1359
1359
  maxVelocity = 2e3
1360
1360
  } = options;
1361
- return react.useMemo(() => {
1361
+ return React.useMemo(() => {
1362
1362
  const vel = velocity instanceof Vector2 ? velocity : Vector2.from(velocity.x, velocity.y);
1363
1363
  const normalizedX = Math.min(Math.abs(vel.x) / maxVelocity, 1);
1364
1364
  const normalizedY = Math.min(Math.abs(vel.y) / maxVelocity, 1);
@@ -1428,7 +1428,7 @@ function useLift(options = {}) {
1428
1428
  springConfig,
1429
1429
  onLift
1430
1430
  } = options;
1431
- const [isLifted, setIsLifted] = react.useState(false);
1431
+ const [isLifted, setIsLifted] = React.useState(false);
1432
1432
  const { value: scale } = useSpring1D({
1433
1433
  to: isLifted ? targetScale : 1,
1434
1434
  config: {
@@ -1437,14 +1437,14 @@ function useLift(options = {}) {
1437
1437
  ...springConfig
1438
1438
  }
1439
1439
  });
1440
- const handleLift = react.useCallback(
1440
+ const handleLift = React.useCallback(
1441
1441
  (lifted) => {
1442
1442
  setIsLifted(lifted);
1443
1443
  onLift == null ? void 0 : onLift(lifted);
1444
1444
  },
1445
1445
  [onLift]
1446
1446
  );
1447
- const bind = react.useMemo(() => {
1447
+ const bind = React.useMemo(() => {
1448
1448
  switch (trigger) {
1449
1449
  case "hover":
1450
1450
  return {
@@ -1471,7 +1471,7 @@ function useLift(options = {}) {
1471
1471
  return {};
1472
1472
  }
1473
1473
  }, [trigger, handleLift]);
1474
- const shadowStyle = react.useMemo(() => {
1474
+ const shadowStyle = React.useMemo(() => {
1475
1475
  if (!shadow) return {};
1476
1476
  const liftAmount = (scale - 1) / (targetScale - 1);
1477
1477
  const clampedLift = Math.max(0, Math.min(1, liftAmount));
@@ -1483,7 +1483,7 @@ function useLift(options = {}) {
1483
1483
  boxShadow: `0 ${yOffset}px ${blur}px ${spread}px rgba(0, 0, 0, ${opacity})`
1484
1484
  };
1485
1485
  }, [scale, targetScale, shadow, shadowIntensity]);
1486
- const style = react.useMemo(
1486
+ const style = React.useMemo(
1487
1487
  () => ({
1488
1488
  transform: `scale3d(${scale}, ${scale}, 1)`,
1489
1489
  transformOrigin: "center center",
@@ -1519,23 +1519,23 @@ function useFlick(options = {}) {
1519
1519
  onFlick,
1520
1520
  onFlickEnd
1521
1521
  } = options;
1522
- const resolvedFriction = react.useMemo(
1522
+ const resolvedFriction = React.useMemo(
1523
1523
  () => ({ ...DEFAULT_FRICTION_CONFIG, ...friction }),
1524
1524
  [friction]
1525
1525
  );
1526
- const [state, setState] = react.useState({
1526
+ const [state, setState] = React.useState({
1527
1527
  x: initial.x,
1528
1528
  y: initial.y,
1529
1529
  isFlicking: false,
1530
1530
  velocity: Vector2.zero()
1531
1531
  });
1532
- const positionRef = react.useRef(Vector2.from(initial.x, initial.y));
1533
- const momentumRef = react.useRef(null);
1534
- const velocityTracker = react.useRef(new VelocityTracker());
1535
- const idRef = react.useRef(generatePhysicsId("flick"));
1536
- const handlersRef = react.useRef({ onFlickStart, onFlick, onFlickEnd });
1532
+ const positionRef = React.useRef(Vector2.from(initial.x, initial.y));
1533
+ const momentumRef = React.useRef(null);
1534
+ const velocityTracker = React.useRef(new VelocityTracker());
1535
+ const idRef = React.useRef(generatePhysicsId("flick"));
1536
+ const handlersRef = React.useRef({ onFlickStart, onFlick, onFlickEnd });
1537
1537
  handlersRef.current = { onFlickStart, onFlick, onFlickEnd };
1538
- const startMomentum = react.useCallback(
1538
+ const startMomentum = React.useCallback(
1539
1539
  (velocity) => {
1540
1540
  var _a, _b;
1541
1541
  let constrainedVelocity = velocity;
@@ -1578,7 +1578,7 @@ function useFlick(options = {}) {
1578
1578
  },
1579
1579
  [axis, resolvedFriction]
1580
1580
  );
1581
- const flick = react.useCallback(
1581
+ const flick = React.useCallback(
1582
1582
  (velocity) => {
1583
1583
  PhysicsEngine.unregister(idRef.current);
1584
1584
  velocityTracker.current.reset();
@@ -1586,7 +1586,7 @@ function useFlick(options = {}) {
1586
1586
  },
1587
1587
  [startMomentum]
1588
1588
  );
1589
- const stop = react.useCallback(() => {
1589
+ const stop = React.useCallback(() => {
1590
1590
  var _a;
1591
1591
  PhysicsEngine.unregister(idRef.current);
1592
1592
  (_a = momentumRef.current) == null ? void 0 : _a.stop();
@@ -1597,7 +1597,7 @@ function useFlick(options = {}) {
1597
1597
  velocity: Vector2.zero()
1598
1598
  }));
1599
1599
  }, []);
1600
- const setPosition = react.useCallback(
1600
+ const setPosition = React.useCallback(
1601
1601
  (position) => {
1602
1602
  stop();
1603
1603
  positionRef.current = Vector2.from(position.x, position.y);
@@ -1610,7 +1610,7 @@ function useFlick(options = {}) {
1610
1610
  },
1611
1611
  [stop]
1612
1612
  );
1613
- const recordSample = react.useCallback((position) => {
1613
+ const recordSample = React.useCallback((position) => {
1614
1614
  velocityTracker.current.addSample(Vector2.from(position.x, position.y));
1615
1615
  positionRef.current = Vector2.from(position.x, position.y);
1616
1616
  setState((prev) => ({
@@ -1619,10 +1619,10 @@ function useFlick(options = {}) {
1619
1619
  y: position.y
1620
1620
  }));
1621
1621
  }, []);
1622
- const getCurrentVelocity = react.useCallback(() => {
1622
+ const getCurrentVelocity = React.useCallback(() => {
1623
1623
  return velocityTracker.current.getVelocity();
1624
1624
  }, []);
1625
- const release = react.useCallback(
1625
+ const release = React.useCallback(
1626
1626
  (position) => {
1627
1627
  positionRef.current = Vector2.from(position.x, position.y);
1628
1628
  const velocity = velocityTracker.current.getVelocity();
@@ -1642,12 +1642,12 @@ function useFlick(options = {}) {
1642
1642
  },
1643
1643
  [threshold, startMomentum]
1644
1644
  );
1645
- react.useEffect(() => {
1645
+ React.useEffect(() => {
1646
1646
  return () => {
1647
1647
  PhysicsEngine.unregister(idRef.current);
1648
1648
  };
1649
1649
  }, []);
1650
- const style = react.useMemo(
1650
+ const style = React.useMemo(
1651
1651
  () => ({
1652
1652
  transform: `translate3d(${state.x}px, ${state.y}px, 0)`,
1653
1653
  willChange: state.isFlicking ? "transform" : "auto"
@@ -1668,7 +1668,7 @@ function useFlick(options = {}) {
1668
1668
  release
1669
1669
  };
1670
1670
  }
1671
- const Draggable = react.forwardRef(
1671
+ const Draggable = React.forwardRef(
1672
1672
  ({
1673
1673
  children,
1674
1674
  initial = { x: 0, y: 0 },
@@ -1711,7 +1711,7 @@ const Draggable = react.forwardRef(
1711
1711
  scale: physicsContext.lift.scale,
1712
1712
  shadowIntensity: physicsContext.lift.shadowIntensity
1713
1713
  });
1714
- const velocity = react.useMemo(() => {
1714
+ const velocity = React.useMemo(() => {
1715
1715
  if (!isDragging && !isMomentum) return Vector2.zero();
1716
1716
  return Vector2.from(0, 0);
1717
1717
  }, [isDragging, isMomentum]);
@@ -1720,7 +1720,7 @@ const Draggable = react.forwardRef(
1720
1720
  intensity: physicsContext.stretch.intensity,
1721
1721
  maxVelocity: physicsContext.stretch.maxVelocity
1722
1722
  });
1723
- const combinedStyle = react.useMemo(() => {
1723
+ const combinedStyle = React.useMemo(() => {
1724
1724
  let result = { ...dragStyle };
1725
1725
  if (enableLift && isDragging) {
1726
1726
  result = combineLiftStyle(result, liftStyle);
@@ -1735,7 +1735,7 @@ const Draggable = react.forwardRef(
1735
1735
  userSelect: "none"
1736
1736
  };
1737
1737
  }, [dragStyle, liftStyle, stretchStyle, enableLift, enableStretch, isDragging, customStyle]);
1738
- const combinedBind = react.useMemo(() => {
1738
+ const combinedBind = React.useMemo(() => {
1739
1739
  return {
1740
1740
  ...bind,
1741
1741
  onPointerDown: (e) => {
@@ -1766,7 +1766,7 @@ const Draggable = react.forwardRef(
1766
1766
  }
1767
1767
  );
1768
1768
  Draggable.displayName = "Draggable";
1769
- const Card = react.forwardRef(
1769
+ const Card = React.forwardRef(
1770
1770
  ({
1771
1771
  children,
1772
1772
  lift: enableLift = true,
@@ -1783,7 +1783,7 @@ const Card = react.forwardRef(
1783
1783
  onPress
1784
1784
  }, ref) => {
1785
1785
  const physicsContext = usePhysicsContext();
1786
- const [isPressed, setIsPressed] = react.useState(false);
1786
+ const [isPressed, setIsPressed] = React.useState(false);
1787
1787
  const resolvedLiftScale = liftScale ?? physicsContext.lift.scale;
1788
1788
  const { style: liftStyle, bind: liftBind, isLifted } = useLift({
1789
1789
  trigger: "hover",
@@ -1800,19 +1800,19 @@ const Card = react.forwardRef(
1800
1800
  ...springConfig
1801
1801
  }
1802
1802
  });
1803
- const handlePointerDown = react.useCallback(() => {
1803
+ const handlePointerDown = React.useCallback(() => {
1804
1804
  if (pressable) {
1805
1805
  setIsPressed(true);
1806
1806
  onPress == null ? void 0 : onPress(true);
1807
1807
  }
1808
1808
  }, [pressable, onPress]);
1809
- const handlePointerUp = react.useCallback(() => {
1809
+ const handlePointerUp = React.useCallback(() => {
1810
1810
  if (pressable) {
1811
1811
  setIsPressed(false);
1812
1812
  onPress == null ? void 0 : onPress(false);
1813
1813
  }
1814
1814
  }, [pressable, onPress]);
1815
- const handlePointerLeave = react.useCallback(() => {
1815
+ const handlePointerLeave = React.useCallback(() => {
1816
1816
  if (isPressed) {
1817
1817
  setIsPressed(false);
1818
1818
  onPress == null ? void 0 : onPress(false);
@@ -1824,7 +1824,7 @@ const Card = react.forwardRef(
1824
1824
  WebkitBackdropFilter: `blur(${glassBlur}px)`,
1825
1825
  border: "1px solid rgba(255, 255, 255, 0.18)"
1826
1826
  } : {};
1827
- const combinedStyle = react.useMemo(() => {
1827
+ const combinedStyle = React.useMemo(() => {
1828
1828
  const liftTransform = enableLift ? liftStyle.transform || "" : "";
1829
1829
  const pressTransform = pressable ? `scale(${pressScaleValue})` : "";
1830
1830
  const transform = `${liftTransform} ${pressTransform}`.trim() || void 0;
@@ -1875,38 +1875,2341 @@ const cardBaseStyles = {
1875
1875
  background: "white",
1876
1876
  boxShadow: "0 4px 12px rgba(0, 0, 0, 0.08)"
1877
1877
  };
1878
+ function PhysicsSlider({
1879
+ label,
1880
+ description,
1881
+ value,
1882
+ min,
1883
+ max,
1884
+ step,
1885
+ config,
1886
+ onChange,
1887
+ decimals = 3,
1888
+ forceDrag = false,
1889
+ className = ""
1890
+ }) {
1891
+ const trackRef = React.useRef(null);
1892
+ const thumbRef = React.useRef(null);
1893
+ const state = React.useRef({
1894
+ isDragging: false,
1895
+ displayPercent: (value - min) / (max - min) * 100,
1896
+ thumbScale: 1,
1897
+ verticalOffset: 0,
1898
+ stretchX: 1,
1899
+ stretchY: 1,
1900
+ stretchOriginPercent: 50,
1901
+ targetOriginPercent: 50,
1902
+ shadowY: 2,
1903
+ shadowBlur: 6,
1904
+ shadowOpacity: 0.12,
1905
+ lastPercent: (value - min) / (max - min) * 100,
1906
+ currentVelocity: 0,
1907
+ pointerStart: null
1908
+ });
1909
+ const [, forceRender] = React.useState(0);
1910
+ const rerender = React.useCallback(() => forceRender((n) => n + 1), []);
1911
+ const springRef = React.useRef(null);
1912
+ const momentumRef = React.useRef(null);
1913
+ const scaleSpringRef = React.useRef(null);
1914
+ const verticalSpringRef = React.useRef(null);
1915
+ const stretchXSpringRef = React.useRef(null);
1916
+ const stretchYSpringRef = React.useRef(null);
1917
+ const stretchOriginSpringRef = React.useRef(null);
1918
+ const shadowSpringRef = React.useRef(null);
1919
+ const velocityTracker = React.useRef(new VelocityTracker1D());
1920
+ const physicsIdRef = React.useRef(generatePhysicsId("slider"));
1921
+ const scaleIdRef = React.useRef(generatePhysicsId("scale"));
1922
+ const verticalIdRef = React.useRef(generatePhysicsId("sliderVertical"));
1923
+ const stretchXIdRef = React.useRef(generatePhysicsId("stretchX"));
1924
+ const stretchYIdRef = React.useRef(generatePhysicsId("stretchY"));
1925
+ const stretchOriginIdRef = React.useRef(generatePhysicsId("stretchOrigin"));
1926
+ const shadowIdRef = React.useRef(generatePhysicsId("shadow"));
1927
+ const dragDecayIdRef = React.useRef(generatePhysicsId("dragDecay"));
1928
+ React.useEffect(() => {
1929
+ if (!state.current.isDragging && !forceDrag) {
1930
+ const targetPercent = (value - min) / (max - min) * 100;
1931
+ animateToPercent(targetPercent);
1932
+ }
1933
+ }, [value, min, max]);
1934
+ const lastValueRef = React.useRef(value);
1935
+ React.useEffect(() => {
1936
+ if (!forceDrag) {
1937
+ lastValueRef.current = value;
1938
+ return;
1939
+ }
1940
+ const s2 = state.current;
1941
+ const targetPercent = (value - min) / (max - min) * 100;
1942
+ const lastPercent = (lastValueRef.current - min) / (max - min) * 100;
1943
+ const velocity = targetPercent - lastPercent;
1944
+ lastValueRef.current = value;
1945
+ s2.thumbScale = config.liftScale;
1946
+ const absVelocity = Math.abs(velocity);
1947
+ const velStretch = Math.min(absVelocity * config.velocityStretch * 15, 1.2);
1948
+ s2.stretchX = 1 + velStretch;
1949
+ s2.stretchY = 1 - velStretch * 0.4;
1950
+ if (Math.abs(velocity) > 0.1) {
1951
+ s2.stretchOriginPercent = velocity > 0 ? 100 : 0;
1952
+ }
1953
+ s2.shadowY = 2 + absVelocity * 0.4;
1954
+ s2.shadowBlur = 6 + absVelocity * 0.6;
1955
+ s2.shadowOpacity = 0.12 + absVelocity * 0.015;
1956
+ s2.displayPercent = targetPercent;
1957
+ s2.isDragging = true;
1958
+ rerender();
1959
+ }, [forceDrag, value, min, max, config.liftScale, config.velocityStretch, rerender]);
1960
+ const animateToPercent = React.useCallback((targetPercent) => {
1961
+ const s2 = state.current;
1962
+ PhysicsEngine.unregister(physicsIdRef.current);
1963
+ springRef.current = new Spring1D(s2.displayPercent, {
1964
+ tension: config.springTension * 400,
1965
+ friction: 26,
1966
+ mass: 1,
1967
+ precision: 0.01
1968
+ });
1969
+ springRef.current.setTarget(targetPercent);
1970
+ PhysicsEngine.register(physicsIdRef.current, (dt) => {
1971
+ if (!springRef.current) return;
1972
+ const result = springRef.current.step(dt);
1973
+ s2.displayPercent = result.value;
1974
+ rerender();
1975
+ if (result.isSettled) {
1976
+ PhysicsEngine.unregister(physicsIdRef.current);
1977
+ }
1978
+ });
1979
+ }, [config.springTension, rerender]);
1980
+ const percentToValue = React.useCallback((percent) => {
1981
+ const raw = min + percent / 100 * (max - min);
1982
+ return Math.round(raw / step) * step;
1983
+ }, [min, max, step]);
1984
+ const animateScaleTo = React.useCallback((targetScale) => {
1985
+ const s2 = state.current;
1986
+ PhysicsEngine.unregister(scaleIdRef.current);
1987
+ scaleSpringRef.current = new Spring1D(s2.thumbScale, {
1988
+ tension: 400,
1989
+ friction: 20,
1990
+ mass: 0.6,
1991
+ precision: 1e-3
1992
+ });
1993
+ scaleSpringRef.current.setTarget(targetScale);
1994
+ PhysicsEngine.register(scaleIdRef.current, (dt) => {
1995
+ if (!scaleSpringRef.current) return;
1996
+ const result = scaleSpringRef.current.step(dt);
1997
+ s2.thumbScale = result.value;
1998
+ rerender();
1999
+ if (result.isSettled) {
2000
+ PhysicsEngine.unregister(scaleIdRef.current);
2001
+ }
2002
+ });
2003
+ }, [rerender]);
2004
+ const getMomentumDecay = React.useCallback((flickMs) => {
2005
+ const clamped = Math.max(100, Math.min(2e3, flickMs));
2006
+ const seconds = clamped / 1e3;
2007
+ const targetRatio = 0.05;
2008
+ return Math.exp(Math.log(targetRatio) / (seconds * 60));
2009
+ }, []);
2010
+ const animateStretchRelax = React.useCallback(() => {
2011
+ const s2 = state.current;
2012
+ PhysicsEngine.unregister(stretchXIdRef.current);
2013
+ stretchXSpringRef.current = new Spring1D(s2.stretchX, {
2014
+ tension: 300,
2015
+ friction: 14,
2016
+ mass: 0.5,
2017
+ precision: 1e-3
2018
+ });
2019
+ stretchXSpringRef.current.setTarget(1);
2020
+ PhysicsEngine.register(stretchXIdRef.current, (dt) => {
2021
+ if (!stretchXSpringRef.current) return;
2022
+ const result = stretchXSpringRef.current.step(dt);
2023
+ s2.stretchX = result.value;
2024
+ rerender();
2025
+ if (result.isSettled) {
2026
+ s2.stretchX = 1;
2027
+ PhysicsEngine.unregister(stretchXIdRef.current);
2028
+ }
2029
+ });
2030
+ PhysicsEngine.unregister(stretchYIdRef.current);
2031
+ stretchYSpringRef.current = new Spring1D(s2.stretchY, {
2032
+ tension: 300,
2033
+ friction: 14,
2034
+ mass: 0.5,
2035
+ precision: 1e-3
2036
+ });
2037
+ stretchYSpringRef.current.setTarget(1);
2038
+ PhysicsEngine.register(stretchYIdRef.current, (dt) => {
2039
+ if (!stretchYSpringRef.current) return;
2040
+ const result = stretchYSpringRef.current.step(dt);
2041
+ s2.stretchY = result.value;
2042
+ rerender();
2043
+ if (result.isSettled) {
2044
+ s2.stretchY = 1;
2045
+ PhysicsEngine.unregister(stretchYIdRef.current);
2046
+ }
2047
+ });
2048
+ PhysicsEngine.unregister(shadowIdRef.current);
2049
+ shadowSpringRef.current = new Spring1D(s2.shadowY, {
2050
+ tension: 250,
2051
+ friction: 20,
2052
+ mass: 0.5,
2053
+ precision: 0.01
2054
+ });
2055
+ shadowSpringRef.current.setTarget(2);
2056
+ PhysicsEngine.register(shadowIdRef.current, (dt) => {
2057
+ if (!shadowSpringRef.current) return;
2058
+ const result = shadowSpringRef.current.step(dt);
2059
+ const t = (result.value - 2) / 10;
2060
+ s2.shadowY = result.value;
2061
+ s2.shadowBlur = 6 + t * 4;
2062
+ s2.shadowOpacity = 0.12 + t * 0.03;
2063
+ rerender();
2064
+ if (result.isSettled) {
2065
+ s2.shadowY = 2;
2066
+ s2.shadowBlur = 6;
2067
+ s2.shadowOpacity = 0.12;
2068
+ PhysicsEngine.unregister(shadowIdRef.current);
2069
+ }
2070
+ });
2071
+ PhysicsEngine.unregister(stretchOriginIdRef.current);
2072
+ stretchOriginSpringRef.current = new Spring1D(s2.stretchOriginPercent, {
2073
+ tension: 300,
2074
+ friction: 18,
2075
+ mass: 0.5,
2076
+ precision: 0.1
2077
+ });
2078
+ stretchOriginSpringRef.current.setTarget(50);
2079
+ PhysicsEngine.register(stretchOriginIdRef.current, (dt) => {
2080
+ if (!stretchOriginSpringRef.current) return;
2081
+ const result = stretchOriginSpringRef.current.step(dt);
2082
+ s2.stretchOriginPercent = result.value;
2083
+ s2.targetOriginPercent = 50;
2084
+ rerender();
2085
+ if (result.isSettled) {
2086
+ s2.stretchOriginPercent = 50;
2087
+ s2.targetOriginPercent = 50;
2088
+ PhysicsEngine.unregister(stretchOriginIdRef.current);
2089
+ }
2090
+ });
2091
+ }, [rerender]);
2092
+ const handlePointerDown = React.useCallback((e) => {
2093
+ if (!trackRef.current) return;
2094
+ const s2 = state.current;
2095
+ e.preventDefault();
2096
+ e.target.setPointerCapture(e.pointerId);
2097
+ s2.pointerStart = { x: e.clientX, y: e.clientY };
2098
+ s2.isDragging = true;
2099
+ PhysicsEngine.unregister(physicsIdRef.current);
2100
+ PhysicsEngine.unregister(verticalIdRef.current);
2101
+ PhysicsEngine.unregister(stretchXIdRef.current);
2102
+ PhysicsEngine.unregister(stretchYIdRef.current);
2103
+ PhysicsEngine.unregister(stretchOriginIdRef.current);
2104
+ PhysicsEngine.unregister(shadowIdRef.current);
2105
+ velocityTracker.current.reset();
2106
+ animateScaleTo(config.liftScale);
2107
+ const rect = trackRef.current.getBoundingClientRect();
2108
+ const x = Math.max(0, Math.min(rect.width, e.clientX - rect.left));
2109
+ const percent = x / rect.width * 100;
2110
+ s2.displayPercent = percent;
2111
+ s2.lastPercent = percent;
2112
+ velocityTracker.current.addSample(percent);
2113
+ onChange(percentToValue(percent));
2114
+ PhysicsEngine.register(dragDecayIdRef.current, () => {
2115
+ if (!s2.isDragging) return;
2116
+ const decayLerp = 0.08;
2117
+ const threshold = 1e-3;
2118
+ if (Math.abs(s2.stretchX - 1) > threshold) {
2119
+ s2.stretchX += (1 - s2.stretchX) * decayLerp;
2120
+ }
2121
+ if (Math.abs(s2.stretchY - 1) > threshold) {
2122
+ s2.stretchY += (1 - s2.stretchY) * decayLerp;
2123
+ }
2124
+ if (Math.abs(s2.stretchOriginPercent - 50) > threshold) {
2125
+ s2.stretchOriginPercent += (50 - s2.stretchOriginPercent) * decayLerp;
2126
+ }
2127
+ s2.shadowY += (2 - s2.shadowY) * decayLerp;
2128
+ s2.shadowBlur += (6 - s2.shadowBlur) * decayLerp;
2129
+ s2.shadowOpacity += (0.12 - s2.shadowOpacity) * decayLerp;
2130
+ rerender();
2131
+ });
2132
+ rerender();
2133
+ }, [onChange, percentToValue, animateScaleTo, config.liftScale, rerender]);
2134
+ const handlePointerMove = React.useCallback((e) => {
2135
+ const s2 = state.current;
2136
+ if (!s2.isDragging || !trackRef.current || !s2.pointerStart) return;
2137
+ const rect = trackRef.current.getBoundingClientRect();
2138
+ const rawX = e.clientX - rect.left;
2139
+ const rawPercent = rawX / rect.width * 100;
2140
+ let percent = rawPercent;
2141
+ let edgeStretchH = 0;
2142
+ if (rawPercent < 0) {
2143
+ edgeStretchH = -rawPercent / 100;
2144
+ percent = rawPercent * 0.15;
2145
+ s2.targetOriginPercent = 100;
2146
+ } else if (rawPercent > 100) {
2147
+ edgeStretchH = (rawPercent - 100) / 100;
2148
+ percent = 100 + (rawPercent - 100) * 0.15;
2149
+ s2.targetOriginPercent = 0;
2150
+ }
2151
+ const velocity = percent - s2.lastPercent;
2152
+ s2.lastPercent = percent;
2153
+ s2.currentVelocity = velocity;
2154
+ if (Math.abs(velocity) > 0.3 && edgeStretchH === 0) {
2155
+ s2.targetOriginPercent = velocity > 0 ? 100 : 0;
2156
+ }
2157
+ const originLerpSpeed = 0.15;
2158
+ s2.stretchOriginPercent += (s2.targetOriginPercent - s2.stretchOriginPercent) * originLerpSpeed;
2159
+ const rawVerticalOffset = e.clientY - s2.pointerStart.y;
2160
+ const rubberBandedOffset = rawVerticalOffset * 0.06;
2161
+ s2.verticalOffset = Math.max(-5, Math.min(5, rubberBandedOffset));
2162
+ const absVelocity = Math.abs(velocity);
2163
+ const velStretch = Math.min(absVelocity * config.velocityStretch * 15, 1.2);
2164
+ const edgeStretchFactorH = Math.min(Math.abs(edgeStretchH) * 0.6, 0.12);
2165
+ const edgeStretchFactorV = Math.abs(s2.verticalOffset) / 5 * 0.06;
2166
+ const targetStretchX = 1 + velStretch + edgeStretchFactorH;
2167
+ const targetStretchY = 1 - velStretch * 0.4 + edgeStretchFactorV;
2168
+ const baseLerp = 0.35;
2169
+ const decayLerp = 0.15;
2170
+ const velocityThreshold = 0.5;
2171
+ if (absVelocity < velocityThreshold) {
2172
+ const decayFactor = 1 - absVelocity / velocityThreshold;
2173
+ s2.stretchX += (1 - s2.stretchX) * decayLerp * decayFactor;
2174
+ s2.stretchY += (1 - s2.stretchY) * decayLerp * decayFactor;
2175
+ s2.stretchOriginPercent += (50 - s2.stretchOriginPercent) * decayLerp * decayFactor;
2176
+ }
2177
+ s2.stretchX += (targetStretchX - s2.stretchX) * baseLerp;
2178
+ s2.stretchY += (targetStretchY - s2.stretchY) * baseLerp;
2179
+ const targetShadowY = 2 + absVelocity * 0.4;
2180
+ const targetShadowBlur = 6 + absVelocity * 0.6;
2181
+ const targetShadowOpacity = 0.12 + absVelocity * 0.015;
2182
+ const shadowLerp = 0.3;
2183
+ s2.shadowY += (targetShadowY - s2.shadowY) * shadowLerp;
2184
+ s2.shadowBlur += (targetShadowBlur - s2.shadowBlur) * shadowLerp;
2185
+ s2.shadowOpacity += (targetShadowOpacity - s2.shadowOpacity) * shadowLerp;
2186
+ s2.displayPercent = Math.max(0, Math.min(100, percent));
2187
+ velocityTracker.current.addSample(percent);
2188
+ onChange(percentToValue(Math.max(0, Math.min(100, percent))));
2189
+ rerender();
2190
+ }, [onChange, percentToValue, config.velocityStretch, rerender]);
2191
+ const handlePointerUp = React.useCallback(() => {
2192
+ const s2 = state.current;
2193
+ if (!s2.isDragging) return;
2194
+ s2.isDragging = false;
2195
+ s2.pointerStart = null;
2196
+ PhysicsEngine.unregister(dragDecayIdRef.current);
2197
+ animateScaleTo(1);
2198
+ animateStretchRelax();
2199
+ if (s2.verticalOffset !== 0) {
2200
+ PhysicsEngine.unregister(verticalIdRef.current);
2201
+ verticalSpringRef.current = new Spring1D(s2.verticalOffset, {
2202
+ tension: 400,
2203
+ friction: 18,
2204
+ mass: 0.5,
2205
+ precision: 0.1
2206
+ });
2207
+ verticalSpringRef.current.setTarget(0);
2208
+ PhysicsEngine.register(verticalIdRef.current, (dt) => {
2209
+ if (!verticalSpringRef.current) return;
2210
+ const result = verticalSpringRef.current.step(dt);
2211
+ s2.verticalOffset = result.value;
2212
+ rerender();
2213
+ if (result.isSettled) {
2214
+ s2.verticalOffset = 0;
2215
+ PhysicsEngine.unregister(verticalIdRef.current);
2216
+ }
2217
+ });
2218
+ }
2219
+ const velocity = velocityTracker.current.getVelocity();
2220
+ velocityTracker.current.reset();
2221
+ if (Math.abs(velocity) > 30) {
2222
+ const decay = getMomentumDecay(config.flickMomentum);
2223
+ momentumRef.current = new Momentum1D(s2.displayPercent, {
2224
+ decay,
2225
+ stopThreshold: 0.3,
2226
+ maxVelocity: config.maxVelocity * 100
2227
+ });
2228
+ momentumRef.current.start(s2.displayPercent, velocity * 0.4);
2229
+ PhysicsEngine.register(physicsIdRef.current, (dt) => {
2230
+ if (!momentumRef.current) return;
2231
+ const result = momentumRef.current.step(dt);
2232
+ const momentumVel = Math.abs(result.velocity || 0) / 100;
2233
+ if (momentumVel > 0.1) {
2234
+ const stretch = Math.min(momentumVel * config.velocityStretch * 5, 0.8);
2235
+ s2.stretchX = 1 + stretch;
2236
+ s2.stretchY = 1 - stretch * 0.5;
2237
+ s2.targetOriginPercent = (result.velocity || 0) > 0 ? 100 : 0;
2238
+ s2.stretchOriginPercent += (s2.targetOriginPercent - s2.stretchOriginPercent) * 0.1;
2239
+ }
2240
+ let newPercent = Math.max(0, Math.min(100, result.position));
2241
+ if (result.position < 0 || result.position > 100) {
2242
+ momentumRef.current.stop();
2243
+ const targetPercent = result.position < 0 ? 0 : 100;
2244
+ animateStretchRelax();
2245
+ springRef.current = new Spring1D(newPercent, {
2246
+ tension: config.springTension * 400,
2247
+ friction: 26
2248
+ });
2249
+ springRef.current.setTarget(targetPercent);
2250
+ PhysicsEngine.register(physicsIdRef.current, (dt2) => {
2251
+ if (!springRef.current) return;
2252
+ const springResult = springRef.current.step(dt2);
2253
+ s2.displayPercent = springResult.value;
2254
+ onChange(percentToValue(springResult.value));
2255
+ rerender();
2256
+ if (springResult.isSettled) {
2257
+ PhysicsEngine.unregister(physicsIdRef.current);
2258
+ }
2259
+ });
2260
+ return;
2261
+ }
2262
+ s2.displayPercent = newPercent;
2263
+ onChange(percentToValue(newPercent));
2264
+ rerender();
2265
+ if (!result.isActive) {
2266
+ animateStretchRelax();
2267
+ PhysicsEngine.unregister(physicsIdRef.current);
2268
+ }
2269
+ });
2270
+ }
2271
+ rerender();
2272
+ }, [config, onChange, percentToValue, animateScaleTo, animateStretchRelax, getMomentumDecay, rerender]);
2273
+ const handleTrackClick = React.useCallback((e) => {
2274
+ const s2 = state.current;
2275
+ if (!trackRef.current || s2.isDragging) return;
2276
+ const rect = trackRef.current.getBoundingClientRect();
2277
+ const x = e.clientX - rect.left;
2278
+ const percent = Math.max(0, Math.min(100, x / rect.width * 100));
2279
+ onChange(percentToValue(percent));
2280
+ animateToPercent(percent);
2281
+ }, [onChange, percentToValue, animateToPercent]);
2282
+ React.useEffect(() => {
2283
+ return () => {
2284
+ PhysicsEngine.unregister(physicsIdRef.current);
2285
+ PhysicsEngine.unregister(scaleIdRef.current);
2286
+ PhysicsEngine.unregister(verticalIdRef.current);
2287
+ PhysicsEngine.unregister(stretchXIdRef.current);
2288
+ PhysicsEngine.unregister(stretchYIdRef.current);
2289
+ PhysicsEngine.unregister(stretchOriginIdRef.current);
2290
+ PhysicsEngine.unregister(shadowIdRef.current);
2291
+ PhysicsEngine.unregister(dragDecayIdRef.current);
2292
+ };
2293
+ }, []);
2294
+ const s = state.current;
2295
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `tekiyo-slider-container ${className}`, children: [
2296
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "tekiyo-slider-header", children: [
2297
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "tekiyo-slider-label-group", children: [
2298
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tekiyo-slider-label", children: label }),
2299
+ description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tekiyo-slider-description", children: description })
2300
+ ] }),
2301
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tekiyo-slider-value", children: value.toFixed(decimals) })
2302
+ ] }),
2303
+ /* @__PURE__ */ jsxRuntime.jsxs(
2304
+ "div",
2305
+ {
2306
+ className: "tekiyo-slider-track",
2307
+ ref: trackRef,
2308
+ onClick: handleTrackClick,
2309
+ children: [
2310
+ /* @__PURE__ */ jsxRuntime.jsx(
2311
+ "div",
2312
+ {
2313
+ className: "tekiyo-slider-fill",
2314
+ style: { width: `${s.displayPercent}%` }
2315
+ }
2316
+ ),
2317
+ /* @__PURE__ */ jsxRuntime.jsx(
2318
+ "div",
2319
+ {
2320
+ ref: thumbRef,
2321
+ className: `tekiyo-slider-thumb ${s.isDragging ? "active" : ""}`,
2322
+ style: {
2323
+ left: `${s.displayPercent}%`,
2324
+ transform: `translate(-50%, calc(-50% + ${s.verticalOffset}px)) scale(${s.thumbScale}) scaleX(${s.stretchX}) scaleY(${s.stretchY})`,
2325
+ transformOrigin: `${s.stretchOriginPercent}% center`,
2326
+ boxShadow: `0 ${s.shadowY}px ${s.shadowBlur}px rgba(0, 0, 0, ${s.shadowOpacity})`
2327
+ },
2328
+ onPointerDown: handlePointerDown,
2329
+ onPointerMove: handlePointerMove,
2330
+ onPointerUp: handlePointerUp,
2331
+ onPointerCancel: handlePointerUp
2332
+ }
2333
+ )
2334
+ ]
2335
+ }
2336
+ )
2337
+ ] });
2338
+ }
2339
+ function createThumbState(percent) {
2340
+ return {
2341
+ displayPercent: percent,
2342
+ thumbScale: 1,
2343
+ verticalOffset: 0,
2344
+ stretchX: 1,
2345
+ stretchY: 1,
2346
+ stretchOriginPercent: 50,
2347
+ targetOriginPercent: 50,
2348
+ shadowY: 2,
2349
+ shadowBlur: 6,
2350
+ shadowOpacity: 0.12,
2351
+ lastPercent: percent,
2352
+ isDragging: false
2353
+ };
2354
+ }
2355
+ function PhysicsRangeSlider({
2356
+ label,
2357
+ description,
2358
+ minValue,
2359
+ maxValue,
2360
+ min,
2361
+ max,
2362
+ step,
2363
+ config,
2364
+ onChange,
2365
+ formatValue = (v) => v.toFixed(0),
2366
+ className = ""
2367
+ }) {
2368
+ const trackRef = React.useRef(null);
2369
+ const minPercent = (minValue - min) / (max - min) * 100;
2370
+ const maxPercent = (maxValue - min) / (max - min) * 100;
2371
+ const minThumb = React.useRef(createThumbState(minPercent));
2372
+ const maxThumb = React.useRef(createThumbState(maxPercent));
2373
+ const activeThumb = React.useRef(null);
2374
+ const pointerStart = React.useRef(null);
2375
+ const [, forceRender] = React.useState(0);
2376
+ const rerender = React.useCallback(() => forceRender((n) => n + 1), []);
2377
+ const minScaleId = React.useRef(generatePhysicsId("rangeMinScale"));
2378
+ const maxScaleId = React.useRef(generatePhysicsId("rangeMaxScale"));
2379
+ const minDecayId = React.useRef(generatePhysicsId("rangeMinDecay"));
2380
+ const maxDecayId = React.useRef(generatePhysicsId("rangeMaxDecay"));
2381
+ const minRelaxId = React.useRef(generatePhysicsId("rangeMinRelax"));
2382
+ const maxRelaxId = React.useRef(generatePhysicsId("rangeMaxRelax"));
2383
+ const minScaleSpring = React.useRef(null);
2384
+ const maxScaleSpring = React.useRef(null);
2385
+ React.useEffect(() => {
2386
+ if (!minThumb.current.isDragging) {
2387
+ minThumb.current.displayPercent = (minValue - min) / (max - min) * 100;
2388
+ }
2389
+ if (!maxThumb.current.isDragging) {
2390
+ maxThumb.current.displayPercent = (maxValue - min) / (max - min) * 100;
2391
+ }
2392
+ rerender();
2393
+ }, [minValue, maxValue, min, max, rerender]);
2394
+ const percentToValue = React.useCallback(
2395
+ (percent) => {
2396
+ const raw = min + percent / 100 * (max - min);
2397
+ return Math.round(raw / step) * step;
2398
+ },
2399
+ [min, max, step]
2400
+ );
2401
+ const animateScaleTo = React.useCallback(
2402
+ (thumb, targetScale) => {
2403
+ const state = thumb === "min" ? minThumb.current : maxThumb.current;
2404
+ const springRef = thumb === "min" ? minScaleSpring : maxScaleSpring;
2405
+ const scaleId = thumb === "min" ? minScaleId : maxScaleId;
2406
+ PhysicsEngine.unregister(scaleId.current);
2407
+ springRef.current = new Spring1D(state.thumbScale, {
2408
+ tension: 400,
2409
+ friction: 20,
2410
+ mass: 0.6,
2411
+ precision: 1e-3
2412
+ });
2413
+ springRef.current.setTarget(targetScale);
2414
+ PhysicsEngine.register(scaleId.current, (dt) => {
2415
+ if (!springRef.current) return;
2416
+ const result = springRef.current.step(dt);
2417
+ state.thumbScale = result.value;
2418
+ rerender();
2419
+ if (result.isSettled) {
2420
+ PhysicsEngine.unregister(scaleId.current);
2421
+ }
2422
+ });
2423
+ },
2424
+ [rerender]
2425
+ );
2426
+ const animateStretchRelax = React.useCallback(
2427
+ (thumb) => {
2428
+ const state = thumb === "min" ? minThumb.current : maxThumb.current;
2429
+ const relaxId = thumb === "min" ? minRelaxId : maxRelaxId;
2430
+ PhysicsEngine.unregister(relaxId.current);
2431
+ const spring = new Spring1D(state.stretchX, {
2432
+ tension: 300,
2433
+ friction: 14,
2434
+ mass: 0.5,
2435
+ precision: 1e-3
2436
+ });
2437
+ spring.setTarget(1);
2438
+ PhysicsEngine.register(relaxId.current, (dt) => {
2439
+ const result = spring.step(dt);
2440
+ state.stretchX = result.value;
2441
+ state.stretchY = 1 - (result.value - 1) * 0.4;
2442
+ state.stretchOriginPercent += (50 - state.stretchOriginPercent) * 0.15;
2443
+ state.shadowY += (2 - state.shadowY) * 0.15;
2444
+ state.shadowBlur += (6 - state.shadowBlur) * 0.15;
2445
+ state.shadowOpacity += (0.12 - state.shadowOpacity) * 0.15;
2446
+ rerender();
2447
+ if (result.isSettled) {
2448
+ state.stretchX = 1;
2449
+ state.stretchY = 1;
2450
+ state.stretchOriginPercent = 50;
2451
+ PhysicsEngine.unregister(relaxId.current);
2452
+ }
2453
+ });
2454
+ },
2455
+ [rerender]
2456
+ );
2457
+ const handlePointerDown = React.useCallback(
2458
+ (e, thumb) => {
2459
+ if (!trackRef.current) return;
2460
+ const state = thumb === "min" ? minThumb.current : maxThumb.current;
2461
+ const decayId = thumb === "min" ? minDecayId : maxDecayId;
2462
+ e.preventDefault();
2463
+ e.stopPropagation();
2464
+ e.target.setPointerCapture(e.pointerId);
2465
+ pointerStart.current = { x: e.clientX, y: e.clientY };
2466
+ activeThumb.current = thumb;
2467
+ state.isDragging = true;
2468
+ animateScaleTo(thumb, config.liftScale);
2469
+ PhysicsEngine.register(decayId.current, () => {
2470
+ if (!state.isDragging) return;
2471
+ const decayLerp = 0.08;
2472
+ const threshold = 1e-3;
2473
+ if (Math.abs(state.stretchX - 1) > threshold) {
2474
+ state.stretchX += (1 - state.stretchX) * decayLerp;
2475
+ }
2476
+ if (Math.abs(state.stretchY - 1) > threshold) {
2477
+ state.stretchY += (1 - state.stretchY) * decayLerp;
2478
+ }
2479
+ if (Math.abs(state.stretchOriginPercent - 50) > threshold) {
2480
+ state.stretchOriginPercent += (50 - state.stretchOriginPercent) * decayLerp;
2481
+ }
2482
+ state.shadowY += (2 - state.shadowY) * decayLerp;
2483
+ state.shadowBlur += (6 - state.shadowBlur) * decayLerp;
2484
+ state.shadowOpacity += (0.12 - state.shadowOpacity) * decayLerp;
2485
+ rerender();
2486
+ });
2487
+ rerender();
2488
+ },
2489
+ [config.liftScale, animateScaleTo, rerender]
2490
+ );
2491
+ const handlePointerMove = React.useCallback(
2492
+ (e) => {
2493
+ if (!activeThumb.current || !trackRef.current || !pointerStart.current) return;
2494
+ const thumb = activeThumb.current;
2495
+ const state = thumb === "min" ? minThumb.current : maxThumb.current;
2496
+ const rect = trackRef.current.getBoundingClientRect();
2497
+ const rawX = e.clientX - rect.left;
2498
+ let percent = rawX / rect.width * 100;
2499
+ if (thumb === "min") {
2500
+ percent = Math.max(0, Math.min(maxThumb.current.displayPercent - 5, percent));
2501
+ } else {
2502
+ percent = Math.min(100, Math.max(minThumb.current.displayPercent + 5, percent));
2503
+ }
2504
+ const velocity = percent - state.lastPercent;
2505
+ state.lastPercent = percent;
2506
+ if (Math.abs(velocity) > 0.3) {
2507
+ state.targetOriginPercent = velocity > 0 ? 100 : 0;
2508
+ }
2509
+ state.stretchOriginPercent += (state.targetOriginPercent - state.stretchOriginPercent) * 0.15;
2510
+ const absVelocity = Math.abs(velocity);
2511
+ const velStretch = Math.min(absVelocity * config.velocityStretch * 15, 1.2);
2512
+ const targetStretchX = 1 + velStretch;
2513
+ const targetStretchY = 1 - velStretch * 0.4;
2514
+ const baseLerp = 0.35;
2515
+ const decayLerp = 0.15;
2516
+ const velocityThreshold = 0.5;
2517
+ if (absVelocity < velocityThreshold) {
2518
+ const decayFactor = 1 - absVelocity / velocityThreshold;
2519
+ state.stretchX += (1 - state.stretchX) * decayLerp * decayFactor;
2520
+ state.stretchY += (1 - state.stretchY) * decayLerp * decayFactor;
2521
+ state.stretchOriginPercent += (50 - state.stretchOriginPercent) * decayLerp * decayFactor;
2522
+ }
2523
+ state.stretchX += (targetStretchX - state.stretchX) * baseLerp;
2524
+ state.stretchY += (targetStretchY - state.stretchY) * baseLerp;
2525
+ const targetShadowY = 2 + absVelocity * 0.4;
2526
+ const targetShadowBlur = 6 + absVelocity * 0.6;
2527
+ const targetShadowOpacity = 0.12 + absVelocity * 0.015;
2528
+ state.shadowY += (targetShadowY - state.shadowY) * 0.3;
2529
+ state.shadowBlur += (targetShadowBlur - state.shadowBlur) * 0.3;
2530
+ state.shadowOpacity += (targetShadowOpacity - state.shadowOpacity) * 0.3;
2531
+ state.displayPercent = percent;
2532
+ const newMin = thumb === "min" ? percentToValue(percent) : minValue;
2533
+ const newMax = thumb === "max" ? percentToValue(percent) : maxValue;
2534
+ onChange(newMin, newMax);
2535
+ rerender();
2536
+ },
2537
+ [config.velocityStretch, minValue, maxValue, percentToValue, onChange, rerender]
2538
+ );
2539
+ const handlePointerUp = React.useCallback(() => {
2540
+ if (!activeThumb.current) return;
2541
+ const thumb = activeThumb.current;
2542
+ const state = thumb === "min" ? minThumb.current : maxThumb.current;
2543
+ const decayId = thumb === "min" ? minDecayId : maxDecayId;
2544
+ state.isDragging = false;
2545
+ pointerStart.current = null;
2546
+ activeThumb.current = null;
2547
+ PhysicsEngine.unregister(decayId.current);
2548
+ animateScaleTo(thumb, 1);
2549
+ animateStretchRelax(thumb);
2550
+ rerender();
2551
+ }, [animateScaleTo, animateStretchRelax, rerender]);
2552
+ React.useEffect(() => {
2553
+ return () => {
2554
+ PhysicsEngine.unregister(minScaleId.current);
2555
+ PhysicsEngine.unregister(maxScaleId.current);
2556
+ PhysicsEngine.unregister(minDecayId.current);
2557
+ PhysicsEngine.unregister(maxDecayId.current);
2558
+ PhysicsEngine.unregister(minRelaxId.current);
2559
+ PhysicsEngine.unregister(maxRelaxId.current);
2560
+ };
2561
+ }, []);
2562
+ const minS = minThumb.current;
2563
+ const maxS = maxThumb.current;
2564
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `tekiyo-range-slider ${className}`, children: [
2565
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "tekiyo-slider-header", children: [
2566
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "tekiyo-slider-label-group", children: [
2567
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tekiyo-slider-label", children: label }),
2568
+ description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tekiyo-slider-description", children: description })
2569
+ ] }),
2570
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tekiyo-slider-value", children: [
2571
+ formatValue(minValue),
2572
+ " - ",
2573
+ formatValue(maxValue)
2574
+ ] })
2575
+ ] }),
2576
+ /* @__PURE__ */ jsxRuntime.jsxs(
2577
+ "div",
2578
+ {
2579
+ className: "tekiyo-slider-track",
2580
+ ref: trackRef,
2581
+ onPointerMove: handlePointerMove,
2582
+ onPointerUp: handlePointerUp,
2583
+ onPointerCancel: handlePointerUp,
2584
+ children: [
2585
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tekiyo-slider-track-bg" }),
2586
+ /* @__PURE__ */ jsxRuntime.jsx(
2587
+ "div",
2588
+ {
2589
+ className: "tekiyo-range-fill",
2590
+ style: {
2591
+ left: `${minS.displayPercent}%`,
2592
+ width: `${maxS.displayPercent - minS.displayPercent}%`
2593
+ }
2594
+ }
2595
+ ),
2596
+ /* @__PURE__ */ jsxRuntime.jsx(
2597
+ "div",
2598
+ {
2599
+ className: `tekiyo-slider-thumb tekiyo-range-thumb-min ${minS.isDragging ? "active" : ""}`,
2600
+ style: {
2601
+ left: `${minS.displayPercent}%`,
2602
+ transform: `translate(-50%, calc(-50% + ${minS.verticalOffset}px)) scale(${minS.thumbScale}) scaleX(${minS.stretchX}) scaleY(${minS.stretchY})`,
2603
+ transformOrigin: `${minS.stretchOriginPercent}% center`,
2604
+ boxShadow: `0 ${minS.shadowY}px ${minS.shadowBlur}px rgba(0, 0, 0, ${minS.shadowOpacity})`
2605
+ },
2606
+ onPointerDown: (e) => handlePointerDown(e, "min")
2607
+ }
2608
+ ),
2609
+ /* @__PURE__ */ jsxRuntime.jsx(
2610
+ "div",
2611
+ {
2612
+ className: `tekiyo-slider-thumb tekiyo-range-thumb-max ${maxS.isDragging ? "active" : ""}`,
2613
+ style: {
2614
+ left: `${maxS.displayPercent}%`,
2615
+ transform: `translate(-50%, calc(-50% + ${maxS.verticalOffset}px)) scale(${maxS.thumbScale}) scaleX(${maxS.stretchX}) scaleY(${maxS.stretchY})`,
2616
+ transformOrigin: `${maxS.stretchOriginPercent}% center`,
2617
+ boxShadow: `0 ${maxS.shadowY}px ${maxS.shadowBlur}px rgba(0, 0, 0, ${maxS.shadowOpacity})`
2618
+ },
2619
+ onPointerDown: (e) => handlePointerDown(e, "max")
2620
+ }
2621
+ )
2622
+ ]
2623
+ }
2624
+ )
2625
+ ] });
2626
+ }
2627
+ function PhysicsStepSlider({
2628
+ label,
2629
+ description,
2630
+ options,
2631
+ value,
2632
+ config,
2633
+ onChange,
2634
+ className = ""
2635
+ }) {
2636
+ const trackRef = React.useRef(null);
2637
+ const currentIndex = options.findIndex((opt) => opt.value === value);
2638
+ const stepPercent = 100 / (options.length - 1);
2639
+ const targetPercent = currentIndex * stepPercent;
2640
+ const state = React.useRef({
2641
+ isDragging: false,
2642
+ displayPercent: targetPercent,
2643
+ thumbScale: 1,
2644
+ verticalOffset: 0,
2645
+ stretchX: 1,
2646
+ stretchY: 1,
2647
+ stretchOriginPercent: 50,
2648
+ targetOriginPercent: 50,
2649
+ shadowY: 2,
2650
+ shadowBlur: 6,
2651
+ shadowOpacity: 0.12,
2652
+ lastPercent: targetPercent,
2653
+ pointerStart: null
2654
+ });
2655
+ const [, forceRender] = React.useState(0);
2656
+ const rerender = React.useCallback(() => forceRender((n) => n + 1), []);
2657
+ const scaleIdRef = React.useRef(generatePhysicsId("stepScale"));
2658
+ const snapIdRef = React.useRef(generatePhysicsId("stepSnap"));
2659
+ const decayIdRef = React.useRef(generatePhysicsId("stepDecay"));
2660
+ const relaxIdRef = React.useRef(generatePhysicsId("stepRelax"));
2661
+ const scaleSpring = React.useRef(null);
2662
+ const snapSpring = React.useRef(null);
2663
+ React.useEffect(() => {
2664
+ if (!state.current.isDragging) {
2665
+ const idx = options.findIndex((opt) => opt.value === value);
2666
+ const percent = idx * stepPercent;
2667
+ animateToPercent(percent);
2668
+ }
2669
+ }, [value, options, stepPercent]);
2670
+ const animateToPercent = React.useCallback(
2671
+ (targetPercent2) => {
2672
+ const s2 = state.current;
2673
+ PhysicsEngine.unregister(snapIdRef.current);
2674
+ snapSpring.current = new Spring1D(s2.displayPercent, {
2675
+ tension: config.springTension * 500,
2676
+ friction: 22,
2677
+ mass: 0.8,
2678
+ precision: 0.01
2679
+ });
2680
+ snapSpring.current.setTarget(targetPercent2);
2681
+ PhysicsEngine.register(snapIdRef.current, (dt) => {
2682
+ if (!snapSpring.current) return;
2683
+ const result = snapSpring.current.step(dt);
2684
+ s2.displayPercent = result.value;
2685
+ rerender();
2686
+ if (result.isSettled) {
2687
+ PhysicsEngine.unregister(snapIdRef.current);
2688
+ }
2689
+ });
2690
+ },
2691
+ [config.springTension, rerender]
2692
+ );
2693
+ const animateScaleTo = React.useCallback(
2694
+ (targetScale) => {
2695
+ const s2 = state.current;
2696
+ PhysicsEngine.unregister(scaleIdRef.current);
2697
+ scaleSpring.current = new Spring1D(s2.thumbScale, {
2698
+ tension: 400,
2699
+ friction: 20,
2700
+ mass: 0.6,
2701
+ precision: 1e-3
2702
+ });
2703
+ scaleSpring.current.setTarget(targetScale);
2704
+ PhysicsEngine.register(scaleIdRef.current, (dt) => {
2705
+ if (!scaleSpring.current) return;
2706
+ const result = scaleSpring.current.step(dt);
2707
+ s2.thumbScale = result.value;
2708
+ rerender();
2709
+ if (result.isSettled) {
2710
+ PhysicsEngine.unregister(scaleIdRef.current);
2711
+ }
2712
+ });
2713
+ },
2714
+ [rerender]
2715
+ );
2716
+ const animateStretchRelax = React.useCallback(() => {
2717
+ const s2 = state.current;
2718
+ PhysicsEngine.unregister(relaxIdRef.current);
2719
+ const spring = new Spring1D(s2.stretchX, {
2720
+ tension: 300,
2721
+ friction: 14,
2722
+ mass: 0.5,
2723
+ precision: 1e-3
2724
+ });
2725
+ spring.setTarget(1);
2726
+ PhysicsEngine.register(relaxIdRef.current, (dt) => {
2727
+ const result = spring.step(dt);
2728
+ s2.stretchX = result.value;
2729
+ s2.stretchY = 1 - (result.value - 1) * 0.4;
2730
+ s2.stretchOriginPercent += (50 - s2.stretchOriginPercent) * 0.15;
2731
+ s2.shadowY += (2 - s2.shadowY) * 0.15;
2732
+ s2.shadowBlur += (6 - s2.shadowBlur) * 0.15;
2733
+ s2.shadowOpacity += (0.12 - s2.shadowOpacity) * 0.15;
2734
+ rerender();
2735
+ if (result.isSettled) {
2736
+ s2.stretchX = 1;
2737
+ s2.stretchY = 1;
2738
+ s2.stretchOriginPercent = 50;
2739
+ PhysicsEngine.unregister(relaxIdRef.current);
2740
+ }
2741
+ });
2742
+ }, [rerender]);
2743
+ const getClosestStepIndex = React.useCallback(
2744
+ (percent) => {
2745
+ const stepSize = 100 / (options.length - 1);
2746
+ return Math.round(percent / stepSize);
2747
+ },
2748
+ [options.length]
2749
+ );
2750
+ const handlePointerDown = React.useCallback(
2751
+ (e) => {
2752
+ if (!trackRef.current) return;
2753
+ const s2 = state.current;
2754
+ e.preventDefault();
2755
+ e.target.setPointerCapture(e.pointerId);
2756
+ s2.pointerStart = { x: e.clientX, y: e.clientY };
2757
+ s2.isDragging = true;
2758
+ PhysicsEngine.unregister(snapIdRef.current);
2759
+ animateScaleTo(config.liftScale);
2760
+ PhysicsEngine.register(decayIdRef.current, () => {
2761
+ if (!s2.isDragging) return;
2762
+ const decayLerp = 0.08;
2763
+ const threshold = 1e-3;
2764
+ if (Math.abs(s2.stretchX - 1) > threshold) {
2765
+ s2.stretchX += (1 - s2.stretchX) * decayLerp;
2766
+ }
2767
+ if (Math.abs(s2.stretchY - 1) > threshold) {
2768
+ s2.stretchY += (1 - s2.stretchY) * decayLerp;
2769
+ }
2770
+ if (Math.abs(s2.stretchOriginPercent - 50) > threshold) {
2771
+ s2.stretchOriginPercent += (50 - s2.stretchOriginPercent) * decayLerp;
2772
+ }
2773
+ s2.shadowY += (2 - s2.shadowY) * decayLerp;
2774
+ s2.shadowBlur += (6 - s2.shadowBlur) * decayLerp;
2775
+ s2.shadowOpacity += (0.12 - s2.shadowOpacity) * decayLerp;
2776
+ rerender();
2777
+ });
2778
+ const rect = trackRef.current.getBoundingClientRect();
2779
+ const x = Math.max(0, Math.min(rect.width, e.clientX - rect.left));
2780
+ const percent = x / rect.width * 100;
2781
+ s2.displayPercent = percent;
2782
+ s2.lastPercent = percent;
2783
+ rerender();
2784
+ },
2785
+ [config.liftScale, animateScaleTo, rerender]
2786
+ );
2787
+ const handlePointerMove = React.useCallback(
2788
+ (e) => {
2789
+ const s2 = state.current;
2790
+ if (!s2.isDragging || !trackRef.current || !s2.pointerStart) return;
2791
+ const rect = trackRef.current.getBoundingClientRect();
2792
+ const rawX = e.clientX - rect.left;
2793
+ const percent = Math.max(0, Math.min(100, rawX / rect.width * 100));
2794
+ const velocity = percent - s2.lastPercent;
2795
+ s2.lastPercent = percent;
2796
+ if (Math.abs(velocity) > 0.3) {
2797
+ s2.targetOriginPercent = velocity > 0 ? 100 : 0;
2798
+ }
2799
+ s2.stretchOriginPercent += (s2.targetOriginPercent - s2.stretchOriginPercent) * 0.15;
2800
+ const absVelocity = Math.abs(velocity);
2801
+ const velStretch = Math.min(absVelocity * config.velocityStretch * 15, 1.2);
2802
+ const targetStretchX = 1 + velStretch;
2803
+ const targetStretchY = 1 - velStretch * 0.4;
2804
+ const baseLerp = 0.35;
2805
+ const decayLerp = 0.15;
2806
+ const velocityThreshold = 0.5;
2807
+ if (absVelocity < velocityThreshold) {
2808
+ const decayFactor = 1 - absVelocity / velocityThreshold;
2809
+ s2.stretchX += (1 - s2.stretchX) * decayLerp * decayFactor;
2810
+ s2.stretchY += (1 - s2.stretchY) * decayLerp * decayFactor;
2811
+ }
2812
+ s2.stretchX += (targetStretchX - s2.stretchX) * baseLerp;
2813
+ s2.stretchY += (targetStretchY - s2.stretchY) * baseLerp;
2814
+ const targetShadowY = 2 + absVelocity * 0.4;
2815
+ const targetShadowBlur = 6 + absVelocity * 0.6;
2816
+ const targetShadowOpacity = 0.12 + absVelocity * 0.015;
2817
+ s2.shadowY += (targetShadowY - s2.shadowY) * 0.3;
2818
+ s2.shadowBlur += (targetShadowBlur - s2.shadowBlur) * 0.3;
2819
+ s2.shadowOpacity += (targetShadowOpacity - s2.shadowOpacity) * 0.3;
2820
+ s2.displayPercent = percent;
2821
+ rerender();
2822
+ },
2823
+ [config.velocityStretch, rerender]
2824
+ );
2825
+ const handlePointerUp = React.useCallback(() => {
2826
+ var _a;
2827
+ const s2 = state.current;
2828
+ if (!s2.isDragging) return;
2829
+ s2.isDragging = false;
2830
+ s2.pointerStart = null;
2831
+ PhysicsEngine.unregister(decayIdRef.current);
2832
+ animateScaleTo(1);
2833
+ animateStretchRelax();
2834
+ const closestIndex = getClosestStepIndex(s2.displayPercent);
2835
+ const snapPercent = closestIndex * stepPercent;
2836
+ animateToPercent(snapPercent);
2837
+ const newValue = (_a = options[closestIndex]) == null ? void 0 : _a.value;
2838
+ if (newValue !== void 0 && newValue !== value) {
2839
+ onChange(newValue);
2840
+ }
2841
+ rerender();
2842
+ }, [animateScaleTo, animateStretchRelax, animateToPercent, getClosestStepIndex, stepPercent, options, value, onChange, rerender]);
2843
+ const handleStepClick = React.useCallback(
2844
+ (index) => {
2845
+ if (state.current.isDragging) return;
2846
+ const newValue = options[index].value;
2847
+ if (newValue !== value) {
2848
+ onChange(newValue);
2849
+ }
2850
+ },
2851
+ [options, value, onChange]
2852
+ );
2853
+ React.useEffect(() => {
2854
+ return () => {
2855
+ PhysicsEngine.unregister(scaleIdRef.current);
2856
+ PhysicsEngine.unregister(snapIdRef.current);
2857
+ PhysicsEngine.unregister(decayIdRef.current);
2858
+ PhysicsEngine.unregister(relaxIdRef.current);
2859
+ };
2860
+ }, []);
2861
+ const s = state.current;
2862
+ const currentOption = options[currentIndex];
2863
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `tekiyo-step-slider ${className}`, children: [
2864
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "tekiyo-slider-header", children: [
2865
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "tekiyo-slider-label-group", children: [
2866
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tekiyo-slider-label", children: label }),
2867
+ description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tekiyo-slider-description", children: description })
2868
+ ] }),
2869
+ /* @__PURE__ */ jsxRuntime.jsx(
2870
+ "span",
2871
+ {
2872
+ className: "tekiyo-step-value",
2873
+ style: { color: currentOption == null ? void 0 : currentOption.color },
2874
+ children: currentOption == null ? void 0 : currentOption.label
2875
+ }
2876
+ )
2877
+ ] }),
2878
+ /* @__PURE__ */ jsxRuntime.jsxs(
2879
+ "div",
2880
+ {
2881
+ className: "tekiyo-step-track",
2882
+ ref: trackRef,
2883
+ onPointerMove: handlePointerMove,
2884
+ onPointerUp: handlePointerUp,
2885
+ onPointerCancel: handlePointerUp,
2886
+ children: [
2887
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tekiyo-step-track-bg" }),
2888
+ /* @__PURE__ */ jsxRuntime.jsx(
2889
+ "div",
2890
+ {
2891
+ className: "tekiyo-step-track-fill",
2892
+ style: {
2893
+ width: `${s.displayPercent}%`,
2894
+ background: (currentOption == null ? void 0 : currentOption.color) || "#007aff"
2895
+ }
2896
+ }
2897
+ ),
2898
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tekiyo-step-dots", children: options.map((option, index) => {
2899
+ const dotPercent = index * stepPercent;
2900
+ const isActive = s.displayPercent >= dotPercent - 1;
2901
+ return /* @__PURE__ */ jsxRuntime.jsx(
2902
+ "div",
2903
+ {
2904
+ className: `tekiyo-step-dot ${isActive ? "active" : ""}`,
2905
+ style: {
2906
+ left: `${dotPercent}%`,
2907
+ background: isActive ? option.color || "#007aff" : void 0
2908
+ },
2909
+ onClick: () => handleStepClick(index)
2910
+ },
2911
+ option.value
2912
+ );
2913
+ }) }),
2914
+ /* @__PURE__ */ jsxRuntime.jsx(
2915
+ "div",
2916
+ {
2917
+ className: `tekiyo-slider-thumb tekiyo-step-thumb ${s.isDragging ? "active" : ""}`,
2918
+ style: {
2919
+ left: `${s.displayPercent}%`,
2920
+ transform: `translate(-50%, calc(-50% + ${s.verticalOffset}px)) scale(${s.thumbScale}) scaleX(${s.stretchX}) scaleY(${s.stretchY})`,
2921
+ transformOrigin: `${s.stretchOriginPercent}% center`,
2922
+ boxShadow: `0 ${s.shadowY}px ${s.shadowBlur}px rgba(0, 0, 0, ${s.shadowOpacity})`
2923
+ },
2924
+ onPointerDown: handlePointerDown
2925
+ }
2926
+ )
2927
+ ]
2928
+ }
2929
+ ),
2930
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tekiyo-step-labels", children: options.map((option, index) => {
2931
+ const labelPercent = index * stepPercent;
2932
+ const isSelected = currentIndex === index;
2933
+ return /* @__PURE__ */ jsxRuntime.jsx(
2934
+ "span",
2935
+ {
2936
+ className: `tekiyo-step-label ${isSelected ? "selected" : ""}`,
2937
+ style: {
2938
+ left: `${labelPercent}%`,
2939
+ color: isSelected ? option.color || "#007aff" : void 0
2940
+ },
2941
+ onClick: () => handleStepClick(index),
2942
+ children: option.label
2943
+ },
2944
+ option.value
2945
+ );
2946
+ }) })
2947
+ ] });
2948
+ }
2949
+ function PhysicsVerticalSlider({
2950
+ label,
2951
+ icon,
2952
+ value,
2953
+ min,
2954
+ max,
2955
+ step,
2956
+ config,
2957
+ onChange,
2958
+ height = 160,
2959
+ color = "#007aff",
2960
+ className = ""
2961
+ }) {
2962
+ const trackRef = React.useRef(null);
2963
+ const state = React.useRef({
2964
+ isDragging: false,
2965
+ displayPercent: (value - min) / (max - min) * 100,
2966
+ thumbScale: 1,
2967
+ horizontalOffset: 0,
2968
+ stretchX: 1,
2969
+ stretchY: 1,
2970
+ stretchOriginPercent: 50,
2971
+ targetOriginPercent: 50,
2972
+ shadowX: 0,
2973
+ shadowY: 2,
2974
+ shadowBlur: 6,
2975
+ shadowOpacity: 0.12,
2976
+ lastPercent: (value - min) / (max - min) * 100,
2977
+ pointerStart: null
2978
+ });
2979
+ const [, forceRender] = React.useState(0);
2980
+ const rerender = React.useCallback(() => forceRender((n) => n + 1), []);
2981
+ const physicsIdRef = React.useRef(generatePhysicsId("vslider"));
2982
+ const scaleIdRef = React.useRef(generatePhysicsId("vscale"));
2983
+ const decayIdRef = React.useRef(generatePhysicsId("vdecay"));
2984
+ const relaxIdRef = React.useRef(generatePhysicsId("vrelax"));
2985
+ const horizontalIdRef = React.useRef(generatePhysicsId("vhorizontal"));
2986
+ const springRef = React.useRef(null);
2987
+ const scaleSpringRef = React.useRef(null);
2988
+ const horizontalSpringRef = React.useRef(null);
2989
+ React.useEffect(() => {
2990
+ if (!state.current.isDragging) {
2991
+ const targetPercent = (value - min) / (max - min) * 100;
2992
+ animateToPercent(targetPercent);
2993
+ }
2994
+ }, [value, min, max]);
2995
+ const animateToPercent = React.useCallback(
2996
+ (targetPercent) => {
2997
+ const s2 = state.current;
2998
+ PhysicsEngine.unregister(physicsIdRef.current);
2999
+ springRef.current = new Spring1D(s2.displayPercent, {
3000
+ tension: config.springTension * 400,
3001
+ friction: 26,
3002
+ mass: 1,
3003
+ precision: 0.01
3004
+ });
3005
+ springRef.current.setTarget(targetPercent);
3006
+ PhysicsEngine.register(physicsIdRef.current, (dt) => {
3007
+ if (!springRef.current) return;
3008
+ const result = springRef.current.step(dt);
3009
+ s2.displayPercent = result.value;
3010
+ rerender();
3011
+ if (result.isSettled) {
3012
+ PhysicsEngine.unregister(physicsIdRef.current);
3013
+ }
3014
+ });
3015
+ },
3016
+ [config.springTension, rerender]
3017
+ );
3018
+ const percentToValue = React.useCallback(
3019
+ (percent) => {
3020
+ const raw = min + percent / 100 * (max - min);
3021
+ return Math.round(raw / step) * step;
3022
+ },
3023
+ [min, max, step]
3024
+ );
3025
+ const animateScaleTo = React.useCallback(
3026
+ (targetScale) => {
3027
+ const s2 = state.current;
3028
+ PhysicsEngine.unregister(scaleIdRef.current);
3029
+ scaleSpringRef.current = new Spring1D(s2.thumbScale, {
3030
+ tension: 400,
3031
+ friction: 20,
3032
+ mass: 0.6,
3033
+ precision: 1e-3
3034
+ });
3035
+ scaleSpringRef.current.setTarget(targetScale);
3036
+ PhysicsEngine.register(scaleIdRef.current, (dt) => {
3037
+ if (!scaleSpringRef.current) return;
3038
+ const result = scaleSpringRef.current.step(dt);
3039
+ s2.thumbScale = result.value;
3040
+ rerender();
3041
+ if (result.isSettled) {
3042
+ PhysicsEngine.unregister(scaleIdRef.current);
3043
+ }
3044
+ });
3045
+ },
3046
+ [rerender]
3047
+ );
3048
+ const animateStretchRelax = React.useCallback(() => {
3049
+ const s2 = state.current;
3050
+ PhysicsEngine.unregister(relaxIdRef.current);
3051
+ const spring = new Spring1D(s2.stretchY, {
3052
+ tension: 300,
3053
+ friction: 14,
3054
+ mass: 0.5,
3055
+ precision: 1e-3
3056
+ });
3057
+ spring.setTarget(1);
3058
+ PhysicsEngine.register(relaxIdRef.current, (dt) => {
3059
+ const result = spring.step(dt);
3060
+ s2.stretchY = result.value;
3061
+ s2.stretchX = 1 - (result.value - 1) * 0.4;
3062
+ s2.stretchOriginPercent += (50 - s2.stretchOriginPercent) * 0.15;
3063
+ s2.shadowY += (2 - s2.shadowY) * 0.15;
3064
+ s2.shadowBlur += (6 - s2.shadowBlur) * 0.15;
3065
+ s2.shadowOpacity += (0.12 - s2.shadowOpacity) * 0.15;
3066
+ rerender();
3067
+ if (result.isSettled) {
3068
+ s2.stretchX = 1;
3069
+ s2.stretchY = 1;
3070
+ s2.stretchOriginPercent = 50;
3071
+ PhysicsEngine.unregister(relaxIdRef.current);
3072
+ }
3073
+ });
3074
+ }, [rerender]);
3075
+ const handlePointerDown = React.useCallback(
3076
+ (e) => {
3077
+ if (!trackRef.current) return;
3078
+ const s2 = state.current;
3079
+ e.preventDefault();
3080
+ e.target.setPointerCapture(e.pointerId);
3081
+ s2.pointerStart = { x: e.clientX, y: e.clientY };
3082
+ s2.isDragging = true;
3083
+ PhysicsEngine.unregister(physicsIdRef.current);
3084
+ PhysicsEngine.unregister(horizontalIdRef.current);
3085
+ PhysicsEngine.unregister(relaxIdRef.current);
3086
+ animateScaleTo(config.liftScale);
3087
+ PhysicsEngine.register(decayIdRef.current, () => {
3088
+ if (!s2.isDragging) return;
3089
+ const decayLerp = 0.08;
3090
+ const threshold = 1e-3;
3091
+ if (Math.abs(s2.stretchY - 1) > threshold) {
3092
+ s2.stretchY += (1 - s2.stretchY) * decayLerp;
3093
+ }
3094
+ if (Math.abs(s2.stretchX - 1) > threshold) {
3095
+ s2.stretchX += (1 - s2.stretchX) * decayLerp;
3096
+ }
3097
+ if (Math.abs(s2.stretchOriginPercent - 50) > threshold) {
3098
+ s2.stretchOriginPercent += (50 - s2.stretchOriginPercent) * decayLerp;
3099
+ }
3100
+ s2.shadowY += (2 - s2.shadowY) * decayLerp;
3101
+ s2.shadowBlur += (6 - s2.shadowBlur) * decayLerp;
3102
+ s2.shadowOpacity += (0.12 - s2.shadowOpacity) * decayLerp;
3103
+ rerender();
3104
+ });
3105
+ const rect = trackRef.current.getBoundingClientRect();
3106
+ const y = Math.max(0, Math.min(rect.height, e.clientY - rect.top));
3107
+ const percent = 100 - y / rect.height * 100;
3108
+ s2.displayPercent = percent;
3109
+ s2.lastPercent = percent;
3110
+ onChange(percentToValue(percent));
3111
+ rerender();
3112
+ },
3113
+ [config.liftScale, animateScaleTo, percentToValue, onChange, rerender]
3114
+ );
3115
+ const handlePointerMove = React.useCallback(
3116
+ (e) => {
3117
+ const s2 = state.current;
3118
+ if (!s2.isDragging || !trackRef.current || !s2.pointerStart) return;
3119
+ const rect = trackRef.current.getBoundingClientRect();
3120
+ const rawY = e.clientY - rect.top;
3121
+ const rawPercent = 100 - rawY / rect.height * 100;
3122
+ let percent = rawPercent;
3123
+ let edgeStretchV = 0;
3124
+ if (rawPercent < 0) {
3125
+ edgeStretchV = -rawPercent / 100;
3126
+ percent = rawPercent * 0.15;
3127
+ s2.targetOriginPercent = 0;
3128
+ } else if (rawPercent > 100) {
3129
+ edgeStretchV = (rawPercent - 100) / 100;
3130
+ percent = 100 + (rawPercent - 100) * 0.15;
3131
+ s2.targetOriginPercent = 100;
3132
+ }
3133
+ const velocity = percent - s2.lastPercent;
3134
+ s2.lastPercent = percent;
3135
+ if (Math.abs(velocity) > 0.3 && edgeStretchV === 0) {
3136
+ s2.targetOriginPercent = velocity > 0 ? 0 : 100;
3137
+ }
3138
+ s2.stretchOriginPercent += (s2.targetOriginPercent - s2.stretchOriginPercent) * 0.15;
3139
+ const rawHorizontalOffset = e.clientX - s2.pointerStart.x;
3140
+ const rubberBandedOffset = rawHorizontalOffset * 0.06;
3141
+ s2.horizontalOffset = Math.max(-5, Math.min(5, rubberBandedOffset));
3142
+ const absVelocity = Math.abs(velocity);
3143
+ const velStretch = Math.min(absVelocity * config.velocityStretch * 15, 1.2);
3144
+ const edgeStretchFactorV = Math.min(Math.abs(edgeStretchV) * 0.6, 0.12);
3145
+ const edgeStretchFactorH = Math.abs(s2.horizontalOffset) / 5 * 0.06;
3146
+ const targetStretchY = 1 + velStretch + edgeStretchFactorV;
3147
+ const targetStretchX = 1 - velStretch * 0.4 + edgeStretchFactorH;
3148
+ const baseLerp = 0.35;
3149
+ const decayLerp = 0.15;
3150
+ const velocityThreshold = 0.5;
3151
+ if (absVelocity < velocityThreshold) {
3152
+ const decayFactor = 1 - absVelocity / velocityThreshold;
3153
+ s2.stretchY += (1 - s2.stretchY) * decayLerp * decayFactor;
3154
+ s2.stretchX += (1 - s2.stretchX) * decayLerp * decayFactor;
3155
+ s2.stretchOriginPercent += (50 - s2.stretchOriginPercent) * decayLerp * decayFactor;
3156
+ }
3157
+ s2.stretchY += (targetStretchY - s2.stretchY) * baseLerp;
3158
+ s2.stretchX += (targetStretchX - s2.stretchX) * baseLerp;
3159
+ const targetShadowY = 2 + absVelocity * 0.4;
3160
+ const targetShadowBlur = 6 + absVelocity * 0.6;
3161
+ const targetShadowOpacity = 0.12 + absVelocity * 0.015;
3162
+ s2.shadowY += (targetShadowY - s2.shadowY) * 0.3;
3163
+ s2.shadowBlur += (targetShadowBlur - s2.shadowBlur) * 0.3;
3164
+ s2.shadowOpacity += (targetShadowOpacity - s2.shadowOpacity) * 0.3;
3165
+ s2.displayPercent = Math.max(0, Math.min(100, percent));
3166
+ onChange(percentToValue(Math.max(0, Math.min(100, percent))));
3167
+ rerender();
3168
+ },
3169
+ [config.velocityStretch, percentToValue, onChange, rerender]
3170
+ );
3171
+ const handlePointerUp = React.useCallback(() => {
3172
+ const s2 = state.current;
3173
+ if (!s2.isDragging) return;
3174
+ s2.isDragging = false;
3175
+ s2.pointerStart = null;
3176
+ PhysicsEngine.unregister(decayIdRef.current);
3177
+ animateScaleTo(1);
3178
+ animateStretchRelax();
3179
+ if (s2.horizontalOffset !== 0) {
3180
+ PhysicsEngine.unregister(horizontalIdRef.current);
3181
+ horizontalSpringRef.current = new Spring1D(s2.horizontalOffset, {
3182
+ tension: 400,
3183
+ friction: 18,
3184
+ mass: 0.5,
3185
+ precision: 0.1
3186
+ });
3187
+ horizontalSpringRef.current.setTarget(0);
3188
+ PhysicsEngine.register(horizontalIdRef.current, (dt) => {
3189
+ if (!horizontalSpringRef.current) return;
3190
+ const result = horizontalSpringRef.current.step(dt);
3191
+ s2.horizontalOffset = result.value;
3192
+ rerender();
3193
+ if (result.isSettled) {
3194
+ s2.horizontalOffset = 0;
3195
+ PhysicsEngine.unregister(horizontalIdRef.current);
3196
+ }
3197
+ });
3198
+ }
3199
+ rerender();
3200
+ }, [animateScaleTo, animateStretchRelax, rerender]);
3201
+ const handleTrackClick = React.useCallback(
3202
+ (e) => {
3203
+ const s2 = state.current;
3204
+ if (!trackRef.current || s2.isDragging) return;
3205
+ const rect = trackRef.current.getBoundingClientRect();
3206
+ const y = e.clientY - rect.top;
3207
+ const percent = 100 - Math.max(0, Math.min(100, y / rect.height * 100));
3208
+ onChange(percentToValue(percent));
3209
+ animateToPercent(percent);
3210
+ },
3211
+ [percentToValue, onChange, animateToPercent]
3212
+ );
3213
+ React.useEffect(() => {
3214
+ return () => {
3215
+ PhysicsEngine.unregister(physicsIdRef.current);
3216
+ PhysicsEngine.unregister(scaleIdRef.current);
3217
+ PhysicsEngine.unregister(decayIdRef.current);
3218
+ PhysicsEngine.unregister(relaxIdRef.current);
3219
+ PhysicsEngine.unregister(horizontalIdRef.current);
3220
+ };
3221
+ }, []);
3222
+ const s = state.current;
3223
+ const displayValue = Math.round(value);
3224
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `tekiyo-vertical-slider ${className}`, style: { height }, children: [
3225
+ /* @__PURE__ */ jsxRuntime.jsxs(
3226
+ "div",
3227
+ {
3228
+ className: "tekiyo-vertical-track",
3229
+ ref: trackRef,
3230
+ onClick: handleTrackClick,
3231
+ onPointerMove: handlePointerMove,
3232
+ onPointerUp: handlePointerUp,
3233
+ onPointerCancel: handlePointerUp,
3234
+ style: { height },
3235
+ children: [
3236
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tekiyo-vertical-track-bg" }),
3237
+ /* @__PURE__ */ jsxRuntime.jsx(
3238
+ "div",
3239
+ {
3240
+ className: "tekiyo-vertical-track-fill",
3241
+ style: {
3242
+ height: `${s.displayPercent}%`,
3243
+ background: color
3244
+ }
3245
+ }
3246
+ ),
3247
+ /* @__PURE__ */ jsxRuntime.jsx(
3248
+ "div",
3249
+ {
3250
+ className: `tekiyo-slider-thumb tekiyo-vertical-thumb ${s.isDragging ? "active" : ""}`,
3251
+ style: {
3252
+ bottom: `${s.displayPercent}%`,
3253
+ transform: `translate(calc(-50% + ${s.horizontalOffset}px), 50%) scale(${s.thumbScale}) scaleX(${s.stretchX}) scaleY(${s.stretchY})`,
3254
+ transformOrigin: `center ${s.stretchOriginPercent}%`,
3255
+ boxShadow: `0 ${s.shadowY}px ${s.shadowBlur}px rgba(0, 0, 0, ${s.shadowOpacity})`
3256
+ },
3257
+ onPointerDown: handlePointerDown
3258
+ }
3259
+ )
3260
+ ]
3261
+ }
3262
+ ),
3263
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "tekiyo-vertical-slider-info", children: [
3264
+ icon && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "tekiyo-vertical-slider-icon", style: { color }, children: icon }),
3265
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tekiyo-vertical-slider-value", style: { color }, children: [
3266
+ displayValue,
3267
+ "%"
3268
+ ] }),
3269
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "tekiyo-vertical-slider-label", children: label })
3270
+ ] })
3271
+ ] });
3272
+ }
3273
+ const sliderPresets = [
3274
+ {
3275
+ name: "Buttery Smooth",
3276
+ description: "iOS classic feel",
3277
+ config: {
3278
+ springTension: 0.8,
3279
+ velocityStretch: 0.02,
3280
+ liftScale: 1.5,
3281
+ flickMomentum: 800,
3282
+ maxVelocity: 3
3283
+ }
3284
+ },
3285
+ {
3286
+ name: "Snappy",
3287
+ description: "Responsive & precise",
3288
+ config: {
3289
+ springTension: 1.4,
3290
+ velocityStretch: 0.01,
3291
+ liftScale: 1.05,
3292
+ flickMomentum: 400,
3293
+ maxVelocity: 5
3294
+ }
3295
+ },
3296
+ {
3297
+ name: "Playful",
3298
+ description: "Fun & elastic",
3299
+ config: {
3300
+ springTension: 0.5,
3301
+ velocityStretch: 0.08,
3302
+ liftScale: 1.5,
3303
+ flickMomentum: 1500,
3304
+ maxVelocity: 6
3305
+ }
3306
+ }
3307
+ ];
3308
+ const defaultSliderConfig = sliderPresets[0].config;
3309
+ function SegmentedControl({
3310
+ options,
3311
+ defaultIndex = 0,
3312
+ value,
3313
+ config,
3314
+ showIndicator,
3315
+ onChange,
3316
+ className = ""
3317
+ }) {
3318
+ const [, forceRender] = React.useState(0);
3319
+ const rerender = React.useCallback(() => forceRender((n) => n + 1), []);
3320
+ const initialIndex = value !== void 0 ? value : defaultIndex;
3321
+ const state = React.useRef({
3322
+ selected: initialIndex,
3323
+ indicatorPos: initialIndex,
3324
+ stretchX: 1,
3325
+ stretchY: 1,
3326
+ pressScale: 1,
3327
+ verticalOffset: 0,
3328
+ isDragging: false,
3329
+ isPressed: false,
3330
+ stretchOriginPercent: 50,
3331
+ targetOriginPercent: 50,
3332
+ velocity: 0,
3333
+ shadowY: 4,
3334
+ shadowBlur: 12,
3335
+ shadowOpacity: 0.15
3336
+ });
3337
+ const containerRef = React.useRef(null);
3338
+ const springRef = React.useRef(null);
3339
+ const pressSpringRef = React.useRef(null);
3340
+ const verticalSpringRef = React.useRef(null);
3341
+ const stretchOriginSpringRef = React.useRef(null);
3342
+ const physicsId = React.useRef(generatePhysicsId("seg"));
3343
+ const pressId = React.useRef(generatePhysicsId("press"));
3344
+ const verticalId = React.useRef(generatePhysicsId("vert"));
3345
+ const stretchOriginId = React.useRef(generatePhysicsId("segOrigin"));
3346
+ const pointerStart = React.useRef(null);
3347
+ const lastPos = React.useRef(initialIndex);
3348
+ const hasDragged = React.useRef(false);
3349
+ const velocitySamples = React.useRef([]);
3350
+ React.useEffect(() => {
3351
+ if (value !== void 0 && value !== state.current.selected && !state.current.isDragging) {
3352
+ animateTo(value);
3353
+ }
3354
+ }, [value]);
3355
+ const getIndexFromX = React.useCallback((clientX) => {
3356
+ if (!containerRef.current) return state.current.selected;
3357
+ const rect = containerRef.current.getBoundingClientRect();
3358
+ const x = Math.max(0, Math.min(rect.width, clientX - rect.left));
3359
+ return Math.min(options.length - 1, Math.floor(x / rect.width * options.length));
3360
+ }, [options.length]);
3361
+ const getPosFromX = React.useCallback((clientX, allowOverdrag = false) => {
3362
+ if (!containerRef.current) return state.current.indicatorPos;
3363
+ const rect = containerRef.current.getBoundingClientRect();
3364
+ const x = clientX - rect.left;
3365
+ const rel = allowOverdrag ? x / rect.width : Math.max(0, Math.min(1, x / rect.width));
3366
+ return rel * (options.length - 1);
3367
+ }, [options.length]);
3368
+ const animateTo = React.useCallback((target, initialVelocity = 0) => {
3369
+ const s2 = state.current;
3370
+ s2.targetOriginPercent = target > s2.indicatorPos ? 0 : 100;
3371
+ s2.selected = target;
3372
+ PhysicsEngine.unregister(physicsId.current);
3373
+ PhysicsEngine.unregister(stretchOriginId.current);
3374
+ if (s2.pressScale !== 1) {
3375
+ PhysicsEngine.unregister(pressId.current);
3376
+ const releaseSpring = new Spring1D(s2.pressScale, {
3377
+ tension: 200,
3378
+ friction: 18,
3379
+ mass: 0.8,
3380
+ precision: 0.01
3381
+ });
3382
+ releaseSpring.setTarget(1);
3383
+ PhysicsEngine.register(pressId.current, (dt2) => {
3384
+ const pr = releaseSpring.step(dt2);
3385
+ s2.pressScale = pr.value;
3386
+ rerender();
3387
+ if (pr.isSettled) {
3388
+ s2.pressScale = 1;
3389
+ PhysicsEngine.unregister(pressId.current);
3390
+ }
3391
+ });
3392
+ }
3393
+ springRef.current = new Spring1D(s2.indicatorPos, {
3394
+ tension: config.springTension * 280,
3395
+ friction: 18,
3396
+ mass: 0.8,
3397
+ precision: 1e-3
3398
+ });
3399
+ springRef.current.setTarget(target);
3400
+ if (initialVelocity) springRef.current.setVelocity(initialVelocity);
3401
+ PhysicsEngine.register(physicsId.current, (dt) => {
3402
+ if (!springRef.current) return;
3403
+ const r = springRef.current.step(dt);
3404
+ const vel = Math.abs(r.velocity || 0);
3405
+ const stretch = Math.min(vel * config.velocityStretch * 6, 0.25);
3406
+ s2.indicatorPos = r.value;
3407
+ s2.velocity = vel;
3408
+ s2.stretchX = 1 + stretch;
3409
+ s2.stretchY = 1 - stretch * 0.5;
3410
+ s2.stretchOriginPercent += (s2.targetOriginPercent - s2.stretchOriginPercent) * 0.12;
3411
+ s2.shadowY = 4 + vel * 2;
3412
+ s2.shadowBlur = 12 + vel * 4;
3413
+ s2.shadowOpacity = 0.15 + vel * 0.05;
3414
+ rerender();
3415
+ if (r.isSettled) {
3416
+ s2.stretchX = 1;
3417
+ s2.stretchY = 1;
3418
+ s2.velocity = 0;
3419
+ s2.shadowY = 4;
3420
+ s2.shadowBlur = 12;
3421
+ s2.shadowOpacity = 0.15;
3422
+ PhysicsEngine.unregister(physicsId.current);
3423
+ stretchOriginSpringRef.current = new Spring1D(s2.stretchOriginPercent, {
3424
+ tension: 300,
3425
+ friction: 18,
3426
+ mass: 0.5,
3427
+ precision: 0.1
3428
+ });
3429
+ stretchOriginSpringRef.current.setTarget(50);
3430
+ PhysicsEngine.register(stretchOriginId.current, (dt2) => {
3431
+ if (!stretchOriginSpringRef.current) return;
3432
+ const or = stretchOriginSpringRef.current.step(dt2);
3433
+ s2.stretchOriginPercent = or.value;
3434
+ s2.targetOriginPercent = 50;
3435
+ rerender();
3436
+ if (or.isSettled) {
3437
+ s2.stretchOriginPercent = 50;
3438
+ s2.targetOriginPercent = 50;
3439
+ PhysicsEngine.unregister(stretchOriginId.current);
3440
+ }
3441
+ });
3442
+ rerender();
3443
+ }
3444
+ });
3445
+ onChange == null ? void 0 : onChange(target);
3446
+ }, [config.springTension, config.velocityStretch, onChange, rerender]);
3447
+ const animatePress = React.useCallback((pressed) => {
3448
+ const s2 = state.current;
3449
+ PhysicsEngine.unregister(pressId.current);
3450
+ pressSpringRef.current = new Spring1D(s2.pressScale, {
3451
+ tension: 400,
3452
+ friction: 22,
3453
+ mass: 0.6,
3454
+ precision: 0.01
3455
+ });
3456
+ pressSpringRef.current.setTarget(pressed ? 1.4 : 1);
3457
+ PhysicsEngine.register(pressId.current, (dt) => {
3458
+ if (!pressSpringRef.current) return;
3459
+ const r = pressSpringRef.current.step(dt);
3460
+ s2.pressScale = r.value;
3461
+ rerender();
3462
+ if (r.isSettled) PhysicsEngine.unregister(pressId.current);
3463
+ });
3464
+ }, [rerender]);
3465
+ const animateVerticalBack = React.useCallback(() => {
3466
+ const s2 = state.current;
3467
+ if (Math.abs(s2.verticalOffset) < 0.5) {
3468
+ s2.verticalOffset = 0;
3469
+ return;
3470
+ }
3471
+ PhysicsEngine.unregister(verticalId.current);
3472
+ verticalSpringRef.current = new Spring1D(s2.verticalOffset, {
3473
+ tension: 400,
3474
+ friction: 20,
3475
+ mass: 0.5,
3476
+ precision: 0.1
3477
+ });
3478
+ verticalSpringRef.current.setTarget(0);
3479
+ PhysicsEngine.register(verticalId.current, (dt) => {
3480
+ if (!verticalSpringRef.current) return;
3481
+ const r = verticalSpringRef.current.step(dt);
3482
+ s2.verticalOffset = r.value;
3483
+ rerender();
3484
+ if (r.isSettled) {
3485
+ s2.verticalOffset = 0;
3486
+ PhysicsEngine.unregister(verticalId.current);
3487
+ }
3488
+ });
3489
+ }, [rerender]);
3490
+ const animateOriginBack = React.useCallback(() => {
3491
+ const s2 = state.current;
3492
+ if (Math.abs(s2.stretchOriginPercent - 50) < 1) {
3493
+ s2.stretchOriginPercent = 50;
3494
+ s2.targetOriginPercent = 50;
3495
+ return;
3496
+ }
3497
+ PhysicsEngine.unregister(stretchOriginId.current);
3498
+ stretchOriginSpringRef.current = new Spring1D(s2.stretchOriginPercent, {
3499
+ tension: 300,
3500
+ friction: 18,
3501
+ mass: 0.5,
3502
+ precision: 0.1
3503
+ });
3504
+ stretchOriginSpringRef.current.setTarget(50);
3505
+ PhysicsEngine.register(stretchOriginId.current, (dt) => {
3506
+ if (!stretchOriginSpringRef.current) return;
3507
+ const r = stretchOriginSpringRef.current.step(dt);
3508
+ s2.stretchOriginPercent = r.value;
3509
+ s2.targetOriginPercent = 50;
3510
+ rerender();
3511
+ if (r.isSettled) {
3512
+ s2.stretchOriginPercent = 50;
3513
+ s2.targetOriginPercent = 50;
3514
+ PhysicsEngine.unregister(stretchOriginId.current);
3515
+ }
3516
+ });
3517
+ }, [rerender]);
3518
+ const onPointerDown = React.useCallback((e) => {
3519
+ const s2 = state.current;
3520
+ pointerStart.current = { x: e.clientX, y: e.clientY };
3521
+ hasDragged.current = false;
3522
+ velocitySamples.current = [];
3523
+ e.currentTarget.setPointerCapture(e.pointerId);
3524
+ PhysicsEngine.unregister(stretchOriginId.current);
3525
+ if (containerRef.current) {
3526
+ const rect = containerRef.current.getBoundingClientRect();
3527
+ const segW = rect.width / options.length;
3528
+ const indLeft = rect.left + s2.indicatorPos * segW;
3529
+ const grabX = e.clientX - indLeft;
3530
+ const pct = Math.max(0, Math.min(100, grabX / segW * 100));
3531
+ s2.targetOriginPercent = pct;
3532
+ s2.stretchOriginPercent = pct;
3533
+ }
3534
+ s2.isPressed = true;
3535
+ animatePress(true);
3536
+ lastPos.current = getPosFromX(e.clientX);
3537
+ rerender();
3538
+ }, [options.length, getPosFromX, animatePress, rerender]);
3539
+ const onPointerMove = React.useCallback((e) => {
3540
+ if (!pointerStart.current) return;
3541
+ const s2 = state.current;
3542
+ const dx = e.clientX - pointerStart.current.x;
3543
+ const dy = e.clientY - pointerStart.current.y;
3544
+ if (!hasDragged.current && (Math.abs(dx) > 5 || Math.abs(dy) > 5)) {
3545
+ hasDragged.current = true;
3546
+ s2.isDragging = true;
3547
+ PhysicsEngine.unregister(physicsId.current);
3548
+ }
3549
+ if (!hasDragged.current) return;
3550
+ let pos = getPosFromX(e.clientX, true);
3551
+ const minPos = 0;
3552
+ const maxPos = options.length - 1;
3553
+ let edgeStretch = 0;
3554
+ if (pos < minPos) {
3555
+ const over = minPos - pos;
3556
+ edgeStretch = -over;
3557
+ pos = minPos - over * 0.15;
3558
+ s2.targetOriginPercent = 100;
3559
+ } else if (pos > maxPos) {
3560
+ const over = pos - maxPos;
3561
+ edgeStretch = over;
3562
+ pos = maxPos + over * 0.15;
3563
+ s2.targetOriginPercent = 0;
3564
+ }
3565
+ const vel = pos - lastPos.current;
3566
+ if (Math.abs(vel) > 0.01 && edgeStretch === 0) {
3567
+ s2.targetOriginPercent = vel > 0 ? 100 : 0;
3568
+ }
3569
+ s2.stretchOriginPercent += (s2.targetOriginPercent - s2.stretchOriginPercent) * 0.15;
3570
+ velocitySamples.current.push(vel);
3571
+ if (velocitySamples.current.length > 5) velocitySamples.current.shift();
3572
+ lastPos.current = pos;
3573
+ const vOff = Math.max(-12, Math.min(12, dy * 0.12));
3574
+ s2.verticalOffset = vOff;
3575
+ const vFactor = Math.min(Math.abs(vel) * config.velocityStretch * 80, 0.6);
3576
+ const edgeFactor = Math.min(Math.abs(edgeStretch) * 0.2, 0.2);
3577
+ s2.stretchX = 1 + vFactor + edgeFactor;
3578
+ s2.stretchY = 1 - vFactor * 0.5 - edgeFactor * 0.4;
3579
+ s2.indicatorPos = pos;
3580
+ const idx = getIndexFromX(e.clientX);
3581
+ if (idx !== s2.selected) s2.selected = idx;
3582
+ rerender();
3583
+ }, [getPosFromX, getIndexFromX, options.length, config.velocityStretch, rerender]);
3584
+ const onPointerUp = React.useCallback((e) => {
3585
+ const s2 = state.current;
3586
+ pointerStart.current = null;
3587
+ s2.isPressed = false;
3588
+ if (!hasDragged.current) {
3589
+ const idx = getIndexFromX(e.clientX);
3590
+ if (idx !== s2.selected) {
3591
+ const dir = idx > s2.indicatorPos ? 1 : -1;
3592
+ animateTo(idx, dir * 3);
3593
+ } else {
3594
+ animatePress(false);
3595
+ animateOriginBack();
3596
+ }
3597
+ rerender();
3598
+ return;
3599
+ }
3600
+ s2.isDragging = false;
3601
+ hasDragged.current = false;
3602
+ animatePress(false);
3603
+ const avgVel = velocitySamples.current.length > 0 ? velocitySamples.current.reduce((a, b) => a + b, 0) / velocitySamples.current.length : 0;
3604
+ velocitySamples.current = [];
3605
+ animateVerticalBack();
3606
+ animateTo(s2.selected, avgVel * 0.5);
3607
+ }, [getIndexFromX, animateTo, animatePress, animateVerticalBack, animateOriginBack, rerender]);
3608
+ const onPointerLeave = React.useCallback(() => {
3609
+ if (pointerStart.current) {
3610
+ const s2 = state.current;
3611
+ pointerStart.current = null;
3612
+ s2.isPressed = false;
3613
+ s2.isDragging = false;
3614
+ hasDragged.current = false;
3615
+ velocitySamples.current = [];
3616
+ animatePress(false);
3617
+ animateVerticalBack();
3618
+ animateTo(s2.selected);
3619
+ }
3620
+ }, [animatePress, animateVerticalBack, animateTo]);
3621
+ React.useEffect(() => {
3622
+ return () => {
3623
+ PhysicsEngine.unregister(physicsId.current);
3624
+ PhysicsEngine.unregister(pressId.current);
3625
+ PhysicsEngine.unregister(verticalId.current);
3626
+ PhysicsEngine.unregister(stretchOriginId.current);
3627
+ };
3628
+ }, []);
3629
+ const s = state.current;
3630
+ const dragClass = s.isPressed || s.isDragging ? "dragging" : "";
3631
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3632
+ "div",
3633
+ {
3634
+ ref: containerRef,
3635
+ className: `tekiyo-segmented-control ${s.isDragging ? "dragging" : ""} ${className}`,
3636
+ onPointerDown,
3637
+ onPointerMove,
3638
+ onPointerUp,
3639
+ onPointerCancel: onPointerUp,
3640
+ onPointerLeave,
3641
+ children: [
3642
+ /* @__PURE__ */ jsxRuntime.jsx(
3643
+ "div",
3644
+ {
3645
+ className: `tekiyo-segmented-indicator ${s.isPressed ? "active" : ""} ${dragClass}`,
3646
+ style: {
3647
+ width: `calc((100% - 6px) / ${options.length})`,
3648
+ left: `calc(${s.indicatorPos} * (100% - 6px) / ${options.length} + 3px)`,
3649
+ transform: `translateY(${s.verticalOffset}px) scaleX(${s.stretchX * s.pressScale}) scaleY(${s.stretchY * s.pressScale})`,
3650
+ transformOrigin: `${s.stretchOriginPercent}% center`
3651
+ }
3652
+ }
3653
+ ),
3654
+ options.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs(
3655
+ "div",
3656
+ {
3657
+ className: `tekiyo-segmented-option ${s.selected === index ? "active" : ""} ${dragClass}`,
3658
+ children: [
3659
+ option,
3660
+ showIndicator && s.selected === index && /* @__PURE__ */ jsxRuntime.jsx("span", { className: `tekiyo-option-dot ${dragClass}` })
3661
+ ]
3662
+ },
3663
+ option
3664
+ ))
3665
+ ]
3666
+ }
3667
+ );
3668
+ }
3669
+ const segmentedControlPresets = [
3670
+ {
3671
+ name: "Buttery Smooth",
3672
+ description: "iOS classic feel",
3673
+ config: {
3674
+ springTension: 0.8,
3675
+ velocityStretch: 0.02,
3676
+ liftScale: 1.5,
3677
+ flickMomentum: 800,
3678
+ maxVelocity: 3
3679
+ }
3680
+ },
3681
+ {
3682
+ name: "Snappy",
3683
+ description: "Responsive & precise",
3684
+ config: {
3685
+ springTension: 1.4,
3686
+ velocityStretch: 0.01,
3687
+ liftScale: 1.05,
3688
+ flickMomentum: 400,
3689
+ maxVelocity: 5
3690
+ }
3691
+ },
3692
+ {
3693
+ name: "Playful",
3694
+ description: "Fun & elastic",
3695
+ config: {
3696
+ springTension: 0.5,
3697
+ velocityStretch: 0.08,
3698
+ liftScale: 1.5,
3699
+ flickMomentum: 1500,
3700
+ maxVelocity: 6
3701
+ }
3702
+ }
3703
+ ];
3704
+ const defaultSegmentedControlConfig = segmentedControlPresets[0].config;
3705
+ const DEFAULT_LIQUID_GLASS_CONFIG = {
3706
+ width: 200,
3707
+ height: 60,
3708
+ bezelWidth: 20,
3709
+ glassThickness: 0.5,
3710
+ refractiveIndex: 1.5,
3711
+ profile: "squircle",
3712
+ scale: 70
3713
+ };
3714
+ const mapCache = /* @__PURE__ */ new Map();
3715
+ function getCacheKey(config) {
3716
+ return `${config.width}-${config.height}-${config.bezelWidth}-${config.glassThickness}-${config.refractiveIndex}-${config.profile}`;
3717
+ }
3718
+ function preloadMaps(width, height, bezelWidth = 20, glassThickness = 0.5, refractiveIndex = 1.5) {
3719
+ const profiles = ["convex", "squircle", "concave", "lip"];
3720
+ profiles.forEach((profile) => {
3721
+ const config = {
3722
+ width,
3723
+ height,
3724
+ bezelWidth,
3725
+ glassThickness,
3726
+ refractiveIndex,
3727
+ profile
3728
+ };
3729
+ const key = getCacheKey(config);
3730
+ if (!mapCache.has(key)) {
3731
+ mapCache.set(key, {
3732
+ displacement: generateDisplacementMapUncached(config),
3733
+ specular: generateSpecularMapUncached(config)
3734
+ });
3735
+ }
3736
+ });
3737
+ }
3738
+ function getCachedMaps(config) {
3739
+ const key = getCacheKey(config);
3740
+ if (mapCache.has(key)) {
3741
+ return mapCache.get(key);
3742
+ }
3743
+ const maps = {
3744
+ displacement: generateDisplacementMapUncached(config),
3745
+ specular: generateSpecularMapUncached(config)
3746
+ };
3747
+ mapCache.set(key, maps);
3748
+ return maps;
3749
+ }
3750
+ function clearMapCache() {
3751
+ mapCache.clear();
3752
+ }
3753
+ function smootherstep(x) {
3754
+ const t = Math.max(0, Math.min(1, x));
3755
+ return t * t * t * (t * (t * 6 - 15) + 10);
3756
+ }
3757
+ function getProfileFunction(profile) {
3758
+ switch (profile) {
3759
+ case "convex":
3760
+ return (x) => Math.sqrt(Math.max(0, 1 - Math.pow(1 - x, 2)));
3761
+ case "squircle":
3762
+ return (x) => Math.pow(Math.max(0, 1 - Math.pow(1 - x, 4)), 0.25);
3763
+ case "concave":
3764
+ return (x) => 1 - Math.sqrt(Math.max(0, 1 - Math.pow(1 - x, 2)));
3765
+ case "lip":
3766
+ return (x) => {
3767
+ const convex = Math.sqrt(Math.max(0, 1 - Math.pow(1 - x, 2)));
3768
+ const concave = 1 - convex;
3769
+ const t = smootherstep(x);
3770
+ return convex * (1 - t) + concave * t;
3771
+ };
3772
+ default:
3773
+ return (x) => x;
3774
+ }
3775
+ }
3776
+ function calculateNormal(f, x) {
3777
+ const delta = 1e-3;
3778
+ const x1 = Math.max(0, x - delta);
3779
+ const x2 = Math.min(1, x + delta);
3780
+ const y1 = f(x1);
3781
+ const y2 = f(x2);
3782
+ const derivative = (y2 - y1) / (x2 - x1);
3783
+ const length = Math.sqrt(derivative * derivative + 1);
3784
+ return {
3785
+ nx: -derivative / length,
3786
+ ny: 1 / length
3787
+ };
3788
+ }
3789
+ function applySnellLaw(incidentAngle, n1, n2) {
3790
+ const sinTheta1 = Math.sin(incidentAngle);
3791
+ const sinTheta2 = n1 / n2 * sinTheta1;
3792
+ if (Math.abs(sinTheta2) > 1) {
3793
+ return incidentAngle;
3794
+ }
3795
+ return Math.asin(sinTheta2);
3796
+ }
3797
+ function calculateDisplacement(normalizedDist, angle, profile, glassThickness, refractiveIndex) {
3798
+ if (normalizedDist >= 1) {
3799
+ return { dx: 0, dy: 0 };
3800
+ }
3801
+ const height = profile(normalizedDist) * glassThickness;
3802
+ const normal = calculateNormal(profile, normalizedDist);
3803
+ const incidentAngle = Math.atan2(normal.nx, normal.ny);
3804
+ const refractedAngle = applySnellLaw(incidentAngle, 1, refractiveIndex);
3805
+ const angleDiff = refractedAngle - incidentAngle;
3806
+ const magnitude = Math.sin(angleDiff) * height;
3807
+ const dx = magnitude * Math.cos(angle);
3808
+ const dy = magnitude * Math.sin(angle);
3809
+ return { dx, dy };
3810
+ }
3811
+ function vectorToRGB(dx, dy) {
3812
+ return {
3813
+ r: Math.round(128 + dx * 127),
3814
+ g: Math.round(128 + dy * 127),
3815
+ b: 128
3816
+ // Unused channel
3817
+ };
3818
+ }
3819
+ function distanceToRoundedRectEdge(x, y, width, height, cornerRadius) {
3820
+ const halfW = width / 2;
3821
+ const halfH = height / 2;
3822
+ const cx = x - halfW;
3823
+ const cy = y - halfH;
3824
+ const r = Math.min(cornerRadius, halfW, halfH);
3825
+ const absX = Math.abs(cx);
3826
+ const absY = Math.abs(cy);
3827
+ const cornerX = halfW - r;
3828
+ const cornerY = halfH - r;
3829
+ if (absX > cornerX && absY > cornerY) {
3830
+ const cornerCenterX = cornerX * Math.sign(cx);
3831
+ const cornerCenterY = cornerY * Math.sign(cy);
3832
+ const dx = cx - cornerCenterX;
3833
+ const dy = cy - cornerCenterY;
3834
+ const distFromCorner = Math.sqrt(dx * dx + dy * dy);
3835
+ const distToEdge = r - distFromCorner;
3836
+ const angle = Math.atan2(dy, dx);
3837
+ return { distance: Math.max(0, distToEdge), angle };
3838
+ }
3839
+ if (absX > absY * (halfW / halfH)) {
3840
+ const distToEdge = halfW - absX;
3841
+ const angle = cx > 0 ? 0 : Math.PI;
3842
+ return { distance: Math.max(0, distToEdge), angle };
3843
+ } else {
3844
+ const distToEdge = halfH - absY;
3845
+ const angle = cy > 0 ? Math.PI / 2 : -Math.PI / 2;
3846
+ return { distance: Math.max(0, distToEdge), angle };
3847
+ }
3848
+ }
3849
+ function generateDisplacementMapUncached(config) {
3850
+ const { width, height, bezelWidth, glassThickness, refractiveIndex, profile } = config;
3851
+ const canvas = document.createElement("canvas");
3852
+ canvas.width = width;
3853
+ canvas.height = height;
3854
+ const ctx = canvas.getContext("2d");
3855
+ if (!ctx) {
3856
+ console.error("Failed to get canvas context");
3857
+ return "";
3858
+ }
3859
+ const profileFn = getProfileFunction(profile);
3860
+ const imageData = ctx.createImageData(width, height);
3861
+ const data = imageData.data;
3862
+ const cornerRadius = Math.min(width, height) / 2;
3863
+ for (let y = 0; y < height; y++) {
3864
+ for (let x = 0; x < width; x++) {
3865
+ const { distance, angle } = distanceToRoundedRectEdge(
3866
+ x,
3867
+ y,
3868
+ width,
3869
+ height,
3870
+ cornerRadius
3871
+ );
3872
+ const normalizedDist = Math.min(1, distance / bezelWidth);
3873
+ const { dx, dy } = calculateDisplacement(
3874
+ normalizedDist,
3875
+ angle,
3876
+ profileFn,
3877
+ glassThickness,
3878
+ refractiveIndex
3879
+ );
3880
+ const { r, g, b } = vectorToRGB(dx, dy);
3881
+ const idx = (y * width + x) * 4;
3882
+ data[idx] = r;
3883
+ data[idx + 1] = g;
3884
+ data[idx + 2] = b;
3885
+ data[idx + 3] = 255;
3886
+ }
3887
+ }
3888
+ ctx.putImageData(imageData, 0, 0);
3889
+ return canvas.toDataURL("image/png");
3890
+ }
3891
+ function generateSpecularMapUncached(config) {
3892
+ const { width, height, bezelWidth, glassThickness, profile } = config;
3893
+ const canvas = document.createElement("canvas");
3894
+ canvas.width = width;
3895
+ canvas.height = height;
3896
+ const ctx = canvas.getContext("2d");
3897
+ if (!ctx) {
3898
+ console.error("Failed to get canvas context");
3899
+ return "";
3900
+ }
3901
+ const profileFn = getProfileFunction(profile);
3902
+ const imageData = ctx.createImageData(width, height);
3903
+ const data = imageData.data;
3904
+ const cornerRadius = Math.min(width, height) / 2;
3905
+ for (let y = 0; y < height; y++) {
3906
+ for (let x = 0; x < width; x++) {
3907
+ const { distance } = distanceToRoundedRectEdge(
3908
+ x,
3909
+ y,
3910
+ width,
3911
+ height,
3912
+ cornerRadius
3913
+ );
3914
+ const normalizedDist = Math.min(1, distance / bezelWidth);
3915
+ let specularIntensity = 0;
3916
+ if (normalizedDist < 1) {
3917
+ const delta = 0.02;
3918
+ const h1 = profileFn(Math.max(0, normalizedDist - delta));
3919
+ const h2 = profileFn(Math.min(1, normalizedDist + delta));
3920
+ const gradient = Math.abs((h2 - h1) / (2 * delta));
3921
+ specularIntensity = Math.pow(gradient, 0.7) * glassThickness * 0.6;
3922
+ const rimStart = 0.25;
3923
+ if (normalizedDist < rimStart) {
3924
+ const rimT = 1 - normalizedDist / rimStart;
3925
+ const smoothRim = rimT * rimT * (3 - 2 * rimT);
3926
+ specularIntensity += smoothRim * 0.3;
3927
+ }
3928
+ specularIntensity = Math.min(1, specularIntensity);
3929
+ }
3930
+ const value = Math.round(specularIntensity * 255);
3931
+ const idx = (y * width + x) * 4;
3932
+ data[idx] = value;
3933
+ data[idx + 1] = value;
3934
+ data[idx + 2] = value;
3935
+ data[idx + 3] = 255;
3936
+ }
3937
+ }
3938
+ ctx.putImageData(imageData, 0, 0);
3939
+ return canvas.toDataURL("image/png");
3940
+ }
3941
+ function generateDisplacementMap(config) {
3942
+ return getCachedMaps(config).displacement;
3943
+ }
3944
+ function generateSpecularMap(config) {
3945
+ return getCachedMaps(config).specular;
3946
+ }
3947
+ function supportsBackdropSvgFilter() {
3948
+ if (typeof window === "undefined" || typeof navigator === "undefined") {
3949
+ return false;
3950
+ }
3951
+ const ua = navigator.userAgent;
3952
+ const isSafari = /Safari\//.test(ua) && !/Chrome\//.test(ua);
3953
+ const isFirefox = /Firefox\//.test(ua);
3954
+ const isChrome = /Chrome\//.test(ua) && !isSafari && !isFirefox;
3955
+ return isChrome;
3956
+ }
3957
+ const LiquidGlassFilter = ({
3958
+ filterId,
3959
+ displacementMapUrl,
3960
+ specularMapUrl,
3961
+ width,
3962
+ height,
3963
+ scale,
3964
+ saturation = 1.2,
3965
+ blurAmount = 1,
3966
+ specularIntensity = 0.25
3967
+ }) => {
3968
+ const filterSvg = React.useMemo(
3969
+ () => /* @__PURE__ */ jsxRuntime.jsx(
3970
+ "svg",
3971
+ {
3972
+ style: {
3973
+ position: "absolute",
3974
+ width: 0,
3975
+ height: 0,
3976
+ overflow: "hidden",
3977
+ pointerEvents: "none"
3978
+ },
3979
+ "aria-hidden": "true",
3980
+ children: /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs(
3981
+ "filter",
3982
+ {
3983
+ id: filterId,
3984
+ x: "-50%",
3985
+ y: "-50%",
3986
+ width: "200%",
3987
+ height: "200%",
3988
+ colorInterpolationFilters: "sRGB",
3989
+ children: [
3990
+ /* @__PURE__ */ jsxRuntime.jsx("feGaussianBlur", { in: "SourceGraphic", stdDeviation: blurAmount, result: "blurred" }),
3991
+ /* @__PURE__ */ jsxRuntime.jsx(
3992
+ "feImage",
3993
+ {
3994
+ href: displacementMapUrl,
3995
+ x: "0",
3996
+ y: "0",
3997
+ width,
3998
+ height,
3999
+ result: "displacement_map",
4000
+ preserveAspectRatio: "none"
4001
+ }
4002
+ ),
4003
+ /* @__PURE__ */ jsxRuntime.jsx(
4004
+ "feDisplacementMap",
4005
+ {
4006
+ in: "blurred",
4007
+ in2: "displacement_map",
4008
+ scale,
4009
+ xChannelSelector: "R",
4010
+ yChannelSelector: "G",
4011
+ result: "displaced"
4012
+ }
4013
+ ),
4014
+ /* @__PURE__ */ jsxRuntime.jsx(
4015
+ "feColorMatrix",
4016
+ {
4017
+ in: "displaced",
4018
+ type: "saturate",
4019
+ values: String(saturation),
4020
+ result: "saturated"
4021
+ }
4022
+ ),
4023
+ /* @__PURE__ */ jsxRuntime.jsx(
4024
+ "feImage",
4025
+ {
4026
+ href: specularMapUrl,
4027
+ x: "0",
4028
+ y: "0",
4029
+ width,
4030
+ height,
4031
+ result: "specular_map_raw",
4032
+ preserveAspectRatio: "none"
4033
+ }
4034
+ ),
4035
+ /* @__PURE__ */ jsxRuntime.jsx("feGaussianBlur", { in: "specular_map_raw", stdDeviation: "3", result: "specular_map" }),
4036
+ /* @__PURE__ */ jsxRuntime.jsx("feFlood", { floodColor: "white", floodOpacity: specularIntensity * 0.5, result: "white" }),
4037
+ /* @__PURE__ */ jsxRuntime.jsx("feComposite", { in: "white", in2: "specular_map", operator: "in", result: "highlight" }),
4038
+ /* @__PURE__ */ jsxRuntime.jsx("feBlend", { in: "highlight", in2: "saturated", mode: "screen", result: "final" })
4039
+ ]
4040
+ }
4041
+ ) })
4042
+ }
4043
+ ),
4044
+ [filterId, displacementMapUrl, specularMapUrl, width, height, scale, saturation, blurAmount, specularIntensity]
4045
+ );
4046
+ return filterSvg;
4047
+ };
4048
+ function generateFilterId(prefix = "liquid-glass") {
4049
+ return `${prefix}-${Math.random().toString(36).substr(2, 9)}`;
4050
+ }
4051
+ function useLiquidGlass(options) {
4052
+ const {
4053
+ width,
4054
+ height,
4055
+ profile = DEFAULT_LIQUID_GLASS_CONFIG.profile,
4056
+ bezelWidth = DEFAULT_LIQUID_GLASS_CONFIG.bezelWidth,
4057
+ glassThickness = DEFAULT_LIQUID_GLASS_CONFIG.glassThickness,
4058
+ refractiveIndex = DEFAULT_LIQUID_GLASS_CONFIG.refractiveIndex,
4059
+ animated = true,
4060
+ saturation = 1.3,
4061
+ blurAmount = 1,
4062
+ specularIntensity = 0.3
4063
+ } = options;
4064
+ const [scale, setScaleState] = React.useState(DEFAULT_LIQUID_GLASS_CONFIG.scale);
4065
+ const [displacementMapUrl, setDisplacementMapUrl] = React.useState("");
4066
+ const [specularMapUrl, setSpecularMapUrl] = React.useState("");
4067
+ const scaleSpringRef = React.useRef(null);
4068
+ const physicsIdRef = React.useRef(generatePhysicsId("liquid-glass"));
4069
+ const filterIdRef = React.useRef(generateFilterId());
4070
+ const isSupported = React.useMemo(() => supportsBackdropSvgFilter(), []);
4071
+ React.useEffect(() => {
4072
+ const config = {
4073
+ width,
4074
+ height,
4075
+ bezelWidth,
4076
+ glassThickness,
4077
+ refractiveIndex,
4078
+ profile
4079
+ };
4080
+ const dispMapUrl = generateDisplacementMap(config);
4081
+ const specMapUrl = generateSpecularMap(config);
4082
+ setDisplacementMapUrl(dispMapUrl);
4083
+ setSpecularMapUrl(specMapUrl);
4084
+ }, [width, height, bezelWidth, glassThickness, refractiveIndex, profile]);
4085
+ React.useEffect(() => {
4086
+ scaleSpringRef.current = new Spring1D(scale, {
4087
+ tension: 200,
4088
+ friction: 20,
4089
+ mass: 1,
4090
+ precision: 0.5
4091
+ });
4092
+ return () => {
4093
+ PhysicsEngine.unregister(physicsIdRef.current);
4094
+ };
4095
+ }, []);
4096
+ const setScale = React.useCallback(
4097
+ (targetScale) => {
4098
+ if (!animated) {
4099
+ setScaleState(targetScale);
4100
+ return;
4101
+ }
4102
+ if (!scaleSpringRef.current) {
4103
+ scaleSpringRef.current = new Spring1D(scale, {
4104
+ tension: 200,
4105
+ friction: 20,
4106
+ mass: 1,
4107
+ precision: 0.5
4108
+ });
4109
+ }
4110
+ scaleSpringRef.current.setTarget(targetScale);
4111
+ if (!PhysicsEngine.has(physicsIdRef.current)) {
4112
+ PhysicsEngine.register(physicsIdRef.current, (dt) => {
4113
+ if (!scaleSpringRef.current) return;
4114
+ const result = scaleSpringRef.current.step(dt);
4115
+ setScaleState(result.value);
4116
+ if (result.isSettled) {
4117
+ PhysicsEngine.unregister(physicsIdRef.current);
4118
+ }
4119
+ });
4120
+ }
4121
+ },
4122
+ [animated, scale]
4123
+ );
4124
+ const filterComponent = React.useMemo(() => {
4125
+ if (!displacementMapUrl || !specularMapUrl || !isSupported) {
4126
+ return null;
4127
+ }
4128
+ return React.createElement(LiquidGlassFilter, {
4129
+ filterId: filterIdRef.current,
4130
+ displacementMapUrl,
4131
+ specularMapUrl,
4132
+ width,
4133
+ height,
4134
+ scale,
4135
+ saturation,
4136
+ blurAmount,
4137
+ specularIntensity
4138
+ });
4139
+ }, [displacementMapUrl, specularMapUrl, width, height, scale, isSupported, saturation, blurAmount, specularIntensity]);
4140
+ const style = React.useMemo(() => {
4141
+ if (!isSupported) {
4142
+ return {
4143
+ backdropFilter: "blur(12px) saturate(1.2)",
4144
+ WebkitBackdropFilter: "blur(12px) saturate(1.2)"
4145
+ };
4146
+ }
4147
+ return {
4148
+ backdropFilter: `url(#${filterIdRef.current})`,
4149
+ WebkitBackdropFilter: `url(#${filterIdRef.current})`
4150
+ };
4151
+ }, [isSupported]);
4152
+ return {
4153
+ filterComponent,
4154
+ style,
4155
+ setScale,
4156
+ scale,
4157
+ isSupported
4158
+ };
4159
+ }
1878
4160
  exports.Card = Card;
1879
4161
  exports.DEFAULT_FRICTION_CONFIG = DEFAULT_FRICTION_CONFIG;
4162
+ exports.DEFAULT_LIQUID_GLASS_CONFIG = DEFAULT_LIQUID_GLASS_CONFIG;
1880
4163
  exports.DEFAULT_SPRING_CONFIG = DEFAULT_SPRING_CONFIG;
1881
4164
  exports.Draggable = Draggable;
4165
+ exports.LiquidGlassFilter = LiquidGlassFilter;
1882
4166
  exports.Momentum = Momentum;
1883
4167
  exports.Momentum1D = Momentum1D;
1884
4168
  exports.PhysicsEngine = PhysicsEngine;
1885
4169
  exports.PhysicsProvider = PhysicsProvider;
4170
+ exports.PhysicsRangeSlider = PhysicsRangeSlider;
4171
+ exports.PhysicsSlider = PhysicsSlider;
4172
+ exports.PhysicsStepSlider = PhysicsStepSlider;
4173
+ exports.PhysicsVerticalSlider = PhysicsVerticalSlider;
4174
+ exports.SegmentedControl = SegmentedControl;
1886
4175
  exports.Spring = Spring;
1887
4176
  exports.Spring1D = Spring1D;
1888
4177
  exports.Vector2 = Vector2;
1889
4178
  exports.VelocityTracker = VelocityTracker;
1890
4179
  exports.VelocityTracker1D = VelocityTracker1D;
1891
4180
  exports.cardBaseStyles = cardBaseStyles;
4181
+ exports.clearMapCache = clearMapCache;
1892
4182
  exports.combineLiftStyle = combineLiftStyle;
1893
4183
  exports.combineStretchStyle = combineStretchStyle;
4184
+ exports.defaultSegmentedControlConfig = defaultSegmentedControlConfig;
4185
+ exports.defaultSliderConfig = defaultSliderConfig;
4186
+ exports.generateDisplacementMap = generateDisplacementMap;
4187
+ exports.generateFilterId = generateFilterId;
1894
4188
  exports.generatePhysicsId = generatePhysicsId;
4189
+ exports.generateSpecularMap = generateSpecularMap;
1895
4190
  exports.gentle = gentle;
4191
+ exports.getCachedMaps = getCachedMaps;
1896
4192
  exports.getFrictionConfig = getFrictionConfig;
1897
4193
  exports.getPreset = getPreset;
4194
+ exports.getProfileFunction = getProfileFunction;
1898
4195
  exports.getSpringConfig = getSpringConfig;
1899
4196
  exports.ios = ios;
4197
+ exports.preloadMaps = preloadMaps;
1900
4198
  exports.presets = presets;
4199
+ exports.segmentedControlPresets = segmentedControlPresets;
4200
+ exports.sliderPresets = sliderPresets;
1901
4201
  exports.smooth = smooth;
4202
+ exports.smootherstep = smootherstep;
1902
4203
  exports.snappy = snappy;
1903
4204
  exports.stiff = stiff;
4205
+ exports.supportsBackdropSvgFilter = supportsBackdropSvgFilter;
1904
4206
  exports.useDrag = useDrag;
1905
4207
  exports.useFlick = useFlick;
1906
4208
  exports.useFrictionConfig = useFrictionConfig;
1907
4209
  exports.useGesture = useGesture;
1908
4210
  exports.useLift = useLift;
1909
4211
  exports.useLiftConfig = useLiftConfig;
4212
+ exports.useLiquidGlass = useLiquidGlass;
1910
4213
  exports.usePhysicsConfig = usePhysicsConfig;
1911
4214
  exports.usePhysicsContext = usePhysicsContext;
1912
4215
  exports.useSpring = useSpring;