diff --git a/.changeset/fast-meals-call.md b/.changeset/fast-meals-call.md
new file mode 100644
index 0000000000..3b006f1b2d
--- /dev/null
+++ b/.changeset/fast-meals-call.md
@@ -0,0 +1,9 @@
+---
+'@alfalab/core-components-bottom-sheet': patch
+'@alfalab/core-components': patch
+---
+
+##### BottomSheet
+
+- Исправлено нестабильное поведение `BottomSheet` при динамической смене `magneticAreas`.
+- Если после обновления `magneticAreas` активный индекс выходит за границы массива (например, когда массив стал короче), компонент теперь переводит шторку в ближайшую доступную магнитную область вместо сброса в offset `0`. Это устраняет рывки и визуальные скачки, сохраняя ближайшую предыдущую высоту.
diff --git a/packages/bottom-sheet/src/component.test.tsx b/packages/bottom-sheet/src/component.test.tsx
index 9d835b1d91..fb07f97b43 100644
--- a/packages/bottom-sheet/src/component.test.tsx
+++ b/packages/bottom-sheet/src/component.test.tsx
@@ -494,6 +494,38 @@ describe('Bottom sheet', () => {
await waitFor(() => expect(onMagnetizeEnd).toHaveBeenCalledWith(1));
});
+ it('should remap active magnetic area after magneticAreas change', async () => {
+ const onMagnetize = jest.fn();
+
+ const { rerender } = render(
+ ,
+ );
+
+ await waitFor(() => expect(onMagnetize).toHaveBeenCalledWith(2));
+
+ onMagnetize.mockClear();
+
+ rerender(
+ ,
+ );
+
+ await waitFor(() => expect(onMagnetize).toHaveBeenCalledWith(1));
+ });
+
it('should call onMagnetize and onMagnetizeEnd prop after closing', async () => {
const onMagnetize = jest.fn();
const onMagnetizeEnd = jest.fn();
diff --git a/packages/bottom-sheet/src/component.tsx b/packages/bottom-sheet/src/component.tsx
index b3ed3ac463..a0b4e2048f 100644
--- a/packages/bottom-sheet/src/component.tsx
+++ b/packages/bottom-sheet/src/component.tsx
@@ -141,6 +141,7 @@ export const BottomSheet = forwardRef(
fullHeight = adjustContainerHeight(fullHeight);
const initialIndexRef = useRef(initialActiveAreaIndex);
+ const prevMagneticAreasRef = useRef();
const prevMagneticAreasPropRef = useRef | undefined>(
magneticAreasProp,
);
@@ -259,6 +260,25 @@ export const BottomSheet = forwardRef(
};
const getActiveAreaIndex = (area?: number) => magneticAreas.findIndex((a) => a === area);
+ const getClosestAreaIndex = (area: number, areas: number[]) =>
+ areas.reduce(
+ (result, currentArea, currentIndex) => {
+ const distance = Math.abs(currentArea - area);
+
+ if (distance < result.distance) {
+ return {
+ distance,
+ index: currentIndex,
+ };
+ }
+
+ return result;
+ },
+ {
+ distance: Number.POSITIVE_INFINITY,
+ index: 0,
+ },
+ ).index;
const setSheetHeight = () => {
if (sheetRef.current) {
@@ -552,8 +572,10 @@ export const BottomSheet = forwardRef(
if (!isFirstRender) {
const magneticAreasPropChanged =
prevMagneticAreasPropRef.current !== magneticAreasProp;
+ const prevMagneticAreas = prevMagneticAreasRef.current;
prevMagneticAreasPropRef.current = magneticAreasProp;
+ prevMagneticAreasRef.current = magneticAreas;
if (magneticAreasPropChanged) {
setSkipTransition(true);
@@ -565,6 +587,16 @@ export const BottomSheet = forwardRef(
setSheetOffset(isNil(idx) ? 0 : lastMagneticArea - magneticAreas[idx]);
setActiveAreaIdx(isNil(idx) ? magneticAreas.length - 1 : idx);
+ } else if (magneticAreasPropChanged && prevMagneticAreas?.length) {
+ const prevActiveArea =
+ prevMagneticAreas[activeAreaIdx] ??
+ prevMagneticAreas[prevMagneticAreas.length - 1];
+ const nextAreaIdx = getClosestAreaIndex(prevActiveArea, magneticAreas);
+ const nextArea = magneticAreas[nextAreaIdx];
+
+ setSheetOffset(lastMagneticArea - nextArea);
+ setActiveAreaIdx(nextAreaIdx);
+ onMagnetize?.(nextAreaIdx);
} else {
setSheetOffset(activeArea ? lastMagneticArea - activeArea : 0);
}