Skip to content

Updating Simple Pattern Documentation #354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/examples/simple-carousel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ function reducer(state: CarouselState, action: CarouselAction): CarouselState {

Then, building upon the reducer logic, the `<Carousel>` is constructed. We hold the number of items within a count of `numItems`. We utilize the reducer within the `React.useReducer` hook.

By creating `slide`, as a `const`, we can utilize that later in the component, calling it within `useSwipeable`: called upon `slide(NEXT)` and `slide(PREV)`, invoking the `dispatch` and the `timeout` we built within `slide`. Within the use of `useSwippeable`, we set `swipeDuration` to `500ms`. We set `preventScrollOnSwipe` to `true`, and `trackMouse` to `true`.
By creating `slide`, as a `const`, we can utilize that later in the component, calling it within `useSwipeable`: called upon `slide(NEXT)` and `slide(PREV)`, invoking the `dispatch` and the `timeout` we built within `slide`. Within the use of `useSwipeable`, we set `swipeDuration` to `500ms`. We set `preventScrollOnSwipe` to `true`, and `trackMouse` to `true`.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realized I had a typo in what was merged previously, fixing it here.


At the end, we return the component itself, built with the components we've created, with `handlers` passed into the wrapping `<div>` around the surrounding container. The `<CarouselContainer>` holds the directional and sliding state, and within that container the items we want to display are mapped as `React.Children`, utilizing `getOrder`.

Expand Down
189 changes: 189 additions & 0 deletions docs/docs/examples/simple-pattern.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,194 @@ import SimplePattern from '@site/src/components/examples/SimplePattern'

# Simple Pattern

Below is an example implementation of a simple pattern which utilizes the hooks provided by `react-swipeable` within a TypeScript context.

## Simple Pattern Code Source

