Skip to content

Commit

Permalink
feat(View): increase swipe back zone (#5725)
Browse files Browse the repository at this point in the history
Увеличил зону срабатывания свайпа назад, тем самым приблизив поведение к нативным приложениям. В частности, как в приложении ВКонтакте на iOS.

> **Note**
>
> Для `ViewInfitnite` повторил всё, что для `View`.

h.2 Побочные изменения

> Спасибо @shevchux и @mendrew за замечания ❤️ Иначе пропустил бы эти кейсы)

Во-первых, немного пошатал тесты.

Во-вторых, оптимизировал код: убрал из зависимостей `useEffect` лишнее; состояние `swipeBackPrevented` теперь храню в `useRef` вместо `useState`.

Во-третьих, поправил следующие кейсы:

h.3 Кейс 1. Элементы с горизонтальным скроллом.

Добавил обнаружение смещения горизонтального скролла (в частности необходимо для `HorizontallScroll`), чтобы предотвращать свайп бек если пользователей взаимодействует с элементами, у которых есть горизонтальный скролл.

Свайп бек включается:
- либо если положение скролла находится в начале, иначе свайп бэк игнорируется. Если мы проскролили к началу, то нам необходимо отжать палец и заново синицировать свайп бэк, чтобы он сработал;
- либо если потянули за край экрана слева (спасибо, @shevchux за предложение).

> Вдохновился поведением у приложения ВКонтакте на iOS.

