From ad7621c1475e0eba0bd47eaca9d6ab151897dab0 Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Mon, 7 Dec 2020 22:57:09 +0100 Subject: [PATCH 01/13] fix: improve rubber band effect when out of bounds --- src/BottomSheet.tsx | 118 +++++++++++++++----------------------------- 1 file changed, 41 insertions(+), 77 deletions(-) diff --git a/src/BottomSheet.tsx b/src/BottomSheet.tsx index b2cb613b..8c28a44f 100644 --- a/src/BottomSheet.tsx +++ b/src/BottomSheet.tsx @@ -7,7 +7,8 @@ import React, { useEffect, useImperativeHandle, useRef } from 'react' import { animated } from 'react-spring' -import { useDrag } from 'react-use-gesture' +import { useDrag, rubberbandIfOutOfBounds } from 'react-use-gesture' +import { clamp } from './utils' import { useAriaHider, useFocusTrap, @@ -390,96 +391,64 @@ export const BottomSheet = React.forwardRef< } }, [on, prefersReducedMotion, ready, set]) - const getY = ({ + const handleDrag = ({ down, - temp, - movement, velocity, - }: { - down: boolean - temp: number - movement: number - velocity: number - }): number => { - const rawY = temp - movement - const predictedDistance = movement * velocity + direction: [, direction], + memo = spring.y.getValue(), + first, + last, + movement: [, _my], + cancel, + }) => { + const my = _my * -1 + + // Cancel the drag operation if the canDrag state changed + if (!canDragRef.current) { + console.log('handleDrag cancelled dragging because canDragRef is false') + springOnResize.current = true + cancel() + return memo + } + + const rawY = memo + my + const predictedDistance = my * velocity const predictedY = Math.max( minSnapRef.current, - Math.min(maxSnapRef.current, rawY - predictedDistance * 2) + Math.min(maxSnapRef.current, rawY + predictedDistance * 2) ) if ( !down && onDismiss && - rawY - predictedDistance < minSnapRef.current / 2 + direction > 0 && + rawY + predictedDistance < minSnapRef.current / 2 ) { + cancel() onDismiss() - return rawY + return memo } - if (down) { - const scale = maxHeight * 0.38196601124999996 - - // If dragging beyond maxSnap it should decay so the user can feel its out of bounds - if (rawY > maxSnapRef.current) { - const overflow = - Math.min(rawY, maxSnapRef.current + scale / 2) - maxSnapRef.current - const resistance = Math.min(0.5, overflow / scale) * overflow + let newY = down + ? clamp( + rubberbandIfOutOfBounds( + rawY, + onDismiss ? 0 : minSnapRef.current, + maxSnapRef.current, + 0.55 + ), + 0, + maxHeightRef.current + ) + : predictedY - return maxSnapRef.current + overflow - resistance - } - - // If onDismiss isn't defined, the user can't flick it out of view and the dragging should decay/slow down - if (!onDismiss && rawY < minSnapRef.current) { - const overflow = - minSnapRef.current - Math.max(rawY, minSnapRef.current - scale / 2) - const resistance = Math.min(0.5, overflow / scale) * overflow - - return minSnapRef.current - overflow + resistance - } - - // apply coordinates as it's being dragged, unless it is out of bounds (in which case a decay should be applied) - return rawY - } - - return predictedY - } - - const handleDrag = ({ - down, - velocity, - direction, - memo = spring.y.getValue(), - first, - last, - movement: [, my], - cancel, - }) => { - let newY = getY({ - down: !!down, - movement: isNaN(my) ? 0 : my, - velocity, - temp: memo as number, - }) - - const relativeVelocity = Math.max(1, velocity) - console.log({ first, memo }) if (first) { - console.log('first ', { memo }) springOnResize.current = false } - // Cancel the drag operation if the canDrag state changed - if (!canDragRef.current) { - console.log('handleDrag cancelled dragging because canDragRef is false') - springOnResize.current = true - cancel() - return - } - if (last) { // Restrict y to a valid snap point - newY = findSnap(newY) + newY = findSnapRef.current(newY) heightRef.current = newY lastSnapRef.current = newY springOnResize.current = true @@ -492,12 +461,7 @@ export const BottomSheet = React.forwardRef< maxSnap: maxSnapRef.current, minSnap: minSnapRef.current, immediate: prefersReducedMotion.current || down, - config: { - mass: relativeVelocity, - tension: 300 * relativeVelocity, - friction: 35 * relativeVelocity, - velocity: direction[1] * velocity, - }, + config: { velocity }, }) return memo From f985fce44ac28d5ede342fc9e624781b97753fbf Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Mon, 7 Dec 2020 23:01:07 +0100 Subject: [PATCH 02/13] Update BottomSheet.tsx --- src/BottomSheet.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BottomSheet.tsx b/src/BottomSheet.tsx index 8c28a44f..1304e1ae 100644 --- a/src/BottomSheet.tsx +++ b/src/BottomSheet.tsx @@ -395,7 +395,7 @@ export const BottomSheet = React.forwardRef< down, velocity, direction: [, direction], - memo = spring.y.getValue(), + memo = spring.y.getValue() as number, first, last, movement: [, _my], From 598cf5dd4ffe2baff3c87cc00cd5a6e15b805796 Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Mon, 7 Dec 2020 23:31:30 +0100 Subject: [PATCH 03/13] fix: improve useDrag logic --- src/BottomSheet.tsx | 65 ++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/src/BottomSheet.tsx b/src/BottomSheet.tsx index 1304e1ae..a908b067 100644 --- a/src/BottomSheet.tsx +++ b/src/BottomSheet.tsx @@ -7,8 +7,7 @@ import React, { useEffect, useImperativeHandle, useRef } from 'react' import { animated } from 'react-spring' -import { useDrag, rubberbandIfOutOfBounds } from 'react-use-gesture' -import { clamp } from './utils' +import { rubberbandIfOutOfBounds, useDrag } from 'react-use-gesture' import { useAriaHider, useFocusTrap, @@ -16,8 +15,8 @@ import { useReducedMotion, useScrollLock, useSnapPoints, - useSpringInterpolations, useSpring, + useSpringInterpolations, } from './hooks' import type { defaultSnapProps, @@ -25,6 +24,7 @@ import type { RefHandles, SnapPointProps, } from './types' +import { clamp } from './utils' export const BottomSheet = React.forwardRef< RefHandles, @@ -392,14 +392,16 @@ export const BottomSheet = React.forwardRef< }, [on, prefersReducedMotion, ready, set]) const handleDrag = ({ - down, - velocity, + args: [{ closeOnTap = false } = {}] = [], + cancel, direction: [, direction], - memo = spring.y.getValue() as number, + down, first, last, + memo = spring.y.getValue() as number, movement: [, _my], - cancel, + tap, + velocity, }) => { const my = _my * -1 @@ -411,6 +413,12 @@ export const BottomSheet = React.forwardRef< return memo } + if (onDismiss && closeOnTap && tap) { + cancel() + onDismiss() + return memo + } + const rawY = memo + my const predictedDistance = my * velocity const predictedY = Math.max( @@ -467,20 +475,8 @@ export const BottomSheet = React.forwardRef< return memo } - useDrag(handleDrag, { - domTarget: backdropRef, - eventOptions: { capture: true }, - axis: 'y', - }) - useDrag(handleDrag, { - domTarget: headerRef, - eventOptions: { capture: true }, - axis: 'y', - }) - useDrag(handleDrag, { - domTarget: footerRef, - eventOptions: { capture: true }, - axis: 'y', + const bind = useDrag(handleDrag, { + filterTaps: true, }) if (Number.isNaN(maxSnapRef.current)) { @@ -516,23 +512,14 @@ export const BottomSheet = React.forwardRef< }} > {sibling} - {blocking ? ( + {blocking && (
{ - if (onDismiss) { - event.preventDefault() - onDismiss() - } - }} + {...bind({ closeOnTap: true })} /> - ) : ( - // backdropRef always needs to be set because of useDrag -
)}
- {header !== false ? ( -
+ {header !== false && ( +
{header}
- ) : ( - // headerRef always needs to be set because of useDrag -
)}
{children}
- {footer ? ( -
+ {footer && ( +
{footer}
- ) : ( - // footerRef always needs to be set because of useDrag -
)}
From 66fbb5a017b85f414a2530835ee60d06b5fc5932 Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Mon, 7 Dec 2020 23:33:52 +0100 Subject: [PATCH 04/13] docs: improve links and sticky example --- README.md | 8 ++++---- pages/fixtures/sticky.tsx | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fc809bb3..cec1f1c6 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A more elaborate example that showcases how snap points work. It also shows how ## [Sticky header & footer](https://react-spring-bottom-sheet.cocody.dev/fixtures/sticky) -> [View demo code](/pages/fixtures/sticky.tsx#L40-L60) +> [View demo code](/pages/fixtures/sticky.tsx#L40-L61) If you provide either a `header` or `footer` prop you'll enable the special behavior seen in this example. And they're not just sticky positioned, both areas support touch gestures. @@ -226,9 +226,9 @@ ef.current.snapTo(({ // Showing all the available props # Credits -- Play icon used on frame overlays: https://fontawesome.com/icons/play-circle?style=regular -- Phone frame used in logo: https://www.figma.com/community/file/896042888090872154/Mono-Devices-1.0 -- iPhone frame used to wrap examples: https://www.figma.com/community/file/858143367356468985/(Variants)-iOS-%26-iPadOS-14-UI-Kit-for-Figma +- Play icon used on frame overlays: [font-awesome](https://fontawesome.com/icons/play-circle?style=regular) +- Phone frame used in logo: [Mono Devices 1.0](https://www.figma.com/community/file/896042888090872154/Mono-Devices-1.0) +- iPhone frame used to wrap examples: [iOS 14 UI Kit for Figma]() [gzip-badge]: http://img.badgesize.io/https://unpkg.com/react-spring-bottom-sheet/dist/index.es.js?compression=gzip&label=gzip%20size&style=flat-square [size-badge]: http://img.badgesize.io/https://unpkg.com/react-spring-bottom-sheet/dist/index.es.js?label=size&style=flat-square diff --git a/pages/fixtures/sticky.tsx b/pages/fixtures/sticky.tsx index 271f287f..492a7222 100644 --- a/pages/fixtures/sticky.tsx +++ b/pages/fixtures/sticky.tsx @@ -42,11 +42,12 @@ const StickyFixturePage: NextPage = ({ open={open} onDismiss={onDismiss} defaultSnap={({ snapPoints, lastSnap }) => - lastSnap ?? Math.max(...snapPoints) + lastSnap ?? Math.min(...snapPoints) } - snapPoints={({ maxHeight }) => [ + snapPoints={({ maxHeight, minHeight }) => [ maxHeight - maxHeight / 5, maxHeight * 0.6, + minHeight, ]} header={

From 91f8ca23091d18775af0aac617763072258f790b Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Mon, 7 Dec 2020 23:46:57 +0100 Subject: [PATCH 05/13] docs: improve example --- README.md | 2 +- pages/fixtures/sticky.tsx | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cec1f1c6..62950a1b 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ A more elaborate example that showcases how snap points work. It also shows how ## [Sticky header & footer](https://react-spring-bottom-sheet.cocody.dev/fixtures/sticky) -> [View demo code](/pages/fixtures/sticky.tsx#L40-L61) +> [View demo code](/pages/fixtures/sticky.tsx#L40-L60) If you provide either a `header` or `footer` prop you'll enable the special behavior seen in this example. And they're not just sticky positioned, both areas support touch gestures. diff --git a/pages/fixtures/sticky.tsx b/pages/fixtures/sticky.tsx index 492a7222..27a26b0e 100644 --- a/pages/fixtures/sticky.tsx +++ b/pages/fixtures/sticky.tsx @@ -44,10 +44,9 @@ const StickyFixturePage: NextPage = ({ defaultSnap={({ snapPoints, lastSnap }) => lastSnap ?? Math.min(...snapPoints) } - snapPoints={({ maxHeight, minHeight }) => [ + snapPoints={({ maxHeight }) => [ maxHeight - maxHeight / 5, maxHeight * 0.6, - minHeight, ]} header={

@@ -68,8 +67,10 @@ const StickyFixturePage: NextPage = ({

- Notice how much better the UX is on resize events when the - "Dismiss" button is sticky. + Putting the "Done" button in a sticky footer is a nice touch on + long bottom sheets with a lot of content. And on resize events + the sticky elements are always visible, unlike the "Dismiss" + button in the first example that needs to be animated first.

@@ -82,6 +83,17 @@ const StickyFixturePage: NextPage = ({ as well to optimize for large phones where the header might be difficult to reach with one hand.

+ +
+

+ Additionally this bottom sheet uses stable viewpoints that are + equivalent to vh CSS units. Predictable heights like this is + also handy if there's content loaded async, or you're + implementing a virtual list so the sheet can't rely on measuring + the height of its content. +

+
+ From 824b5c278c531fd0bdf2c54bbd9ff82ff22a1ec8 Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Mon, 7 Dec 2020 23:54:09 +0100 Subject: [PATCH 06/13] fix: avoid calling cancel in the same loop --- src/BottomSheet.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/BottomSheet.tsx b/src/BottomSheet.tsx index a908b067..46e66ccb 100644 --- a/src/BottomSheet.tsx +++ b/src/BottomSheet.tsx @@ -414,7 +414,6 @@ export const BottomSheet = React.forwardRef< } if (onDismiss && closeOnTap && tap) { - cancel() onDismiss() return memo } From 6be180fbfd81317339e24c80389663eef0f1202f Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Tue, 8 Dec 2020 00:12:33 +0100 Subject: [PATCH 07/13] fix: respond to header and footer prop changes --- src/BottomSheet.tsx | 11 ++++---- src/hooks/useSnapPoints.tsx | 53 ++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/src/BottomSheet.tsx b/src/BottomSheet.tsx index 46e66ccb..dbc989aa 100644 --- a/src/BottomSheet.tsx +++ b/src/BottomSheet.tsx @@ -85,7 +85,6 @@ export const BottomSheet = React.forwardRef< const [spring, set] = useSpring() const containerRef = useRef(null) - const backdropRef = useRef(null) const contentRef = useRef(null) const contentContainerRef = useRef(null) const headerRef = useRef(null) @@ -115,15 +114,17 @@ export const BottomSheet = React.forwardRef< }) const { minSnap, maxSnap, maxHeight, findSnap } = useSnapPoints({ + contentContainerRef, + controlledMaxHeight, + footerEnabled: !!footer, + footerRef, getSnapPoints, + headerEnabled: !!header, + headerRef, heightRef, lastSnapRef, ready, - contentContainerRef, - controlledMaxHeight, registerReady, - footerRef, - headerRef, }) // Setup refs that are used in cases where full control is needed over when a side effect is executed diff --git a/src/hooks/useSnapPoints.tsx b/src/hooks/useSnapPoints.tsx index 70d36ca6..695edcfa 100644 --- a/src/hooks/useSnapPoints.tsx +++ b/src/hooks/useSnapPoints.tsx @@ -14,8 +14,10 @@ import { useReady } from './useReady' export function useSnapPoints({ contentContainerRef, controlledMaxHeight, + footerEnabled, footerRef, getSnapPoints, + headerEnabled, headerRef, heightRef, lastSnapRef, @@ -24,8 +26,10 @@ export function useSnapPoints({ }: { contentContainerRef: React.RefObject controlledMaxHeight?: number + footerEnabled: boolean footerRef: React.RefObject getSnapPoints: snapPoints + headerEnabled: boolean headerRef: React.RefObject heightRef: React.RefObject lastSnapRef: React.RefObject @@ -33,10 +37,12 @@ export function useSnapPoints({ registerReady: ReturnType['registerReady'] }) { const { maxHeight, minHeight, headerHeight, footerHeight } = useDimensions({ - controlledMaxHeight, - headerRef, contentContainerRef, + controlledMaxHeight, + footerEnabled, footerRef, + headerEnabled, + headerRef, registerReady, }) @@ -86,16 +92,20 @@ export function useSnapPoints({ } function useDimensions({ - headerRef, contentContainerRef, - footerRef, controlledMaxHeight, + footerEnabled, + footerRef, + headerEnabled, + headerRef, registerReady, }: { - controlledMaxHeight?: number - headerRef: React.RefObject contentContainerRef: React.RefObject + controlledMaxHeight?: number + footerEnabled: boolean footerRef: React.RefObject + headerEnabled: boolean + headerRef: React.RefObject registerReady: ReturnType['registerReady'] }) { const setReady = useMemo(() => registerReady('contentHeight'), [ @@ -103,19 +113,19 @@ function useDimensions({ ]) const maxHeight = useMaxHeight(controlledMaxHeight, registerReady) - // Rewrite these to set refs and use nextTick - const { height: headerHeight } = useElementSizeObserver( - headerRef, - 'headerHeight' - ) + // @TODO probably better to forward props instead of checking refs to decide if it's enabled + const { height: headerHeight } = useElementSizeObserver(headerRef, { + label: 'headerHeight', + enabled: headerEnabled, + }) const { height: contentHeight } = useElementSizeObserver( contentContainerRef, - 'contentHeight' - ) - const { height: footerHeight } = useElementSizeObserver( - footerRef, - 'footerHeight' + { label: 'contentHeight', enabled: true } ) + const { height: footerHeight } = useElementSizeObserver(footerRef, { + label: 'footerHeight', + enabled: footerEnabled, + }) const minHeight = Math.min(maxHeight - headerHeight - footerHeight, contentHeight) + headerHeight + @@ -143,11 +153,12 @@ function useDimensions({ * * @param ref - A React ref to an element */ +const defaultSize = { width: 0, height: 0 } function useElementSizeObserver( ref: React.RefObject, - label: string + { label, enabled }: { label: string; enabled: boolean } ): { width: number; height: number } { - let [size, setSize] = useState(() => ({ width: 0, height: 0 })) + let [size, setSize] = useState(defaultSize) useDebugValue(`${label}: ${size.height}`) @@ -160,7 +171,7 @@ function useElementSizeObserver( }, []) useEffect(() => { - if (!ref.current) { + if (!ref.current || !enabled) { return } @@ -174,9 +185,9 @@ function useElementSizeObserver( return () => { resizeObserver.disconnect() } - }, [ref, handleResize]) + }, [ref, handleResize, enabled]) - return size + return enabled ? size : defaultSize } // Blazingly keep track of the current viewport height without blocking the thread, keeping that sweet 60fps on smartphones From 15270051c3d40e1ecdd42846041b831e46a073ee Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Tue, 8 Dec 2020 00:16:23 +0100 Subject: [PATCH 08/13] fix: should cancel, figure out another safari fix --- src/BottomSheet.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BottomSheet.tsx b/src/BottomSheet.tsx index dbc989aa..db222eef 100644 --- a/src/BottomSheet.tsx +++ b/src/BottomSheet.tsx @@ -415,6 +415,7 @@ export const BottomSheet = React.forwardRef< } if (onDismiss && closeOnTap && tap) { + cancel() onDismiss() return memo } From 6cb63bfa57a307b3abca2684c7f296d239c1f406 Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Tue, 8 Dec 2020 00:18:53 +0100 Subject: [PATCH 09/13] fix: remove extra clamp --- src/BottomSheet.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/BottomSheet.tsx b/src/BottomSheet.tsx index db222eef..a7fc2182 100644 --- a/src/BottomSheet.tsx +++ b/src/BottomSheet.tsx @@ -24,7 +24,6 @@ import type { RefHandles, SnapPointProps, } from './types' -import { clamp } from './utils' export const BottomSheet = React.forwardRef< RefHandles, @@ -439,15 +438,11 @@ export const BottomSheet = React.forwardRef< } let newY = down - ? clamp( - rubberbandIfOutOfBounds( - rawY, - onDismiss ? 0 : minSnapRef.current, - maxSnapRef.current, - 0.55 - ), - 0, - maxHeightRef.current + ? rubberbandIfOutOfBounds( + rawY, + onDismiss ? 0 : minSnapRef.current, + maxSnapRef.current, + 0.55 ) : predictedY From 68f111b77b7cb589d1f552c3bbf2b9554b8cca25 Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Tue, 8 Dec 2020 00:28:51 +0100 Subject: [PATCH 10/13] docs: improve aside example --- README.md | 2 +- package-lock.json | 45 +++++++++++++++++++++++++++++++++++++++- package.json | 1 + pages/fixtures/aside.tsx | 7 +++++++ tailwind.config.js | 1 + 5 files changed, 54 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 62950a1b..d4b0f1fa 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ If you provide either a `header` or `footer` prop you'll enable the special beha ## [Non-blocking overlay mode](https://react-spring-bottom-sheet.cocody.dev/fixtures/aside) -> [View demo code](/pages/fixtures/aside.tsx#L41-L46) +> [View demo code](/pages/fixtures/aside.tsx#L41-L53) In most cases you use a bottom sheet the same way you do with a dialog: you want it to overlay the page and block out distractions. But there are times when you want a bottom sheet but without it taking all the attention and overlaying the entire page. Providing `blocking={false}` helps this use case. By doing so you disable a couple of behaviors that are there for accessibility (focus-locking and more) that prevents a screen reader or a keyboard user from accidentally leaving the bottom sheet. diff --git a/package-lock.json b/package-lock.json index f03b945a..cf3e1b2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2102,6 +2102,15 @@ } } }, + "@tailwindcss/forms": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.2.1.tgz", + "integrity": "sha512-czfvEdY+J2Ogfd6RUSr/ZSUmDxTujr34M++YLnp2cCPC3oJ4kFvFMaRXA6cEXKw7F1hJuapdjXRjsXIEXGgORg==", + "dev": true, + "requires": { + "mini-svg-data-uri": "^1.2.3" + } + }, "@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -3158,6 +3167,16 @@ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", "dev": true }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, "bl": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", @@ -6449,6 +6468,13 @@ "flat-cache": "^3.0.4" } }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + }, "filesize": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz", @@ -9197,6 +9223,12 @@ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true }, + "mini-svg-data-uri": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.2.3.tgz", + "integrity": "sha512-zd6KCAyXgmq6FV1mR10oKXYtvmA9vRoB6xPSTUJTbFApCtkefDnYueVR1gkof3KcdLZo1Y8mjF2DFmQMIxsHNQ==", + "dev": true + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -9345,6 +9377,13 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, + "nan": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", + "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "dev": true, + "optional": true + }, "nanoid": { "version": "3.1.20", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", @@ -20515,7 +20554,11 @@ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "dev": true, - "optional": true + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } }, "glob-parent": { "version": "3.1.0", diff --git a/package.json b/package.json index 646eeff7..28911b52 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "devDependencies": { "@semantic-release/changelog": "^5.0.1", "@semantic-release/git": "^9.0.0", + "@tailwindcss/forms": "^0.2.1", "@types/classnames": "^2.2.11", "@types/node": "^14.14.10", "@types/react": "^17.0.0", diff --git a/pages/fixtures/aside.tsx b/pages/fixtures/aside.tsx index be5a150a..a001a581 100644 --- a/pages/fixtures/aside.tsx +++ b/pages/fixtures/aside.tsx @@ -42,6 +42,13 @@ const AsideFixturePage: NextPage = ({ open={open} onDismiss={() => setOpen(false)} blocking={false} + header={ + + } snapPoints={({ maxHeight }) => [maxHeight / 4, maxHeight * 0.6]} > diff --git a/tailwind.config.js b/tailwind.config.js index 8d80f8e6..d697486d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -37,4 +37,5 @@ module.exports = { ringWidth: ['group-focus', 'focus-visible'], }, }, + plugins: [require('@tailwindcss/forms')], } From 42c7efa2f6b04ea4474a2e697e8c2df63132773b Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Tue, 8 Dec 2020 00:30:40 +0100 Subject: [PATCH 11/13] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d4b0f1fa..8aaa44ab 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ npm i react-spring-bottom-sheet ## [Basic](https://react-spring-bottom-sheet.cocody.dev/fixtures/simple) -> [View demo code](/pages/fixtures/simple.tsx#L43-L47) +> [View demo code](/pages/fixtures/simple.tsx#L44-L48) MVP example, showing what you get by implementing `open`, `onDismiss` and a single **snap point** always set to `minHeight`. @@ -30,7 +30,7 @@ A more elaborate example that showcases how snap points work. It also shows how ## [Sticky header & footer](https://react-spring-bottom-sheet.cocody.dev/fixtures/sticky) -> [View demo code](/pages/fixtures/sticky.tsx#L40-L60) +> [View demo code](/pages/fixtures/sticky.tsx#L41-L61) If you provide either a `header` or `footer` prop you'll enable the special behavior seen in this example. And they're not just sticky positioned, both areas support touch gestures. From 99f1b2015c6815ea448d0c3dcaeaa4dd74de3e56 Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Tue, 8 Dec 2020 00:35:09 +0100 Subject: [PATCH 12/13] fix: headerEnabled test --- src/BottomSheet.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BottomSheet.tsx b/src/BottomSheet.tsx index a7fc2182..b26789f8 100644 --- a/src/BottomSheet.tsx +++ b/src/BottomSheet.tsx @@ -118,7 +118,7 @@ export const BottomSheet = React.forwardRef< footerEnabled: !!footer, footerRef, getSnapPoints, - headerEnabled: !!header, + headerEnabled: header !== false, headerRef, heightRef, lastSnapRef, From cc114f0653362d6fe85af652c9ae5ec5383e5fd2 Mon Sep 17 00:00:00 2001 From: Cody Olsen Date: Tue, 8 Dec 2020 00:44:02 +0100 Subject: [PATCH 13/13] chore: adding focus test --- pages/fixtures/experiments.tsx | 37 +++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/pages/fixtures/experiments.tsx b/pages/fixtures/experiments.tsx index 7ddec1a7..84d283d2 100644 --- a/pages/fixtures/experiments.tsx +++ b/pages/fixtures/experiments.tsx @@ -3,6 +3,7 @@ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' import Button from '../../docs/fixtures/Button' import Code from '../../docs/fixtures/Code' import Container from '../../docs/fixtures/Container' +import Expandable from '../../docs/fixtures/Expandable' import Kbd from '../../docs/fixtures/Kbd' import SheetContent from '../../docs/fixtures/SheetContent' import { BottomSheet } from '../../src' @@ -155,17 +156,39 @@ function Four() { <> [0, minHeight]} + header={ + + } + footer={ + + } > -

- Using onDismiss lets users close the sheet by swiping - it down, tapping on the backdrop or by hitting esc on - their keyboard. -

+ + +
+

Testing focus management and keyboard behavior on open.

+
+ +