react-modal-sheet
Version:
Flexible bottom sheet component for your React apps
193 lines (187 loc) • 6.58 kB
TypeScript
import { EasingDefinition, motion, MotionProps, MotionValue } from 'motion/react';
import { ForwardRefExoticComponent, ReactNode, ComponentPropsWithoutRef, RefAttributes, FunctionComponent, HTMLAttributes, RefObject } from 'react';
type SheetDetent = 'default' | 'full' | 'content';
type CommonProps = {
className?: string;
unstyled?: boolean;
};
type MotionCommonProps = Omit<ComponentPropsWithoutRef<typeof motion.div>, 'initial' | 'animate' | 'exit'>;
interface SheetTweenConfig {
ease: EasingDefinition;
duration: number;
}
type SheetProps = {
unstyled?: boolean;
avoidKeyboard?: boolean;
children: ReactNode;
detent?: SheetDetent;
disableDismiss?: boolean;
disableDrag?: boolean;
disableScrollLocking?: boolean;
dragCloseThreshold?: number;
dragVelocityThreshold?: number;
initialSnap?: number;
isOpen: boolean;
modalEffectRootId?: string;
modalEffectThreshold?: number;
mountPoint?: Element;
prefersReducedMotion?: boolean;
snapPoints?: number[];
tweenConfig?: SheetTweenConfig;
onClose: () => void;
onCloseEnd?: () => void;
onCloseStart?: () => void;
onOpenEnd?: () => void;
onOpenStart?: () => void;
onSnap?: (index: number) => void;
} & MotionCommonProps;
type SheetContainerProps = MotionCommonProps & CommonProps & {
children: ReactNode;
};
type SheetHeaderProps = MotionCommonProps & CommonProps & {
children?: ReactNode;
disableDrag?: boolean;
};
type SheetContentProps = MotionCommonProps & CommonProps & {
disableDrag?: boolean | ((args: SheetStateInfo) => boolean);
disableScroll?: boolean | ((args: SheetStateInfo) => boolean);
scrollRef?: RefObject<HTMLDivElement | null>;
scrollClassName?: string;
scrollStyle?: MotionProps['style'];
};
type SheetBackdropProps = MotionCommonProps & CommonProps;
type SheetDragIndicatorProps = HTMLAttributes<HTMLDivElement> & CommonProps;
type SheetStateInfo = {
scrollPosition?: 'top' | 'bottom' | 'middle';
currentSnap?: number;
};
type SheetSnapPoint = {
snapIndex: number;
snapValue: number;
snapValueY: number;
};
type SheetComponent = ForwardRefExoticComponent<SheetProps & RefAttributes<any>>;
type ContainerComponent = ForwardRefExoticComponent<SheetContainerProps & RefAttributes<any>>;
type HeaderComponent = ForwardRefExoticComponent<SheetHeaderProps & RefAttributes<any>>;
type BackdropComponent = ForwardRefExoticComponent<SheetBackdropProps & RefAttributes<any>>;
type ContentComponent = ForwardRefExoticComponent<SheetContentProps & RefAttributes<any>>;
interface SheetCompoundComponent {
Container: ContainerComponent;
Header: HeaderComponent;
DragIndicator: FunctionComponent<SheetDragIndicatorProps>;
Content: ContentComponent;
Backdrop: BackdropComponent;
}
type SheetCompound = SheetComponent & SheetCompoundComponent;
type UseScrollPositionOptions = {
/**
* Debounce delay in ms for scroll event handling.
* @default 32
*/
debounceDelay?: number;
/**
* Enable or disable the hook entirely.
* @default true
*/
isEnabled?: boolean;
};
/**
* Hook to track the scroll position of an element.
*
* The scroll position can be 'top', 'bottom', 'middle', or undefined if the content is not scrollable.
* The hook provides a `scrollRef` callback to assign to the scrollable element.
*
* Note that the scroll position is only updated when the user stops scrolling
* for a short moment (debounced). You can set `debounceDelay` to `0` to disable debouncing entirely.
*
* @param options Configuration options for the hook.
* @returns An object containing the `scrollRef` callback and the current `scrollPosition`.
*
* @example
* ```tsx
* import { useScrollPosition } from 'react-modal-sheet';
*
* function MyComponent() {
* const { scrollRef, scrollPosition } = useScrollPosition();
*
* return (
* <div>
* <p>Scroll position: {scrollPosition}</p>
* <div ref={scrollRef} style={{ overflowY: 'auto', maxHeight: '300px' }}>
* ...long content...
* </div>
* </div>
* );
* }
* ```
*/
declare function useScrollPosition(options?: UseScrollPositionOptions): {
scrollRef: (element: HTMLElement | null) => void;
scrollPosition: "top" | "bottom" | "middle" | undefined;
};
type UseVirtualKeyboardOptions = {
/**
* Ref to the container element to apply `keyboard-inset-height` CSS variable updates.
* @default document.documentElement
*/
containerRef?: RefObject<HTMLElement | null>;
/**
* Enable or disable the hook entirely.
* @default true
*/
isEnabled?: boolean;
/**
* Minimum pixel height difference to consider the keyboard visible.
* @default 100
*/
visualViewportThreshold?: number;
/**
* Whether to treat contenteditable elements as text inputs.
* @default true
*/
includeContentEditable?: boolean;
/**
* Delay in ms for debouncing viewport changes.
* @default 100
*/
debounceDelay?: number;
};
/**
* A hook that detects virtual keyboard visibility and height.
* It listens to focus events and visual viewport changes to determine
* if a text input is focused and the keyboard is likely visible.
*
* It also sets the `--keyboard-inset-height` CSS variable on the specified container
* (or `:root` by default) to allow for easy styling adjustments when the keyboard is open.
*
* @param options Configuration options for the hook.
* @returns An object containing `isKeyboardOpen` and `keyboardHeight`.
*
* @example
* ```tsx
* import { useVirtualKeyboard } from 'react-modal-sheet';
*
* function MyComponent() {
* const { isKeyboardOpen, keyboardHeight } = useVirtualKeyboard();
*
* return (
* <div>
* <p>Keyboard is {isKeyboardOpen ? 'open' : 'closed'}</p>
* <p>Keyboard height: {keyboardHeight}px</p>
* </div>
* );
* }
* ```
*/
declare function useVirtualKeyboard(options?: UseVirtualKeyboardOptions): {
keyboardHeight: number;
isKeyboardOpen: boolean;
};
interface SheetRef {
y: MotionValue<number>;
yInverted: MotionValue<number>;
height: number;
snapTo: (index: number) => Promise<void>;
}
declare const Sheet: SheetCompound;
export { Sheet, type SheetBackdropProps, type SheetContainerProps, type SheetContentProps, type SheetDetent, type SheetDragIndicatorProps, type SheetHeaderProps, type SheetProps, type SheetRef, type SheetSnapPoint, type SheetStateInfo, type SheetTweenConfig, useScrollPosition, useVirtualKeyboard };