> Использую состояние `swipeBackPrevented`, чтобы вызывать функцию обнаружения только один раз во время начала жеста. Чтобы получить родителей со скроллом, применяю утилиту [getOverflowAncestors()](https://github.com/floating-ui/floating-ui/blob/%40floating-ui/react-dom%402.0.2/packages/utils/test/getOverflowAncestors.test.ts) из бибилотеки Floating UI.

h.3 Кейс 2. Компонент [Gallery](https://vkcom.github.io/VKUI/5.7.2/#/Gallery).

Добавил `stopPropagation()` на `onDragStart` для `Gallery`, чтобы предотвращать свайп бэк.

---

Добавил в доки `View` (стайлгайд и сторибук) примеры с кейсами 1 и 2.

По замечанию @shevchux, в доке у `CellButton`'ов выставил `stopPropagation={false}`, чтобы не путало пользователей. Иначе свайп бэк блокируется.
  • Loading branch information
inomdzhon authored Sep 19, 2023
1 parent de2126d commit 9525ded
Show file tree
Hide file tree
Showing 9 changed files with 620 additions and 198 deletions.
1 change: 1 addition & 0 deletions packages/vkui/src/components/BaseGallery/BaseGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ export const BaseGallery = ({
};

const onStart = (e: TouchEvent) => {
e.originalEvent.stopPropagation();
onDragStart?.(e);
setShiftState((prevState) => ({ ...prevState, animation: false }));
};
Expand Down
37 changes: 34 additions & 3 deletions packages/vkui/src/components/View/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ const [activePanel, setActivePanel] = useState('panel1');

**Блокировка свайпа (вариант #1)**

Компоненты, которые сами обрабатывают жесты (например, карта), могут конфликтовать со свайпбеком — повесьте на них свойство `data-vkui-swipe-back={false}`
Компоненты, которые сами обрабатывают жесты (например, карта или кастомный компонент по типу карусели), могут конфликтовать со свайпбеком. Вот как можно это решить:

- либо повесьте на них свойство `data-vkui-swipe-back={false}`;
- либо вызывайте `event.stopPropagation()` на событие `onStartX` компонента [Touch](#/Touch).

<br />

Expand Down Expand Up @@ -141,7 +144,9 @@ const MainPanelContent = ({ onProfileClick }) => {
<PanelHeader>Main</PanelHeader>
<Group>
<div style={{ height: 200 }} />
<CellButton onClick={onProfileClick}>Профиль</CellButton>
<CellButton stopPropagation={false} onClick={onProfileClick}>
Профиль
</CellButton>
<div style={{ height: 600 }} />
</Group>
</React.Fragment>
Expand All @@ -159,7 +164,33 @@ const ProfilePanelContent = ({ onSettingsClick }) => {
</Div>
</Group>
<Group>
<CellButton onClick={onSettingsClick}>Настройки</CellButton>
<CellButton stopPropagation={false} onClick={onSettingsClick}>
Настройки
</CellButton>
</Group>
<Group
header={<Header>Gallery</Header>}
description="Полностью блокирует свайпбэк (за счёт event.stopPropagation() на onStartX компонента Touch)"
>
<Gallery slideWidth="90%" bullets="dark">
<div style={{ backgroundColor: 'var(--vkui--color_background_negative)' }} />
<img src="https://placebear.com/1024/640" style={{ display: 'block' }} />
<div style={{ backgroundColor: 'var(--vkui--color_background_accent)' }} />
</Gallery>
</Group>
<Group
header={<Header>HorizontalScroll</Header>}
description="Свайпбэк срабатывает либо если мы тянем за левый край экрана, либо если позиция горизонтального скролла равна нулю"
>
<HorizontalScroll>
<div style={{ display: 'flex' }}>
{getRandomUsers(15).map((user) => (
<HorizontalCell key={user.id} size="s" header={user.first_name}>
<Avatar size={56} src={user.photo_100} />
</HorizontalCell>
))}
</div>
</HorizontalScroll>
</Group>
</React.Fragment>
);
Expand Down
38 changes: 36 additions & 2 deletions packages/vkui/src/components/View/View.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import { Meta, StoryObj } from '@storybook/react';
import vkBridge from '@vkontakte/vk-bridge';
import { Platform } from '../../lib/platform';
import { CanvasFullLayout, DisableCartesianParam } from '../../storybook/constants';
import { getRandomUsers } from '../../testing/mock';
import { Alert } from '../Alert/Alert';
import { Avatar } from '../Avatar/Avatar';
import { CellButton } from '../CellButton/CellButton';
import { ConfigProviderOverride } from '../ConfigProvider/ConfigProviderOverride';
import { Div } from '../Div/Div';
import { FormItem } from '../FormItem/FormItem';
import { Gallery } from '../Gallery/Gallery';
import { Group } from '../Group/Group';
import { Header } from '../Header/Header';
import { HorizontalCell } from '../HorizontalCell/HorizontalCell';
import { HorizontalScroll } from '../HorizontalScroll/HorizontalScroll';
import { Input } from '../Input/Input';
import { Panel } from '../Panel/Panel';
import { PanelHeader } from '../PanelHeader/PanelHeader';
Expand All @@ -32,7 +38,9 @@ const MainPanelContent = ({ onProfileClick }: { onProfileClick: () => void }) =>
<React.Fragment>
<PanelHeader>Main</PanelHeader>
<Group>
<CellButton onClick={onProfileClick}>Профиль</CellButton>
<CellButton stopPropagation={false} onClick={onProfileClick}>
Профиль
</CellButton>
</Group>
</React.Fragment>
);
Expand All @@ -49,7 +57,33 @@ const ProfilePanelContent = ({ onSettingsClick }: { onSettingsClick: () => void
</Div>
</Group>
<Group>
<CellButton onClick={onSettingsClick}>Настройки</CellButton>
<CellButton stopPropagation={false} onClick={onSettingsClick}>
Настройки
</CellButton>
</Group>
<Group
header={<Header>Gallery</Header>}
description="Полностью блокирует свайпбэк (за счёт event.stopPropagation() на onStartX компонента Touch)"
>
<Gallery slideWidth="90%" bullets="dark">
<div style={{ backgroundColor: 'var(--vkui--color_background_negative)' }} />
<img src="https://placebear.com/1024/640" style={{ display: 'block' }} />
<div style={{ backgroundColor: 'var(--vkui--color_background_accent)' }} />
</Gallery>
</Group>
<Group
header={<Header>HorizontalScroll</Header>}
description="Свайпбэк срабатывает либо если мы тянем за левый край экрана, либо если позиция горизонтального скролла равна нулю"
>
<HorizontalScroll>
<div style={{ display: 'flex' }}>
{getRandomUsers(15).map((user) => (
<HorizontalCell key={user.id} size="s" header={user.first_name}>
<Avatar size={56} src={user.photo_100} />
</HorizontalCell>
))}
</div>
</HorizontalScroll>
</Group>
</React.Fragment>
);
Expand Down
Loading

0 comments on commit 9525ded

Please sign in to comment.