Skip to content

Commit

Permalink
Updating Simple Pattern Documentation (#354)
Browse files Browse the repository at this point in the history
  • Loading branch information
sblinde authored Oct 16, 2024
1 parent 5005742 commit 527a3be
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 17 deletions.
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`.

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

0 comments on commit 527a3be

Please sign in to comment.