Skip to content

Commit

Permalink
commit
Browse files Browse the repository at this point in the history
  • Loading branch information
inomdzhon committed Sep 30, 2024
1 parent 1e33e88 commit fba65d5
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 93 deletions.
15 changes: 14 additions & 1 deletion packages/vkui/src/components/ModalPageV2/ModalPage.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,20 @@ export const DynamicModalPage: Story = {
</div>
</HorizontalScroll>
</Group>
<CustomSelect options={cities} forceDropdownPortal={false} />
<FormItem>
<CustomSelect
options={cities}
placeholder="forceDropdownPortal={false}"
forceDropdownPortal={false}
/>
</FormItem>
{/* <FormItem>
<CustomSelect
options={cities}
placeholder="forceDropdownPortal={true}"
forceDropdownPortal={true}
/>
</FormItem> */}
<Div>
But I must explain to you how all this mistaken idea of denouncing pleasure and
praising pain was born and I will give you a complete account of the system, and
Expand Down
157 changes: 75 additions & 82 deletions packages/vkui/src/lib/bottomSheet/BottomSheetController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export class BottomSheetController {
private readonly scrollableChildrenObserver: UIScrollableChildrenObserver;
private readonly lowerBound = 75;

private panState: 'idle' | 'start' | 'moving' = 'idle';
private pannedEl: HTMLElement | null = null;
private isPanStarted = false;
private isMoving = false;
private rafId: number | null = null;
// private expanded = false;

private targetHeight = 0;

Check failure on line 23 in packages/vkui/src/lib/bottomSheet/BottomSheetController.ts

View workflow job for this annotation

GitHub Actions / Run linters

'targetHeight' is declared but its value is never read.
Expand All @@ -39,53 +39,63 @@ export class BottomSheetController {
}

panStart(event: TouchEvent, currentShiftY: number) {
if (this.panState !== 'idle') {
return;
}

const target = event.target as HTMLElement;

if (hasSelectionWithRangeType(target)) {
return;
}

this.panGestureRecognizer.setStartCoords(event);
this.currentShiftY = currentShiftY;
this.panState = 'start';
this.pannedEl = target;
this.isPanStarted = true;
this.currentShiftY = currentShiftY;
this.panGestureRecognizer.setStartCoords(event);
}

panMove(event: TouchEvent) {
if (!this.isPanStarted || hasSelectionWithRangeType(event.target)) {
if (this.panState === 'idle') {
return;
}

this.panGestureRecognizer.setInitialTimeOnce();
this.panGestureRecognizer.setEndCoords(event);

const scheduleCallingShouldStartMovingFn = () => {
if (this.canStartMoving(this.getPanDirection())) {
if (this.panState === 'moving') {
if (event.cancelable) {
event.preventDefault();
}

if (this.targetHeight === 0) {
this.targetHeight = this.targetEl.offsetHeight;
}

const { y1, y2 } = this.panGestureRecognizer;
const offsetPercent = y2 - y1 - MINIMUM_DISTANCE_FOR_MOVING_START;
this.nextShiftY = (offsetPercent / this.targetEl.offsetHeight) * 100;
this.nextShiftY += this.currentShiftY;
this.nextShiftY = rubberbandIfOutOfBounds(this.nextShiftY, 0, 100);

if (this.currentShiftY !== this.nextShiftY) {
this.isMoving = true;
this.animationBackdropController.setInlineStyles(this.nextShiftY);
this.animationTargetController.setInlineStyles(this.nextShiftY);
}
if (this.rafId === null) {
this.rafId = requestAnimationFrame(this.scheduleCallingShouldStartMovingFn);
}
} else {
if (
this.shouldBePreventedIfPanGestureDistanceIsNotAsExpected() ||
// Может быть `null` если нажали на Shadow DOM.
this.pannedEl === null ||
this.shouldBePreventedIfPannedElIsScrolling(this.pannedEl) ||
this.shouldBePreventedIfPannedElIsExternal(this.pannedEl) ||
this.shouldBePreventedByDataAttribute(this.pannedEl)
) {
return;
}
};

requestAnimationFrame(scheduleCallingShouldStartMovingFn);
this.panState = 'moving';
this.targetHeight = this.targetEl.offsetHeight;
this.panGestureRecognizer.setStartCoords(event);
}
}

panEnd() {
if (this.isMoving) {
if (this.rafId !== null) {
cancelAnimationFrame(this.rafId);
this.rafId = null;
}

if (this.panState === 'moving') {
if (this.nextShiftY > this.lowerBound || this.isPanGestureForClose()) {
this.onClose();
} else {
Expand All @@ -96,8 +106,7 @@ export class BottomSheetController {
}
}

this.isPanStarted = false;
this.isMoving = false;
this.panState = 'idle';
this.targetHeight = 0;
this.nextShiftY = 0;
this.pannedEl = null;
Expand All @@ -112,77 +121,61 @@ export class BottomSheetController {
this.scrollableChildrenObserver.unobserve();
}

private canStartMoving(panDirection?: 'up' | 'down') {
const hasExpectedPanGestureDistance =
this.panGestureRecognizer.distance() > MINIMUM_DISTANCE_FOR_MOVING_START;
private readonly scheduleCallingShouldStartMovingFn = () => {
const { y1, y2 } = this.panGestureRecognizer;
const offsetPercent = y2 - y1;

if (!hasExpectedPanGestureDistance) {
return false;
}
this.nextShiftY = (offsetPercent / this.targetEl.offsetHeight) * 100;
this.nextShiftY += this.currentShiftY;
this.nextShiftY = rubberbandIfOutOfBounds(this.nextShiftY, 0, 100);

if (this.checkScrollablePredicates()) {
return false;
if (this.currentShiftY !== this.nextShiftY) {
this.animationBackdropController.setInlineStyles(this.nextShiftY);
this.animationTargetController.setInlineStyles(this.nextShiftY);
}

switch (panDirection) {
case 'up':
if (this.scrollableChildrenObserver.hasYScroll) {
return this.currentShiftY !== 0;
}
this.rafId = null;
};

return true;
case 'down':
default:
return true;
}
}

private checkScrollablePredicates() {
if (!this.pannedEl) {
private isPanGestureForClose() {
const panDirection = this.panGestureRecognizer.direction();
if (panDirection.axis !== 'y' || panDirection.direction !== 1) {
return false;
}

this.scrollableChildrenObserver.observeOnce(this.targetEl, this.pannedEl);
const velocity = this.panGestureRecognizer.velocity();
return velocity.y >= MINIMUM_PAN_GESTURE_FOR_TRIGGER_CLOSE;
}

if (!this.scrollableChildrenObserver.isScrollable) {
return false;
}
private shouldBePreventedIfPanGestureDistanceIsNotAsExpected() {
return this.panGestureRecognizer.distance() < MINIMUM_DISTANCE_FOR_MOVING_START;
}

if (this.scrollableChildrenObserver.isScrolling) {
return true;
}
private shouldBePreventedIfPannedElIsExternal(pannedEl: HTMLElement) {
return !this.targetEl.contains(pannedEl);
}

private shouldBePreventedByDataAttribute(pannedEl: HTMLElement) {
// eslint-disable-next-line no-restricted-properties
return pannedEl.closest('[data-vkui-prevent-swipe=true]');
}

if (this.scrollableChildrenObserver.hasXScroll) {
const delta = this.panGestureRecognizer.delta();
private shouldBePreventedIfPannedElIsScrolling(pannedEl: HTMLElement) {
this.scrollableChildrenObserver.observeOnce(this.targetEl, pannedEl);

if (Math.abs(delta.x) > Math.abs(delta.y)) {
if (this.scrollableChildrenObserver.isScrollable) {
if (this.scrollableChildrenObserver.isScrolling) {
return true;
}
}

if (this.scrollableChildrenObserver.hasYScroll) {
return this.scrollableChildrenObserver.isYScrolled;
const panDirection = this.panGestureRecognizer.direction();
return (
(this.scrollableChildrenObserver.hasXScroll && panDirection.axis === 'x') ||
(this.scrollableChildrenObserver.hasYScroll &&
(panDirection.direction === -1 || this.scrollableChildrenObserver.isYScrolled))
);
}

return false;
}

private getPanDirection() {
const delta = this.panGestureRecognizer.delta();
if (delta.y > 0) {
return 'down';
} else if (delta.y < 0) {
return 'up';
}
return;
}

private isPanGestureForClose() {
if (this.getPanDirection() !== 'down') {
return false;
}

const velocity = this.panGestureRecognizer.velocity();
return velocity.y >= MINIMUM_PAN_GESTURE_FOR_TRIGGER_CLOSE;
}
}
21 changes: 12 additions & 9 deletions packages/vkui/src/lib/touch/UIPanGestureRecognizer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { getFirstTouchEventData } from '../dom';

export type VCoords = { x: number; y: number };
export type Direction = { axis: 'x' | 'y'; direction: -1 | 1 | null };

export type Coords = { x: number; y: number };

const DEFAULT_INITIAL_TIME = 0;
const MILLISECONDS = 1000;
Expand Down Expand Up @@ -35,10 +37,7 @@ export class UIPanGestureRecognizer {
this.y2 = clientY;
}

delta(): {
x: number;
y: number;
} {
delta(): Coords {
return {
x: this.x2 - this.x1,
y: this.y2 - this.y1,
Expand All @@ -50,10 +49,7 @@ export class UIPanGestureRecognizer {
return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
}

velocity(): {
x: number;
y: number;
} {
velocity(): Coords {
const deltaTime = (Date.now() - this.initialTime) / MILLISECONDS;

if (deltaTime <= 0) {
Expand All @@ -72,6 +68,13 @@ export class UIPanGestureRecognizer {
return degrees < 0 ? 360 + degrees : degrees;
}

direction(): Direction {
const delta = this.delta();
return Math.abs(delta.x) > Math.abs(delta.y)
? { axis: 'x', direction: delta.x > 0 ? 1 : delta.x < 0 ? -1 : null }
: { axis: 'y', direction: delta.y > 0 ? 1 : delta.y < 0 ? -1 : null };
}

reset(): void {
this.initialTime = DEFAULT_INITIAL_TIME;
this.x1 = this.y1 = 0;
Expand Down
5 changes: 4 additions & 1 deletion packages/vkui/src/lib/touch/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export type * from './functions';
export { getSupportedEvents, coordX, coordY, touchEnabled, rubber } from './functions';

export type { VCoords } from './UIPanGestureRecognizer';
export type {
Direction as UIPanGestureRecognizerDirection,
Coords as UIPanGestureRecognizerCoords,
} from './UIPanGestureRecognizer';
export { UIPanGestureRecognizer } from './UIPanGestureRecognizer';

0 comments on commit fba65d5

Please sign in to comment.