Skip to content

Commit

Permalink
✍️ Prep docs and storybook
Browse files Browse the repository at this point in the history
  • Loading branch information
Harley Alexander authored and Harley Alexander committed Apr 8, 2020
1 parent e9d0bdf commit 80087ca
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 15 deletions.
53 changes: 47 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,49 @@
# `bottom-sheet`
# `mui-bottom-sheet`

Features:
> 👆 A delightful bottom sheet component for react up to material design spec
- peek heights - make drawer stop at specific heights on the way up to full screen
- clamp to max height - if the contents are smaller than full screen, clamp it
- hide completely with hidden prop
- backdrop fades in and out as you swipe up
The bottom sheet in the Google Maps app for a location is _really_ nice. I set out to recreate that level of UX detail with a modern `BottomSheet` for React. [Try it in the storybook]().

![Bottom Sheet Demo](https://user-images.githubusercontent.com/43975092/78733881-0f751300-798a-11ea-8c9c-62cda96fc35b.gif)

Under the hood it uses `react-spring` for delightful animations and `react-use-gesture` to handle dragging. This is still a work in progress - let me know features you'd like and I'll add them in.

## Installation

```bash
yarn add mui-bottom-sheet
```

## Usage

```js
import React from 'react';
import { BottomSheet } from 'mui-bottom-sheet';

export const App = () => {
return <BottomSheet>Add your content here</BottomSheet>;
};
```

## Props (options)

| prop | type | description | default |
| --------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- |
| `backdrop` | `boolean?` | Whether to show the transparent backdrop that fades in as you pull up. | `true` |
| `background` | `React.ReactElement?` | Background element that slides up behind the main bottom sheet. | `null` |
| `defaultHeight` | `number?` | Default height when the sheet is closed. | `100` |
| `hidden` | `boolean?` | When true, the sheet will completely hide at the bottom of the screen. | `false` |
| `fullHeight` | `boolean?` | Whether to allow the sheet to go 100% of the screen height. If false, the highest it can go is the maximum of `peekHeights`. Otherwise it'll stick to `defaultHeight`. | `true` |
| `peekHeights` | `number[]?` | Progressive peek heights for the bottom sheet to stop at. Use this to reveal more detailed information as the sheet is pulled up. | `[]` |
| `styles` | `{ root: {}, backdrop: {}, background: {} }` | Pass additional styles to either the sheet, the backdrop or the background components | `{ root: {}, backdrop: {}, background: {} }` |
| `threshold` | `number?` | The threshold for over-dragging the sheet above its maximum height before it snaps to the highest position. | `70` |

## Upcoming

- [ ] Cypress tests - Jest doesn't play nicely with dragging and dropping. Help appreciated.
- [ ] Hooks API for binding custom components instead of a prescribed `animated.div`
- [ ] Programatically set stop height
- [ ] Access to current stop index
- [ ] `open()` and `close()` for programatic interaction
- [ ] passing custom `react-spring` config
- [ ] anything else?
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@
"trailingComma": "es5"
},
"name": "mui-bottom-sheet",
"keywords": [
"material design",
"mui",
"bottom sheet",
"action sheet",
"bottom drawer",
"sheet",
"react",
"react-spring",
"react-use-gesture",
"google maps"
],
"author": "Harley Alexander",
"module": "dist/mui-bottom-sheet.esm.js",
"devDependencies": {
Expand Down
9 changes: 7 additions & 2 deletions src/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,10 @@ export const BottomSheet: React.FC<BottomSheetProps> = props => {
.sort()
.reverse()
.find(n => n !== defaultPosition && n < defaultPosition);

const backdropStyle = {
/** backdrop should only begin to fade in after first stop */
opacity: y.to([backdropActiveAt as number, defaultPosition], [1, 0]),
opacity: y.to([(backdropActiveAt as number) || 0, defaultPosition], [1, 0]),
/** Set display none when backdrop isn't show so you can interact with the page */
display: y.to(py => (py < defaultPosition && backdrop ? 'block' : 'none')),
};
Expand Down Expand Up @@ -239,7 +240,11 @@ export const BottomSheet: React.FC<BottomSheetProps> = props => {
</a.div>
{background && (
<a.div
style={{ ...styles.background, ...backgroundStyle }}
style={{
...styles.background,
...userStyles.background,
...backgroundStyle,
}}
className="background"
>
{background}
Expand Down
114 changes: 107 additions & 7 deletions stories/BottomSheet.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,130 @@
import React, { PropsWithChildren } from 'react';
import { BottomSheet, BottomSheetProps } from '../src/BottomSheet';
import { boolean, number, object } from '@storybook/addon-knobs';
import backgroundImage from './static/buildings.jpg';

export default {
title: 'A BottomSheet',
component: BottomSheet,
decorators: [
StoryFn => (
<div style={{ fontFamily: 'sans-serif', padding: 24 }}>
<StoryFn />
</div>
),
],
};

// By passing optional props to this story, you can control the props of the component when
// you consume the story in a test.

export const Default = (
props?: Partial<PropsWithChildren<BottomSheetProps>>
) => (
<div style={{ fontFamily: 'sans-serif', padding: 24 }}>
Map will go here
<BottomSheet
children={<div style={{ height: 900, padding: 24 }}>Sheet content</div>}
{...props}
/>
);

export const AllPropsAsKnobs = () => (
<BottomSheet
backdrop={boolean('backdrop', true)}
defaultHeight={number('defaultHeight', 100)}
hidden={boolean('hidden', false)}
fullHeight={boolean('fullHeight', true)}
peekHeights={object('peekHeights', [250, 400])}
threshold={number('threshold', 70)}
>
<div style={{ height: 900, padding: 24 }}>Content</div>
</BottomSheet>
);
export const CustomStyles = () => (
<div style={{ background: '#1E2128', color: '#F3EFF5' }}>
Regular page content
<BottomSheet
peekHeights={[100, 300]}
fullHeight={false}
background={
<div style={{ padding: 24 }}>
Add images, carousels, or anything in here!
</div>
}
styles={object('styles', {
root: {
borderRadius: '10px 10px 0 0',
backgroundColor: '#313642',
color: '#F3EFF5',
},
backdrop: {
backgroundColor: 'rgba(255,255,255,0.8)',
},
background: {
backgroundImage: `url(${backgroundImage})`,
backgroundPosition: 'center center',
backgroundSize: 'cover',
paddingBottom: 24,
display: 'flex',
alignItems: 'flex-end',
color: '#fff',
fontSize: 24,
fontWeight: 'bold',
},
})}
>
<div style={{ height: 900, padding: 24 }}>
Pull me up for a pretty image.
</div>
</BottomSheet>
</div>
);

CustomStyles.story = {
decorators: [
StoryFn => (
<div
style={{
fontFamily: 'sans-serif',
padding: 24,
background: '#1E2128',
color: '#F3EFF5',
}}
>
<StoryFn />
</div>
),
],
};

export const WithLongContentAndBackground = () => (
<>
Your regular page here.
<BottomSheet
background={
<div style={{ backgroundColor: '#eee', height: '100%', padding: 24 }}>
Location image sliders can go here
Add images, carousels, or anything in here!
</div>
}
peekHeights={[250, 400]}
{...props}
styles={{
root: {
borderRadius: '10px 10px 0 0',
},
}}
>
<div style={{ height: 900, width: '100%', padding: 24 }}>
Waypoint information carousel here
<div style={{ height: 250, width: '100%', padding: 24 }}>
Main bottom sheet content can go here.
</div>
<div
style={{ height: 250, width: '100%', padding: 24, background: '#eee' }}
>
Peek heights reveal more information progressively.
</div>
<div
style={{ height: 250, width: '100%', padding: 24, background: '#ccc' }}
>
Only scroll when you're at full height.
</div>
</BottomSheet>
</div>
</>
);
Binary file added stories/static/buildings.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 80087ca

Please sign in to comment.