Skip to content

Commit

Permalink
Merge pull request #1992 from VKCOM/feat/gallery-autosize
Browse files Browse the repository at this point in the history
Feat/gallery autosize
  • Loading branch information
thoughtspile committed Oct 26, 2021
2 parents b71447e + 91d023c commit 273a35d
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 113 deletions.
5 changes: 2 additions & 3 deletions src/components/Gallery/Gallery.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
.Gallery {
position: relative;
overflow: hidden;
height: 100px;
}

.Gallery__viewport {
Expand All @@ -18,13 +17,13 @@
display: flex;
width: 100%;
height: 100%;
min-height: 100px;
cursor: grab;
align-items: stretch;
}

.Gallery__slide {
width: 100%;
height: 100%;
flex: 0 0 auto;
max-width: 100%;
overflow: hidden;
Expand All @@ -36,7 +35,7 @@

.Gallery__slide > * {
width: 100%;
height: 100%;
min-height: 100%;
}

.Gallery__bullets {
Expand Down
198 changes: 89 additions & 109 deletions src/components/Gallery/Readme.md
Original file line number Diff line number Diff line change
@@ -1,113 +1,93 @@
```jsx
class Example extends React.Component {
const [slideIndex, setSlideIndex] = useState(0);
const [isDraggable, setIsDraggable] = useState(true);
const [showArrows, setShowArrows] = useState(true);

constructor (props) {
<View activePanel="gallery">
<Panel nav="gallery">
<PanelHeader>Gallery</PanelHeader>
<Group header={<Header mode="secondary">Sticks right</Header>}>
<Gallery
slideWidth="90%"
bullets="dark"
>
<div style={{ backgroundColor: 'var(--destructive)' }} />
<img src="https://picsum.photos/1024/640" style={{ display: 'block'}} />
<div style={{ backgroundColor: 'var(--accent)' }} />
</Gallery>
</Group>
<Group header={<Header mode="secondary">Sticks left</Header>}>
<Gallery
slideWidth="90%"
align="right"
>
<div style={{ height: 150, backgroundColor: 'var(--destructive)' }} />
<div style={{ backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ backgroundColor: 'var(--accent)' }} />
</Gallery>
</Group>
<Group header={<Header mode="secondary">Centered</Header>}>
<Gallery
slideWidth="90%"
align="center"
>
<div style={{ backgroundColor: 'var(--destructive)' }} />
<div style={{ backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ backgroundColor: 'var(--accent)' }} />
</Gallery>
</Group>
<Group header={<Header mode="secondary">Custom width</Header>}>
<Gallery
slideWidth="custom"
style={{ height: 150 }}
>
<div style={{ width: 200, backgroundColor: 'var(--destructive)' }} />
<div style={{ width: 120, backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ width: 70, backgroundColor: 'var(--accent)' }} />
<div style={{ width: 220, backgroundColor: 'var(--icon_secondary)' }} />
</Gallery>
</Group>
<Group header={<Header mode="secondary">Arrows</Header>}>
<Gallery
slideWidth="90%"
style={{ height: 150 }}
bullets="dark"
showArrows
>
<div style={{ backgroundColor: 'var(--destructive)' }} />
<div style={{ backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ backgroundColor: 'var(--accent)' }} />
</Gallery>
</Group>
<Group header={<Header mode="secondary">Controled</Header>}>
<Gallery
slideWidth="90%"
align="center"
style={{ height: 150 }}
slideIndex={slideIndex}
onChange={setSlideIndex}
isDraggable={isDraggable}
showArrows={showArrows}
>
<div style={{ backgroundColor: 'var(--destructive)' }} />
<div style={{ backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ backgroundColor: 'var(--accent)' }} />
</Gallery>

super(props);

this.state = {
slideIndex: 0,
isDraggable: true,
showArrows: true,
}
}

render () {
const { isDraggable, showArrows, slideIndex } = this.state
return (
<View activePanel="gallery">
<Panel id="gallery">
<PanelHeader>Gallery</PanelHeader>
<Group header={<Header mode="secondary">Sticks right</Header>}>
<Gallery
slideWidth="90%"
style={{ height: 150 }}
bullets="dark"
>
<div style={{ backgroundColor: 'var(--destructive)' }} />
<div style={{ backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ backgroundColor: 'var(--accent)' }} />
</Gallery>
</Group>
<Group header={<Header mode="secondary">Sticks left</Header>}>
<Gallery
slideWidth="90%"
align="right"
style={{ height: 150 }}
>
<div style={{ backgroundColor: 'var(--destructive)' }} />
<div style={{ backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ backgroundColor: 'var(--accent)' }} />
</Gallery>
</Group>
<Group header={<Header mode="secondary">Centered</Header>}>
<Gallery
slideWidth="90%"
align="center"
style={{ height: 150 }}
>
<div style={{ backgroundColor: 'var(--destructive)' }} />
<div style={{ backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ backgroundColor: 'var(--accent)' }} />
</Gallery>
</Group>
<Group header={<Header mode="secondary">Custom width</Header>}>
<Gallery
slideWidth="custom"
style={{ height: 150 }}
>
<div style={{ width: 200, backgroundColor: 'var(--destructive)' }} />
<div style={{ width: 120, backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ width: 70, backgroundColor: 'var(--accent)' }} />
<div style={{ width: 220, backgroundColor: 'var(--icon_secondary)' }} />
</Gallery>
</Group>
<Group header={<Header mode="secondary">Arrows</Header>}>
<Gallery
slideWidth="90%"
style={{ height: 150 }}
bullets="dark"
showArrows
>
<div style={{ backgroundColor: 'var(--destructive)' }} />
<div style={{ backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ backgroundColor: 'var(--accent)' }} />
</Gallery>
</Group>
<Group header={<Header mode="secondary">Controled</Header>}>
<Gallery
slideWidth="90%"
align="center"
style={{ height: 150 }}
slideIndex={slideIndex}
onChange={slideIndex => this.setState({slideIndex})}
isDraggable={isDraggable}
showArrows={showArrows}
>
<div style={{ backgroundColor: 'var(--destructive)' }} />
<div style={{ backgroundColor: 'var(--button_commerce_background)' }} />
<div style={{ backgroundColor: 'var(--accent)' }} />
</Gallery>

<FormItem>
<Checkbox checked={isDraggable} onChange={e => this.setState({ isDraggable: e.target.checked })}>
isDraggable
</Checkbox>
<Checkbox checked={showArrows} onChange={e => this.setState({ showArrows: e.target.checked })}>
showArrows
</Checkbox>
</FormItem>
<FormItem>
<Button size='l' stretched onClick={() => this.setState({ slideIndex: (slideIndex + 1) % 3 })}>
Next slide
</Button>
</FormItem>
</Group>
</Panel>
</View>
)
}
}

<Example />
<FormItem>
<Checkbox checked={isDraggable} onChange={e => setIsDraggable(e.target.checked)}>
isDraggable
</Checkbox>
<Checkbox checked={showArrows} onChange={e => setShowArrows(e.target.checked)}>
showArrows
</Checkbox>
</FormItem>
<FormItem>
<Button size='l' stretched onClick={() => setSlideIndex((slideIndex + 1) % 3)}>
Next slide
</Button>
</FormItem>
</Group>
</Panel>
</View>
```
9 changes: 8 additions & 1 deletion src/components/Touch/Touch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { createElement } from 'react';
// Настоящего Touch нет в jsdom: https://github.com/jsdom/jsdom/issues/1508
const asClientPos = ([clientX = 0, clientY = 0] = []): Touch & MouseEvent => ({ clientX, clientY }) as any;

function fireMouseSwipe(e: HTMLElement, [start, ...move]: any[], ops: { startEl?: HTMLElement } = {}) {
function fireMouseSwipe(e: HTMLElement, [start, ...move]: any[], ops: { startEl?: HTMLElement; end?: boolean } = {}) {
fireEvent.mouseDown(ops.startEl || e, asClientPos(start));
move.forEach((p) => fireEvent.mouseMove(e, asClientPos(p)));
fireEvent.mouseUp(e, asClientPos(move[move.length - 1]));
Expand Down Expand Up @@ -141,6 +141,13 @@ describe('Touch', () => {
isSlideX: false,
}));
});
it('calls onEnd when unmounting', () => {
const handlers = makeHandlers();
const h = render(<Touch {...handlers} data-testid="__t__" />);
fireGesture(screen.getByTestId('__t__'), [[20, 20], [20, 30]], { end: false });
h.unmount();
expect(handlers.onEnd).toBeCalledTimes(1);
});
if (input === 'touch') {
it('stops gesture if multitouch', () => {
const handlers = makeHandlers();
Expand Down
12 changes: 12 additions & 0 deletions src/components/Touch/Touch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { HasComponent, HasRootRef } from '../../types';
import { useDOM } from '../../lib/dom';
import { useExternRef } from '../../hooks/useExternRef';
import { useEventListener } from '../../hooks/useEventListener';
import { useUnmountCleanup } from '../../hooks/useUnmountCleanup';
import { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';

export interface TouchProps extends React.AllHTMLAttributes<HTMLElement>, HasRootRef<HTMLElement>, HasComponent {
Expand Down Expand Up @@ -78,6 +79,8 @@ export const Touch: React.FC<TouchProps> = ({
}: TouchProps) => {
const { document } = useDOM();
const events = React.useMemo(getSupportedEvents, []);
// Save last event to trigger end* on unmount
const lastEvent = React.useRef<VKUITouchEvent>();
const didSlide = React.useRef(false);
const gesture = React.useRef<Partial<Gesture>>({});
const handle = (e: VKUITouchEvent, handers: TouchEventHandler[]) => {
Expand All @@ -91,6 +94,7 @@ export const Touch: React.FC<TouchProps> = ({
const enterHandler = useEventListener(usePointerHover ? 'pointerenter' : 'mouseenter', onEnter);
const leaveHandler = useEventListener(usePointerHover ? 'pointerleave' : 'mouseleave', onLeave);
const startHandler = useEventListener(events[0], (e: VKUITouchEvent) => {
lastEvent.current = e;
gesture.current = {
startX: coordX(e),
startY: coordY(e),
Expand Down Expand Up @@ -118,7 +122,14 @@ export const Touch: React.FC<TouchProps> = ({
startHandler.add(el);
}, [Component]);

useUnmountCleanup(() => {
if (gesture.current.isPressed) {
onEnd(lastEvent.current);
}
});

function onMove(e: VKUITouchEvent) {
lastEvent.current = e;
const { isPressed, isX, isY, startX, startY } = gesture.current;

if (isPressed) {
Expand Down Expand Up @@ -173,6 +184,7 @@ export const Touch: React.FC<TouchProps> = ({

didSlide.current = isSlide;
gesture.current = {};
lastEvent.current = null;

// Если это был тач-евент, симулируем отмену hover
if (touchEnabled()) {
Expand Down
9 changes: 9 additions & 0 deletions src/hooks/useUnmountCleanup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react';

export function useUnmountCleanup(cleanup: VoidFunction) {
const cleanupRef = React.useRef(cleanup);
React.useEffect(() => {
cleanupRef.current = cleanup;
});
React.useEffect(() => () => cleanupRef.current(), []);
}

0 comments on commit 273a35d

Please sign in to comment.