You can see this full example as pure code within the [Pattern.tsx](https://github.com/FormidableLabs/react-swipeable/blob/main/examples/app/SimplePattern/pattern.tsx) file within the React-Swipeable repo directly.

## Simple Pattern Live Preview

<SimplePattern />

## Simple Pattern Code Explained

Import the hook directly from the `react-swipeable` library, along with the directions from the library, and the `SwipeEventData`. In our example, we built and imported a local set of UI components: you can utilize your own UI and styling, or use your favorite UI component library of choice.

```typescript
import React, { FunctionComponent, ReactNode } from 'react';
import { useSwipeable, UP, DOWN, SwipeEventData } from 'react-swipeable';
import {
Wrapper,
CarouselContainer,
CarouselSlot,
PatternBox,
PREV,
NEXT,
D
} from '../components';
```

In our example, we utilize SVGs for our `UpArrow` and `DownArrow` to give indications of when someone is successfully activating the pattern for user feedback, but know you can use whatever UI library of your choice, or stylize your own!

```typescript
const UpArrow = ({active}: {active: boolean}) => (
<svg viewBox="0 0 16 16" version="1.1" style={{width: '15px'}}>
<g transform="translate(-35.399 -582.91)">
<path style={{fill: active ? '#EEEE00' : '#000000'}} d="m40.836 598.91v-6.75h-5.4375l4-4.625 4-4.625 4 4.625 4 4.625h-5.0938v6.75h-5.4688z" />
</g>
</svg>
);

const DownArrow = ({active}: {active: boolean}) => (
<svg viewBox="0 0 16 16" version="1.1" style={{width: '15px'}}>
<g transform="translate(-35.399 -598.91)">
<path style={{fill: active ? '#EEEE00' : '#000000'}} d="m40.836 598.91v6.75h-5.4375l4 4.625 4 4.625 4-4.625 4-4.625h-5.0938v-6.75h-5.4688z" />
</g>
</svg>
);
```

Next, we set up our types for the `Directions`, `CarouselState`, and `CarouselAction`.

```typescript
type Direction = typeof PREV | typeof NEXT;

interface CarouselState {
pos: number;
sliding: boolean;
dir: Direction;
}

type CarouselAction =
| { type: Direction, numItems: number }
| { type: 'stopSliding' };
```

Below, we create a function called `getOrder`, which drives the position of each item in the carousel, and what order of position each will be displayed in the context of the carousel. Then, we set a `pattern` as an array of the pattern we want the user to follow to unlock the slide action. Finally here, we then set `getInitialState`, setting the position of the initial items, the `sliding`, as false, and the direction.

```typescript

const getOrder = (index: number, pos: number, numItems: number) => {
return index - pos < 0 ? numItems - Math.abs(index - pos) : index - pos;
};

const pattern = [UP, DOWN, UP, DOWN];

const getInitialState = (numItems: number): CarouselState => ({ pos: numItems - 1, sliding: false, dir: NEXT });

```

At the bottom of the file, we set up a reducer for controlling the action of the Carousel utilizing a switch to set the `CarouselState` logic.

```typescript
function reducer(state: CarouselState, action: CarouselAction): CarouselState {
switch (action.type) {
case PREV:
return {
...state,
dir: PREV,
sliding: true,
pos: state.pos === 0 ? action.numItems - 1 : state.pos - 1
};
case NEXT:
return {
...state,
dir: NEXT,
sliding: true,
pos: state.pos === action.numItems - 1 ? 0 : state.pos + 1
};
case 'stopSliding':
return { ...state, sliding: false };
default:
return state;
}
}
```

Then, building upon the reducer logic, the `<Carousel>` is constructed. We hold the number of items within `numItems`, and utilize the reducer within the `React.useReducer` hook.

By creating the `slide`, as a `const`, we can utilize that to call within `handleSwiped` as an action that is called upon the user successfully execution of the pattern.

It may help to briefly look at the `handlers` for a moment, and how we utilize `useSwipeable`. Within this, with each `onSwiped`, we call `handleSwiped`. So for each swipe the user takes within the text box above the carousel, we execute `handleSwiped` and pass along the `eventData`. If the `eventData.dir` matches the pattern for this indexed (`pIdx`) item, and the direction indicated, then we `setPIdx` to a greater number.

What does this do? Two things: it helps us know when the user successfully got to the end of the pattern, and activate the `slide` action, and it also controls the arrows activating the color within the `<PatternBox>` to give feedback to the user that they were successful in activating the steps of the pattern!

Two other important items to note: we utilized `onTouchStartOrOnMouseDown` to pass through `event.preventDefault()` as a callback, and used `touchEventOptions: {passive: false}` in case certain browsers ignored the `preventDefault()` callback bubbling up.

From there, the rest of the UI of the component is built. The `<PatternBox>` holds where the user swipes in order to interact with the Carousel itself, along with the arrows that give the feedback to the user that the pattern was successful. The `<CarouselContainer>` holds the Carousel display and items. Our Simple Pattern is complete!

```typescript
const Carousel: FunctionComponent<{children: ReactNode}> = (props) => {
const numItems = React.Children.count(props.children);
const [state, dispatch] = React.useReducer(reducer, getInitialState(numItems));

const slide = (dir: Direction) => {
dispatch({ type: dir, numItems });
setTimeout(() => {
dispatch({ type: 'stopSliding' });
}, 50);
};

const [pIdx, setPIdx] = React.useState(0);

const handleSwiped = (eventData: SwipeEventData) => {
if (eventData.dir === pattern[pIdx]) {
// user successfully got to the end of the pattern!
if (pIdx + 1 === pattern.length) {
setPIdx(pattern.length);
slide(NEXT);
setTimeout(() => {
setPIdx(0);
}, 50);
} else {
// user is cont. with the pattern
setPIdx((i) => (i += 1));
}
} else {
// user got the next pattern step wrong, reset pattern
setPIdx(0);
}
};

const handlers = useSwipeable({
onSwiped: handleSwiped,
onTouchStartOrOnMouseDown: (({ event }) => event.preventDefault()),
touchEventOptions: { passive: false },
preventScrollOnSwipe: true,
trackMouse: true
});

return (
<>
<PatternBox {...handlers}>
Swipe the pattern below, within this box, to make the carousel go to the next
slide
{`\n`}
<p style={{textAlign: 'center', paddingTop: '15px'}}>
Swipe:
<D><UpArrow active={pIdx > 0} /></D>
<D><DownArrow active={pIdx > 1} /></D>
<D><UpArrow active={pIdx > 2} /></D>
<D><DownArrow active={pIdx > 3} /></D>
</p>
</PatternBox>
<div style={{paddingBottom: '15px'}}>
<Wrapper>
<CarouselContainer dir={state.dir} sliding={state.sliding}>
{React.Children.map(props.children, (child, index) => (
<CarouselSlot
key={index}
order={getOrder(index, state.pos, numItems)}
>
{child}
</CarouselSlot>
))}
</CarouselContainer>
</Wrapper>
</div>
</>
);
};
```

7 changes: 0 additions & 7 deletions docs/src/components/examples/SimplePattern/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@ function SimplePattern() {
<Item src={Product4} />
<Item src={Product5} />
</Carousel>
<h6>
<a href="https://github.com/FormidableLabs/react-swipeable/blob/main/examples/app/SimplePattern/pattern.tsx">
See code
</a>{" "}
for example usage of <code>onTouchStartOrMouseDown</code> and{" "}
<code>touchEventOptions</code>
</h6>
</div>
);
}
Expand Down
4 changes: 2 additions & 2 deletions docs/src/components/examples/SimplePattern/pattern.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ const Carousel: FunctionComponent<{ children: ReactNode }> = (props) => {
return (
<>
<PatternBox {...handlers}>
Swipe the pattern below, within this box, to make the carousel go to the
next slide
Within this text area container, swipe the pattern seen below to make
the carousel navigate to the next slide.
{`\n`}
<p style={{ textAlign: "center", paddingTop: "15px" }}>
Swipe:
Expand Down
5 changes: 4 additions & 1 deletion docs/src/components/examples/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ export const SlideButton = styled.button<{ float: "left" | "right" }>`
`;

export const PatternBox = styled.div`
padding: 30px;
padding: 10px;
border: 1px solid black;
margin: 10px auto 20px auto;
text-align: center;
`;

export const D = styled.span`
Expand Down
5 changes: 1 addition & 4 deletions examples/app/SimplePattern/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Carousel from './pattern';
function SimplePattern() {
return (
<div>
<h5 style={{ marginBottom: '20px' }}>
<h5 style={{ marginBottom: '20px'}}>
<strong>👉 Swipe pattern</strong>
</h5>
<Carousel>
Expand All @@ -17,9 +17,6 @@ function SimplePattern() {
<Item img="https://unsplash.it/478/205" />
<Item img="https://unsplash.it/479/205" />
</Carousel>
<h6>
<a href="https://github.com/FormidableLabs/react-swipeable/blob/main/examples/app/SimplePattern/pattern.tsx">See code</a> for example usage of <code>onTouchStartOrMouseDown</code> and <code>touchEventOptions</code>
</h6>
</div>
);
}
Expand Down
4 changes: 2 additions & 2 deletions examples/app/SimplePattern/pattern.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ const Carousel: FunctionComponent<{children: ReactNode}> = (props) => {
return (
<>
<PatternBox {...handlers}>
Swipe the pattern below, within this box, to make the carousel go to the next
slide
Within this text area container, swipe the pattern seen below to make
the carousel navigate to the next slide.
{`\n`}
<p style={{textAlign: 'center', paddingTop: '15px'}}>
Swipe:
Expand Down
Loading