react-slide-panel 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -126,7 +126,7 @@ function SlidePanel({
126
126
  const wasAlreadyLockedRef = (0, import_react.useRef)(false);
127
127
  (0, import_react.useEffect)(() => {
128
128
  const el = panelRef.current;
129
- const shouldLock = open && lockScroll && el;
129
+ const shouldLock = isMounted && lockScroll && el;
130
130
  if (shouldLock) {
131
131
  const htmlOverflow = document.documentElement.style.overflow;
132
132
  const bodyOverflow = document.body.style.overflow;
@@ -149,7 +149,7 @@ function SlidePanel({
149
149
  }
150
150
  }
151
151
  };
152
- }, [open, lockScroll, lockScrollHtml]);
152
+ }, [isMounted, lockScroll, lockScrollHtml]);
153
153
  const closePanel = (0, import_react.useCallback)(() => {
154
154
  onOpenChange(false);
155
155
  }, [onOpenChange]);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/SlidePanel.tsx","../src/SlidePanelCloseButton.tsx"],"sourcesContent":["export { SlidePanel } from './SlidePanel';\nexport type { SlidePanelProps, SlidePanelSide } from './SlidePanel';\nexport { SidePanelCloseButton } from './SlidePanelCloseButton';\nexport type { SidePanelCloseButtonProps } from './SlidePanelCloseButton';\n","import React, {\n useRef,\n useEffect,\n useState,\n useCallback,\n useLayoutEffect,\n useMemo,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';\nimport { SidePanelCloseButton } from './SlidePanelCloseButton';\nimport './styles.css';\n\nexport type SlidePanelSide = 'top' | 'right' | 'bottom' | 'left';\n\nexport interface SlidePanelProps {\n /** Whether the panel is open. */\n open: boolean;\n /** Callback when open state should change (e.g. user closes). Pass the new boolean to update controlled state. */\n onOpenChange: (open: boolean) => void;\n /** Called when the close transition finishes and the panel is hidden. */\n onClosed?: () => void;\n /** Called when the open transition finishes and the panel is visible. */\n onOpened?: () => void;\n /** ID of the portal container element. Default: `'rsp-container'`. */\n idName?: string;\n /** Hide the default close button. */\n hideCloseBtn?: boolean;\n /** Disable closing by clicking the overlay. */\n noClose?: boolean;\n /** Which side the panel slides from. Default: `'right'`. */\n side?: SlidePanelSide;\n /** When true, unmount panel when closed and remount when opened. Default: `true`. */\n rerender?: boolean;\n /** z-index of overlay and panel. `'auto'` uses a high default. */\n zIndex?: number | 'auto';\n /** Panel width (e.g. `'500px'`). Only for side `'left'` or `'right'`. */\n width?: string;\n /** Panel height (e.g. `'500px'`). Only for side `'top'` or `'bottom'`. */\n height?: string;\n /** Lock body scroll when the panel is open. */\n lockScroll?: boolean;\n /** When lockScroll is true, also set overflow hidden on html. Default: `true`. */\n lockScrollHtml?: boolean;\n /** Overlay background color (any valid CSS color). Default: `'black'`. */\n overlayColor?: string;\n /** Overlay opacity (0–1). Default: `0.5`. */\n overlayOpacity?: number;\n /** Overlay transition duration in ms. Default: `500`. */\n overlayDuration?: number;\n /** Panel background color. Default: `'white'`. */\n panelColor?: string;\n /** Panel transition duration in ms. Default: `300`. */\n panelDuration?: number;\n /** Animation class name: `slide-right` | `slide-left` | `slide-top` | `slide-bottom`, or custom. Default picks from `side`. */\n transitionName?: string;\n /** Class for the header container. */\n headerClass?: string;\n /** Class for the scrollable body container. */\n bodyClass?: string;\n /** Class for the footer container. */\n footerClass?: string;\n /** Optional fixed header content. */\n header?: React.ReactNode;\n /** Optional fixed footer content. */\n footer?: React.ReactNode;\n /** Panel body content. */\n children?: React.ReactNode;\n /** Additional class for the panel element. */\n className?: string;\n /** Inline styles for the panel element. */\n style?: React.CSSProperties;\n}\n\n/**\n * A modal slide panel that slides in from the chosen edge (top, right, bottom, left).\n * Renders via a portal and supports overlay, scroll lock, and custom header/footer.\n */\nexport function SlidePanel({\n open,\n onOpenChange,\n onClosed,\n onOpened,\n idName = 'rsp-container',\n hideCloseBtn = false,\n noClose = false,\n side = 'right',\n rerender = true,\n zIndex: zIndexProp = 'auto',\n width = 'auto',\n height = 'auto',\n lockScroll = false,\n lockScrollHtml = true,\n overlayColor = 'black',\n overlayOpacity = 0.5,\n overlayDuration = 500,\n panelColor = 'white',\n panelDuration = 300,\n transitionName,\n headerClass = '',\n bodyClass = '',\n footerClass = '',\n header,\n footer,\n children,\n className = '',\n style: styleProp,\n}: SlidePanelProps) {\n const [isMounted, setIsMounted] = useState(open);\n const [isExiting, setIsExiting] = useState(false);\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);\n\n const panelRef = useRef<HTMLDivElement | null>(null);\n const containerCreatedByUsRef = useRef(false);\n\n const transition = transitionName ?? `slide-${side}`;\n\n const callbacks = useRef({ onOpened, onClosed });\n useLayoutEffect(() => {\n callbacks.current = { onOpened, onClosed };\n });\n\n // 1. Manage Mount & Exit Animation States\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout>;\n\n if (open) {\n setIsMounted(true);\n setIsExiting(false);\n timeoutId = setTimeout(() => {\n callbacks.current.onOpened?.();\n }, Math.max(overlayDuration, panelDuration));\n } else if (isMounted) {\n setIsExiting(true);\n timeoutId = setTimeout(() => {\n setIsMounted(false);\n setIsExiting(false);\n callbacks.current.onClosed?.();\n }, Math.max(overlayDuration, panelDuration));\n }\n\n return () => clearTimeout(timeoutId);\n }, [open, isMounted, overlayDuration, panelDuration]);\n\n // 2. Safely Create/Destroy Portal Container\n useLayoutEffect(() => {\n if (typeof document === 'undefined') return;\n let el = document.getElementById(idName);\n if (!el) {\n el = document.createElement('div');\n el.setAttribute('id', idName);\n document.body.appendChild(el);\n containerCreatedByUsRef.current = true;\n }\n setPortalContainer(el);\n return () => {\n if (containerCreatedByUsRef.current && el?.parentNode) {\n document.body.removeChild(el);\n containerCreatedByUsRef.current = false;\n }\n };\n }, [idName]);\n\n // 3. Robust Scroll Locking (Nested Modal Support)\n const originalHtmlOverflowRef = useRef<string | null>(null);\n const wasAlreadyLockedRef = useRef(false);\n\n useEffect(() => {\n const el = panelRef.current;\n const shouldLock = open && lockScroll && el;\n\n if (shouldLock) {\n // Record the exact state of the page *before* we apply our locks\n const htmlOverflow = document.documentElement.style.overflow;\n const bodyOverflow = document.body.style.overflow;\n \n wasAlreadyLockedRef.current = htmlOverflow === 'hidden' || bodyOverflow === 'hidden';\n originalHtmlOverflowRef.current = htmlOverflow;\n\n disableBodyScroll(el, { reserveScrollBarGap: true });\n \n // Only force HTML overflow if the page wasn't already locked by something else\n if (lockScrollHtml && !wasAlreadyLockedRef.current) {\n document.documentElement.style.overflow = 'hidden';\n }\n }\n\n return () => {\n if (shouldLock && el) {\n enableBodyScroll(el);\n \n // Only restore the manual HTML overflow if *we* were the ones who hid it\n if (lockScrollHtml && !wasAlreadyLockedRef.current) {\n if (originalHtmlOverflowRef.current) {\n document.documentElement.style.overflow = originalHtmlOverflowRef.current;\n } else {\n document.documentElement.style.removeProperty('overflow');\n }\n }\n }\n };\n }, [open, lockScroll, lockScrollHtml]);\n\n const closePanel = useCallback(() => {\n onOpenChange(false);\n }, [onOpenChange]);\n\n // 4. Styles matched perfectly to the CSS Keyframes\n const overlayStyles: React.CSSProperties = useMemo(\n () => ({\n zIndex: zIndexProp === 'auto' ? 9999 : zIndexProp,\n animationDuration: `${overlayDuration}ms`,\n ['--overlay-opacity' as string]: overlayOpacity,\n opacity: isExiting ? 0 : overlayOpacity,\n pointerEvents: isExiting ? 'none' : 'auto', \n backgroundColor: overlayColor,\n }),\n [zIndexProp, overlayDuration, overlayOpacity, overlayColor, isExiting]\n );\n\n const panelStyles: React.CSSProperties = useMemo(() => {\n const base: React.CSSProperties = {\n zIndex: zIndexProp === 'auto' ? 9999 : zIndexProp,\n backgroundColor: panelColor,\n animationDuration: `${panelDuration}ms`,\n display: 'flex', \n flexDirection: 'column',\n maxWidth: '100%',\n ...styleProp,\n };\n\n if (side === 'left' || side === 'right') {\n base.width = width;\n base.height = '100%';\n } else {\n base.height = height;\n base.width = '100%';\n base.maxHeight = '100vh';\n }\n return base;\n }, [zIndexProp, panelColor, panelDuration, side, width, height, styleProp]);\n\n const isCompletelyHidden = !isMounted;\n if (!portalContainer || (isCompletelyHidden && rerender)) return null;\n\n const overlayTransitionClass = isExiting ? 'overlay-leave-active' : 'overlay-enter-active';\n const panelTransitionClass = isExiting ? `${transition}-leave-active` : `${transition}-enter-active`;\n\n const panelContent = (\n <div\n ref={panelRef}\n className={`rsp rsp--${side}-side ${panelTransitionClass} ${className}`.trim()}\n style={panelStyles}\n >\n {header != null && (\n <div className={[headerClass, 'rsp__header'].filter(Boolean).join(' ')} style={{ flexShrink: 0 }}>\n {header}\n </div>\n )}\n \n <div className={[bodyClass, 'rsp__body'].filter(Boolean).join(' ')} style={{ flexGrow: 1, overflowY: 'auto' }}>\n {children}\n {!hideCloseBtn && <SidePanelCloseButton onClose={closePanel} />}\n </div>\n\n {footer != null && (\n <div className={[footerClass, 'rsp__footer'].filter(Boolean).join(' ')} style={{ flexShrink: 0 }}>\n {footer}\n </div>\n )}\n </div>\n );\n\n const content = (\n <div \n className={`rsp-wrapper${!isCompletelyHidden ? ' rsp-wrapper--active' : ''}`}\n style={{ display: isCompletelyHidden ? 'none' : 'block' }}\n >\n {!isCompletelyHidden && (\n <div\n className={`rsp-overlay ${overlayTransitionClass}`}\n style={overlayStyles}\n onClick={() => !noClose && closePanel()}\n role=\"presentation\"\n aria-hidden=\"true\"\n />\n )}\n {panelContent}\n </div>\n );\n\n return createPortal(content, portalContainer);\n}\n\nSlidePanel.displayName = 'SlidePanel';","export interface SidePanelCloseButtonProps {\n onClose: () => void;\n className?: string;\n}\n\nexport function SidePanelCloseButton({ onClose, className }: SidePanelCloseButtonProps) {\n return (\n <button\n type=\"button\"\n className={className ? `rsp-close ${className}` : 'rsp-close'}\n onClick={onClose}\n aria-label=\"Close panel\"\n >\n <span className=\"rsp-close__x\" />\n </button>\n );\n}\n"],"mappings":"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","names":["import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/SlidePanel.tsx","../src/SlidePanelCloseButton.tsx"],"sourcesContent":["export { SlidePanel } from './SlidePanel';\nexport type { SlidePanelProps, SlidePanelSide } from './SlidePanel';\nexport { SidePanelCloseButton } from './SlidePanelCloseButton';\nexport type { SidePanelCloseButtonProps } from './SlidePanelCloseButton';\n","import React, {\n useRef,\n useEffect,\n useState,\n useCallback,\n useLayoutEffect,\n useMemo,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';\nimport { SidePanelCloseButton } from './SlidePanelCloseButton';\nimport './styles.css';\n\nexport type SlidePanelSide = 'top' | 'right' | 'bottom' | 'left';\n\nexport interface SlidePanelProps {\n /** Whether the panel is open. */\n open: boolean;\n /** Callback when open state should change (e.g. user closes). Pass the new boolean to update controlled state. */\n onOpenChange: (open: boolean) => void;\n /** Called when the close transition finishes and the panel is hidden. */\n onClosed?: () => void;\n /** Called when the open transition finishes and the panel is visible. */\n onOpened?: () => void;\n /** ID of the portal container element. Default: `'rsp-container'`. */\n idName?: string;\n /** Hide the default close button. */\n hideCloseBtn?: boolean;\n /** Disable closing by clicking the overlay. */\n noClose?: boolean;\n /** Which side the panel slides from. Default: `'right'`. */\n side?: SlidePanelSide;\n /** When true, unmount panel when closed and remount when opened. Default: `true`. */\n rerender?: boolean;\n /** z-index of overlay and panel. `'auto'` uses a high default. */\n zIndex?: number | 'auto';\n /** Panel width (e.g. `'500px'`). Only for side `'left'` or `'right'`. */\n width?: string;\n /** Panel height (e.g. `'500px'`). Only for side `'top'` or `'bottom'`. */\n height?: string;\n /** Lock body scroll when the panel is open. */\n lockScroll?: boolean;\n /** When lockScroll is true, also set overflow hidden on html. Default: `true`. */\n lockScrollHtml?: boolean;\n /** Overlay background color (any valid CSS color). Default: `'black'`. */\n overlayColor?: string;\n /** Overlay opacity (0–1). Default: `0.5`. */\n overlayOpacity?: number;\n /** Overlay transition duration in ms. Default: `500`. */\n overlayDuration?: number;\n /** Panel background color. Default: `'white'`. */\n panelColor?: string;\n /** Panel transition duration in ms. Default: `300`. */\n panelDuration?: number;\n /** Animation class name: `slide-right` | `slide-left` | `slide-top` | `slide-bottom`, or custom. Default picks from `side`. */\n transitionName?: string;\n /** Class for the header container. */\n headerClass?: string;\n /** Class for the scrollable body container. */\n bodyClass?: string;\n /** Class for the footer container. */\n footerClass?: string;\n /** Optional fixed header content. */\n header?: React.ReactNode;\n /** Optional fixed footer content. */\n footer?: React.ReactNode;\n /** Panel body content. */\n children?: React.ReactNode;\n /** Additional class for the panel element. */\n className?: string;\n /** Inline styles for the panel element. */\n style?: React.CSSProperties;\n}\n\n/**\n * A modal slide panel that slides in from the chosen edge (top, right, bottom, left).\n * Renders via a portal and supports overlay, scroll lock, and custom header/footer.\n */\nexport function SlidePanel({\n open,\n onOpenChange,\n onClosed,\n onOpened,\n idName = 'rsp-container',\n hideCloseBtn = false,\n noClose = false,\n side = 'right',\n rerender = true,\n zIndex: zIndexProp = 'auto',\n width = 'auto',\n height = 'auto',\n lockScroll = false,\n lockScrollHtml = true,\n overlayColor = 'black',\n overlayOpacity = 0.5,\n overlayDuration = 500,\n panelColor = 'white',\n panelDuration = 300,\n transitionName,\n headerClass = '',\n bodyClass = '',\n footerClass = '',\n header,\n footer,\n children,\n className = '',\n style: styleProp,\n}: SlidePanelProps) {\n const [isMounted, setIsMounted] = useState(open);\n const [isExiting, setIsExiting] = useState(false);\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);\n\n const panelRef = useRef<HTMLDivElement | null>(null);\n const containerCreatedByUsRef = useRef(false);\n\n const transition = transitionName ?? `slide-${side}`;\n\n const callbacks = useRef({ onOpened, onClosed });\n useLayoutEffect(() => {\n callbacks.current = { onOpened, onClosed };\n });\n\n // 1. Manage Mount & Exit Animation States\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout>;\n\n if (open) {\n setIsMounted(true);\n setIsExiting(false);\n timeoutId = setTimeout(() => {\n callbacks.current.onOpened?.();\n }, Math.max(overlayDuration, panelDuration));\n } else if (isMounted) {\n setIsExiting(true);\n timeoutId = setTimeout(() => {\n setIsMounted(false);\n setIsExiting(false);\n callbacks.current.onClosed?.();\n }, Math.max(overlayDuration, panelDuration));\n }\n\n return () => clearTimeout(timeoutId);\n }, [open, isMounted, overlayDuration, panelDuration]);\n\n // 2. Safely Create/Destroy Portal Container\n useLayoutEffect(() => {\n if (typeof document === 'undefined') return;\n let el = document.getElementById(idName);\n if (!el) {\n el = document.createElement('div');\n el.setAttribute('id', idName);\n document.body.appendChild(el);\n containerCreatedByUsRef.current = true;\n }\n setPortalContainer(el);\n return () => {\n if (containerCreatedByUsRef.current && el?.parentNode) {\n document.body.removeChild(el);\n containerCreatedByUsRef.current = false;\n }\n };\n }, [idName]);\n\n // 3. Robust Scroll Locking (Nested Modal Support)\n const originalHtmlOverflowRef = useRef<string | null>(null);\n const wasAlreadyLockedRef = useRef(false);\n\n useEffect(() => {\n const el = panelRef.current;\n const shouldLock = isMounted && lockScroll && el;\n\n if (shouldLock) {\n // Record the exact state of the page *before* we apply our locks\n const htmlOverflow = document.documentElement.style.overflow;\n const bodyOverflow = document.body.style.overflow;\n \n wasAlreadyLockedRef.current = htmlOverflow === 'hidden' || bodyOverflow === 'hidden';\n originalHtmlOverflowRef.current = htmlOverflow;\n\n disableBodyScroll(el, { reserveScrollBarGap: true });\n \n // Only force HTML overflow if the page wasn't already locked by something else\n if (lockScrollHtml && !wasAlreadyLockedRef.current) {\n document.documentElement.style.overflow = 'hidden';\n }\n }\n\n return () => {\n if (shouldLock && el) {\n enableBodyScroll(el);\n \n // Only restore the manual HTML overflow if *we* were the ones who hid it\n if (lockScrollHtml && !wasAlreadyLockedRef.current) {\n if (originalHtmlOverflowRef.current) {\n document.documentElement.style.overflow = originalHtmlOverflowRef.current;\n } else {\n document.documentElement.style.removeProperty('overflow');\n }\n }\n }\n };\n }, [isMounted, lockScroll, lockScrollHtml]);\n\n const closePanel = useCallback(() => {\n onOpenChange(false);\n }, [onOpenChange]);\n\n // 4. Styles matched perfectly to the CSS Keyframes\n const overlayStyles: React.CSSProperties = useMemo(\n () => ({\n zIndex: zIndexProp === 'auto' ? 9999 : zIndexProp,\n animationDuration: `${overlayDuration}ms`,\n ['--overlay-opacity' as string]: overlayOpacity,\n opacity: isExiting ? 0 : overlayOpacity,\n pointerEvents: isExiting ? 'none' : 'auto', \n backgroundColor: overlayColor,\n }),\n [zIndexProp, overlayDuration, overlayOpacity, overlayColor, isExiting]\n );\n\n const panelStyles: React.CSSProperties = useMemo(() => {\n const base: React.CSSProperties = {\n zIndex: zIndexProp === 'auto' ? 9999 : zIndexProp,\n backgroundColor: panelColor,\n animationDuration: `${panelDuration}ms`,\n display: 'flex', \n flexDirection: 'column',\n maxWidth: '100%',\n ...styleProp,\n };\n\n if (side === 'left' || side === 'right') {\n base.width = width;\n base.height = '100%';\n } else {\n base.height = height;\n base.width = '100%';\n base.maxHeight = '100vh';\n }\n return base;\n }, [zIndexProp, panelColor, panelDuration, side, width, height, styleProp]);\n\n const isCompletelyHidden = !isMounted;\n if (!portalContainer || (isCompletelyHidden && rerender)) return null;\n\n const overlayTransitionClass = isExiting ? 'overlay-leave-active' : 'overlay-enter-active';\n const panelTransitionClass = isExiting ? `${transition}-leave-active` : `${transition}-enter-active`;\n\n const panelContent = (\n <div\n ref={panelRef}\n className={`rsp rsp--${side}-side ${panelTransitionClass} ${className}`.trim()}\n style={panelStyles}\n >\n {header != null && (\n <div className={[headerClass, 'rsp__header'].filter(Boolean).join(' ')} style={{ flexShrink: 0 }}>\n {header}\n </div>\n )}\n \n <div className={[bodyClass, 'rsp__body'].filter(Boolean).join(' ')} style={{ flexGrow: 1, overflowY: 'auto' }}>\n {children}\n {!hideCloseBtn && <SidePanelCloseButton onClose={closePanel} />}\n </div>\n\n {footer != null && (\n <div className={[footerClass, 'rsp__footer'].filter(Boolean).join(' ')} style={{ flexShrink: 0 }}>\n {footer}\n </div>\n )}\n </div>\n );\n\n const content = (\n <div \n className={`rsp-wrapper${!isCompletelyHidden ? ' rsp-wrapper--active' : ''}`}\n style={{ display: isCompletelyHidden ? 'none' : 'block' }}\n >\n {!isCompletelyHidden && (\n <div\n className={`rsp-overlay ${overlayTransitionClass}`}\n style={overlayStyles}\n onClick={() => !noClose && closePanel()}\n role=\"presentation\"\n aria-hidden=\"true\"\n />\n )}\n {panelContent}\n </div>\n );\n\n return createPortal(content, portalContainer);\n}\n\nSlidePanel.displayName = 'SlidePanel';","export interface SidePanelCloseButtonProps {\n onClose: () => void;\n className?: string;\n}\n\nexport function SidePanelCloseButton({ onClose, className }: SidePanelCloseButtonProps) {\n return (\n <button\n type=\"button\"\n className={className ? `rsp-close ${className}` : 'rsp-close'}\n onClick={onClose}\n aria-label=\"Close panel\"\n >\n <span className=\"rsp-close__x\" />\n </button>\n );\n}\n"],"mappings":"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","names":["import_jsx_runtime"]}
package/dist/index.mjs CHANGED
@@ -106,7 +106,7 @@ function SlidePanel({
106
106
  const wasAlreadyLockedRef = useRef(false);
107
107
  useEffect(() => {
108
108
  const el = panelRef.current;
109
- const shouldLock = open && lockScroll && el;
109
+ const shouldLock = isMounted && lockScroll && el;
110
110
  if (shouldLock) {
111
111
  const htmlOverflow = document.documentElement.style.overflow;
112
112
  const bodyOverflow = document.body.style.overflow;
@@ -129,7 +129,7 @@ function SlidePanel({
129
129
  }
130
130
  }
131
131
  };
132
- }, [open, lockScroll, lockScrollHtml]);
132
+ }, [isMounted, lockScroll, lockScrollHtml]);
133
133
  const closePanel = useCallback(() => {
134
134
  onOpenChange(false);
135
135
  }, [onOpenChange]);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/SlidePanel.tsx","../src/SlidePanelCloseButton.tsx"],"sourcesContent":["import React, {\n useRef,\n useEffect,\n useState,\n useCallback,\n useLayoutEffect,\n useMemo,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';\nimport { SidePanelCloseButton } from './SlidePanelCloseButton';\nimport './styles.css';\n\nexport type SlidePanelSide = 'top' | 'right' | 'bottom' | 'left';\n\nexport interface SlidePanelProps {\n /** Whether the panel is open. */\n open: boolean;\n /** Callback when open state should change (e.g. user closes). Pass the new boolean to update controlled state. */\n onOpenChange: (open: boolean) => void;\n /** Called when the close transition finishes and the panel is hidden. */\n onClosed?: () => void;\n /** Called when the open transition finishes and the panel is visible. */\n onOpened?: () => void;\n /** ID of the portal container element. Default: `'rsp-container'`. */\n idName?: string;\n /** Hide the default close button. */\n hideCloseBtn?: boolean;\n /** Disable closing by clicking the overlay. */\n noClose?: boolean;\n /** Which side the panel slides from. Default: `'right'`. */\n side?: SlidePanelSide;\n /** When true, unmount panel when closed and remount when opened. Default: `true`. */\n rerender?: boolean;\n /** z-index of overlay and panel. `'auto'` uses a high default. */\n zIndex?: number | 'auto';\n /** Panel width (e.g. `'500px'`). Only for side `'left'` or `'right'`. */\n width?: string;\n /** Panel height (e.g. `'500px'`). Only for side `'top'` or `'bottom'`. */\n height?: string;\n /** Lock body scroll when the panel is open. */\n lockScroll?: boolean;\n /** When lockScroll is true, also set overflow hidden on html. Default: `true`. */\n lockScrollHtml?: boolean;\n /** Overlay background color (any valid CSS color). Default: `'black'`. */\n overlayColor?: string;\n /** Overlay opacity (0–1). Default: `0.5`. */\n overlayOpacity?: number;\n /** Overlay transition duration in ms. Default: `500`. */\n overlayDuration?: number;\n /** Panel background color. Default: `'white'`. */\n panelColor?: string;\n /** Panel transition duration in ms. Default: `300`. */\n panelDuration?: number;\n /** Animation class name: `slide-right` | `slide-left` | `slide-top` | `slide-bottom`, or custom. Default picks from `side`. */\n transitionName?: string;\n /** Class for the header container. */\n headerClass?: string;\n /** Class for the scrollable body container. */\n bodyClass?: string;\n /** Class for the footer container. */\n footerClass?: string;\n /** Optional fixed header content. */\n header?: React.ReactNode;\n /** Optional fixed footer content. */\n footer?: React.ReactNode;\n /** Panel body content. */\n children?: React.ReactNode;\n /** Additional class for the panel element. */\n className?: string;\n /** Inline styles for the panel element. */\n style?: React.CSSProperties;\n}\n\n/**\n * A modal slide panel that slides in from the chosen edge (top, right, bottom, left).\n * Renders via a portal and supports overlay, scroll lock, and custom header/footer.\n */\nexport function SlidePanel({\n open,\n onOpenChange,\n onClosed,\n onOpened,\n idName = 'rsp-container',\n hideCloseBtn = false,\n noClose = false,\n side = 'right',\n rerender = true,\n zIndex: zIndexProp = 'auto',\n width = 'auto',\n height = 'auto',\n lockScroll = false,\n lockScrollHtml = true,\n overlayColor = 'black',\n overlayOpacity = 0.5,\n overlayDuration = 500,\n panelColor = 'white',\n panelDuration = 300,\n transitionName,\n headerClass = '',\n bodyClass = '',\n footerClass = '',\n header,\n footer,\n children,\n className = '',\n style: styleProp,\n}: SlidePanelProps) {\n const [isMounted, setIsMounted] = useState(open);\n const [isExiting, setIsExiting] = useState(false);\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);\n\n const panelRef = useRef<HTMLDivElement | null>(null);\n const containerCreatedByUsRef = useRef(false);\n\n const transition = transitionName ?? `slide-${side}`;\n\n const callbacks = useRef({ onOpened, onClosed });\n useLayoutEffect(() => {\n callbacks.current = { onOpened, onClosed };\n });\n\n // 1. Manage Mount & Exit Animation States\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout>;\n\n if (open) {\n setIsMounted(true);\n setIsExiting(false);\n timeoutId = setTimeout(() => {\n callbacks.current.onOpened?.();\n }, Math.max(overlayDuration, panelDuration));\n } else if (isMounted) {\n setIsExiting(true);\n timeoutId = setTimeout(() => {\n setIsMounted(false);\n setIsExiting(false);\n callbacks.current.onClosed?.();\n }, Math.max(overlayDuration, panelDuration));\n }\n\n return () => clearTimeout(timeoutId);\n }, [open, isMounted, overlayDuration, panelDuration]);\n\n // 2. Safely Create/Destroy Portal Container\n useLayoutEffect(() => {\n if (typeof document === 'undefined') return;\n let el = document.getElementById(idName);\n if (!el) {\n el = document.createElement('div');\n el.setAttribute('id', idName);\n document.body.appendChild(el);\n containerCreatedByUsRef.current = true;\n }\n setPortalContainer(el);\n return () => {\n if (containerCreatedByUsRef.current && el?.parentNode) {\n document.body.removeChild(el);\n containerCreatedByUsRef.current = false;\n }\n };\n }, [idName]);\n\n // 3. Robust Scroll Locking (Nested Modal Support)\n const originalHtmlOverflowRef = useRef<string | null>(null);\n const wasAlreadyLockedRef = useRef(false);\n\n useEffect(() => {\n const el = panelRef.current;\n const shouldLock = open && lockScroll && el;\n\n if (shouldLock) {\n // Record the exact state of the page *before* we apply our locks\n const htmlOverflow = document.documentElement.style.overflow;\n const bodyOverflow = document.body.style.overflow;\n \n wasAlreadyLockedRef.current = htmlOverflow === 'hidden' || bodyOverflow === 'hidden';\n originalHtmlOverflowRef.current = htmlOverflow;\n\n disableBodyScroll(el, { reserveScrollBarGap: true });\n \n // Only force HTML overflow if the page wasn't already locked by something else\n if (lockScrollHtml && !wasAlreadyLockedRef.current) {\n document.documentElement.style.overflow = 'hidden';\n }\n }\n\n return () => {\n if (shouldLock && el) {\n enableBodyScroll(el);\n \n // Only restore the manual HTML overflow if *we* were the ones who hid it\n if (lockScrollHtml && !wasAlreadyLockedRef.current) {\n if (originalHtmlOverflowRef.current) {\n document.documentElement.style.overflow = originalHtmlOverflowRef.current;\n } else {\n document.documentElement.style.removeProperty('overflow');\n }\n }\n }\n };\n }, [open, lockScroll, lockScrollHtml]);\n\n const closePanel = useCallback(() => {\n onOpenChange(false);\n }, [onOpenChange]);\n\n // 4. Styles matched perfectly to the CSS Keyframes\n const overlayStyles: React.CSSProperties = useMemo(\n () => ({\n zIndex: zIndexProp === 'auto' ? 9999 : zIndexProp,\n animationDuration: `${overlayDuration}ms`,\n ['--overlay-opacity' as string]: overlayOpacity,\n opacity: isExiting ? 0 : overlayOpacity,\n pointerEvents: isExiting ? 'none' : 'auto', \n backgroundColor: overlayColor,\n }),\n [zIndexProp, overlayDuration, overlayOpacity, overlayColor, isExiting]\n );\n\n const panelStyles: React.CSSProperties = useMemo(() => {\n const base: React.CSSProperties = {\n zIndex: zIndexProp === 'auto' ? 9999 : zIndexProp,\n backgroundColor: panelColor,\n animationDuration: `${panelDuration}ms`,\n display: 'flex', \n flexDirection: 'column',\n maxWidth: '100%',\n ...styleProp,\n };\n\n if (side === 'left' || side === 'right') {\n base.width = width;\n base.height = '100%';\n } else {\n base.height = height;\n base.width = '100%';\n base.maxHeight = '100vh';\n }\n return base;\n }, [zIndexProp, panelColor, panelDuration, side, width, height, styleProp]);\n\n const isCompletelyHidden = !isMounted;\n if (!portalContainer || (isCompletelyHidden && rerender)) return null;\n\n const overlayTransitionClass = isExiting ? 'overlay-leave-active' : 'overlay-enter-active';\n const panelTransitionClass = isExiting ? `${transition}-leave-active` : `${transition}-enter-active`;\n\n const panelContent = (\n <div\n ref={panelRef}\n className={`rsp rsp--${side}-side ${panelTransitionClass} ${className}`.trim()}\n style={panelStyles}\n >\n {header != null && (\n <div className={[headerClass, 'rsp__header'].filter(Boolean).join(' ')} style={{ flexShrink: 0 }}>\n {header}\n </div>\n )}\n \n <div className={[bodyClass, 'rsp__body'].filter(Boolean).join(' ')} style={{ flexGrow: 1, overflowY: 'auto' }}>\n {children}\n {!hideCloseBtn && <SidePanelCloseButton onClose={closePanel} />}\n </div>\n\n {footer != null && (\n <div className={[footerClass, 'rsp__footer'].filter(Boolean).join(' ')} style={{ flexShrink: 0 }}>\n {footer}\n </div>\n )}\n </div>\n );\n\n const content = (\n <div \n className={`rsp-wrapper${!isCompletelyHidden ? ' rsp-wrapper--active' : ''}`}\n style={{ display: isCompletelyHidden ? 'none' : 'block' }}\n >\n {!isCompletelyHidden && (\n <div\n className={`rsp-overlay ${overlayTransitionClass}`}\n style={overlayStyles}\n onClick={() => !noClose && closePanel()}\n role=\"presentation\"\n aria-hidden=\"true\"\n />\n )}\n {panelContent}\n </div>\n );\n\n return createPortal(content, portalContainer);\n}\n\nSlidePanel.displayName = 'SlidePanel';","export interface SidePanelCloseButtonProps {\n onClose: () => void;\n className?: string;\n}\n\nexport function SidePanelCloseButton({ onClose, className }: SidePanelCloseButtonProps) {\n return (\n <button\n type=\"button\"\n className={className ? `rsp-close ${className}` : 'rsp-close'}\n onClick={onClose}\n aria-label=\"Close panel\"\n >\n <span className=\"rsp-close__x\" />\n </button>\n );\n}\n"],"mappings":"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","names":["jsx"]}
1
+ {"version":3,"sources":["../src/SlidePanel.tsx","../src/SlidePanelCloseButton.tsx"],"sourcesContent":["import React, {\n useRef,\n useEffect,\n useState,\n useCallback,\n useLayoutEffect,\n useMemo,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';\nimport { SidePanelCloseButton } from './SlidePanelCloseButton';\nimport './styles.css';\n\nexport type SlidePanelSide = 'top' | 'right' | 'bottom' | 'left';\n\nexport interface SlidePanelProps {\n /** Whether the panel is open. */\n open: boolean;\n /** Callback when open state should change (e.g. user closes). Pass the new boolean to update controlled state. */\n onOpenChange: (open: boolean) => void;\n /** Called when the close transition finishes and the panel is hidden. */\n onClosed?: () => void;\n /** Called when the open transition finishes and the panel is visible. */\n onOpened?: () => void;\n /** ID of the portal container element. Default: `'rsp-container'`. */\n idName?: string;\n /** Hide the default close button. */\n hideCloseBtn?: boolean;\n /** Disable closing by clicking the overlay. */\n noClose?: boolean;\n /** Which side the panel slides from. Default: `'right'`. */\n side?: SlidePanelSide;\n /** When true, unmount panel when closed and remount when opened. Default: `true`. */\n rerender?: boolean;\n /** z-index of overlay and panel. `'auto'` uses a high default. */\n zIndex?: number | 'auto';\n /** Panel width (e.g. `'500px'`). Only for side `'left'` or `'right'`. */\n width?: string;\n /** Panel height (e.g. `'500px'`). Only for side `'top'` or `'bottom'`. */\n height?: string;\n /** Lock body scroll when the panel is open. */\n lockScroll?: boolean;\n /** When lockScroll is true, also set overflow hidden on html. Default: `true`. */\n lockScrollHtml?: boolean;\n /** Overlay background color (any valid CSS color). Default: `'black'`. */\n overlayColor?: string;\n /** Overlay opacity (0–1). Default: `0.5`. */\n overlayOpacity?: number;\n /** Overlay transition duration in ms. Default: `500`. */\n overlayDuration?: number;\n /** Panel background color. Default: `'white'`. */\n panelColor?: string;\n /** Panel transition duration in ms. Default: `300`. */\n panelDuration?: number;\n /** Animation class name: `slide-right` | `slide-left` | `slide-top` | `slide-bottom`, or custom. Default picks from `side`. */\n transitionName?: string;\n /** Class for the header container. */\n headerClass?: string;\n /** Class for the scrollable body container. */\n bodyClass?: string;\n /** Class for the footer container. */\n footerClass?: string;\n /** Optional fixed header content. */\n header?: React.ReactNode;\n /** Optional fixed footer content. */\n footer?: React.ReactNode;\n /** Panel body content. */\n children?: React.ReactNode;\n /** Additional class for the panel element. */\n className?: string;\n /** Inline styles for the panel element. */\n style?: React.CSSProperties;\n}\n\n/**\n * A modal slide panel that slides in from the chosen edge (top, right, bottom, left).\n * Renders via a portal and supports overlay, scroll lock, and custom header/footer.\n */\nexport function SlidePanel({\n open,\n onOpenChange,\n onClosed,\n onOpened,\n idName = 'rsp-container',\n hideCloseBtn = false,\n noClose = false,\n side = 'right',\n rerender = true,\n zIndex: zIndexProp = 'auto',\n width = 'auto',\n height = 'auto',\n lockScroll = false,\n lockScrollHtml = true,\n overlayColor = 'black',\n overlayOpacity = 0.5,\n overlayDuration = 500,\n panelColor = 'white',\n panelDuration = 300,\n transitionName,\n headerClass = '',\n bodyClass = '',\n footerClass = '',\n header,\n footer,\n children,\n className = '',\n style: styleProp,\n}: SlidePanelProps) {\n const [isMounted, setIsMounted] = useState(open);\n const [isExiting, setIsExiting] = useState(false);\n const [portalContainer, setPortalContainer] = useState<HTMLElement | null>(null);\n\n const panelRef = useRef<HTMLDivElement | null>(null);\n const containerCreatedByUsRef = useRef(false);\n\n const transition = transitionName ?? `slide-${side}`;\n\n const callbacks = useRef({ onOpened, onClosed });\n useLayoutEffect(() => {\n callbacks.current = { onOpened, onClosed };\n });\n\n // 1. Manage Mount & Exit Animation States\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout>;\n\n if (open) {\n setIsMounted(true);\n setIsExiting(false);\n timeoutId = setTimeout(() => {\n callbacks.current.onOpened?.();\n }, Math.max(overlayDuration, panelDuration));\n } else if (isMounted) {\n setIsExiting(true);\n timeoutId = setTimeout(() => {\n setIsMounted(false);\n setIsExiting(false);\n callbacks.current.onClosed?.();\n }, Math.max(overlayDuration, panelDuration));\n }\n\n return () => clearTimeout(timeoutId);\n }, [open, isMounted, overlayDuration, panelDuration]);\n\n // 2. Safely Create/Destroy Portal Container\n useLayoutEffect(() => {\n if (typeof document === 'undefined') return;\n let el = document.getElementById(idName);\n if (!el) {\n el = document.createElement('div');\n el.setAttribute('id', idName);\n document.body.appendChild(el);\n containerCreatedByUsRef.current = true;\n }\n setPortalContainer(el);\n return () => {\n if (containerCreatedByUsRef.current && el?.parentNode) {\n document.body.removeChild(el);\n containerCreatedByUsRef.current = false;\n }\n };\n }, [idName]);\n\n // 3. Robust Scroll Locking (Nested Modal Support)\n const originalHtmlOverflowRef = useRef<string | null>(null);\n const wasAlreadyLockedRef = useRef(false);\n\n useEffect(() => {\n const el = panelRef.current;\n const shouldLock = isMounted && lockScroll && el;\n\n if (shouldLock) {\n // Record the exact state of the page *before* we apply our locks\n const htmlOverflow = document.documentElement.style.overflow;\n const bodyOverflow = document.body.style.overflow;\n \n wasAlreadyLockedRef.current = htmlOverflow === 'hidden' || bodyOverflow === 'hidden';\n originalHtmlOverflowRef.current = htmlOverflow;\n\n disableBodyScroll(el, { reserveScrollBarGap: true });\n \n // Only force HTML overflow if the page wasn't already locked by something else\n if (lockScrollHtml && !wasAlreadyLockedRef.current) {\n document.documentElement.style.overflow = 'hidden';\n }\n }\n\n return () => {\n if (shouldLock && el) {\n enableBodyScroll(el);\n \n // Only restore the manual HTML overflow if *we* were the ones who hid it\n if (lockScrollHtml && !wasAlreadyLockedRef.current) {\n if (originalHtmlOverflowRef.current) {\n document.documentElement.style.overflow = originalHtmlOverflowRef.current;\n } else {\n document.documentElement.style.removeProperty('overflow');\n }\n }\n }\n };\n }, [isMounted, lockScroll, lockScrollHtml]);\n\n const closePanel = useCallback(() => {\n onOpenChange(false);\n }, [onOpenChange]);\n\n // 4. Styles matched perfectly to the CSS Keyframes\n const overlayStyles: React.CSSProperties = useMemo(\n () => ({\n zIndex: zIndexProp === 'auto' ? 9999 : zIndexProp,\n animationDuration: `${overlayDuration}ms`,\n ['--overlay-opacity' as string]: overlayOpacity,\n opacity: isExiting ? 0 : overlayOpacity,\n pointerEvents: isExiting ? 'none' : 'auto', \n backgroundColor: overlayColor,\n }),\n [zIndexProp, overlayDuration, overlayOpacity, overlayColor, isExiting]\n );\n\n const panelStyles: React.CSSProperties = useMemo(() => {\n const base: React.CSSProperties = {\n zIndex: zIndexProp === 'auto' ? 9999 : zIndexProp,\n backgroundColor: panelColor,\n animationDuration: `${panelDuration}ms`,\n display: 'flex', \n flexDirection: 'column',\n maxWidth: '100%',\n ...styleProp,\n };\n\n if (side === 'left' || side === 'right') {\n base.width = width;\n base.height = '100%';\n } else {\n base.height = height;\n base.width = '100%';\n base.maxHeight = '100vh';\n }\n return base;\n }, [zIndexProp, panelColor, panelDuration, side, width, height, styleProp]);\n\n const isCompletelyHidden = !isMounted;\n if (!portalContainer || (isCompletelyHidden && rerender)) return null;\n\n const overlayTransitionClass = isExiting ? 'overlay-leave-active' : 'overlay-enter-active';\n const panelTransitionClass = isExiting ? `${transition}-leave-active` : `${transition}-enter-active`;\n\n const panelContent = (\n <div\n ref={panelRef}\n className={`rsp rsp--${side}-side ${panelTransitionClass} ${className}`.trim()}\n style={panelStyles}\n >\n {header != null && (\n <div className={[headerClass, 'rsp__header'].filter(Boolean).join(' ')} style={{ flexShrink: 0 }}>\n {header}\n </div>\n )}\n \n <div className={[bodyClass, 'rsp__body'].filter(Boolean).join(' ')} style={{ flexGrow: 1, overflowY: 'auto' }}>\n {children}\n {!hideCloseBtn && <SidePanelCloseButton onClose={closePanel} />}\n </div>\n\n {footer != null && (\n <div className={[footerClass, 'rsp__footer'].filter(Boolean).join(' ')} style={{ flexShrink: 0 }}>\n {footer}\n </div>\n )}\n </div>\n );\n\n const content = (\n <div \n className={`rsp-wrapper${!isCompletelyHidden ? ' rsp-wrapper--active' : ''}`}\n style={{ display: isCompletelyHidden ? 'none' : 'block' }}\n >\n {!isCompletelyHidden && (\n <div\n className={`rsp-overlay ${overlayTransitionClass}`}\n style={overlayStyles}\n onClick={() => !noClose && closePanel()}\n role=\"presentation\"\n aria-hidden=\"true\"\n />\n )}\n {panelContent}\n </div>\n );\n\n return createPortal(content, portalContainer);\n}\n\nSlidePanel.displayName = 'SlidePanel';","export interface SidePanelCloseButtonProps {\n onClose: () => void;\n className?: string;\n}\n\nexport function SidePanelCloseButton({ onClose, className }: SidePanelCloseButtonProps) {\n return (\n <button\n type=\"button\"\n className={className ? `rsp-close ${className}` : 'rsp-close'}\n onClick={onClose}\n aria-label=\"Close panel\"\n >\n <span className=\"rsp-close__x\" />\n </button>\n );\n}\n"],"mappings":"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","names":["jsx"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-slide-panel",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Easy to use and flexible modal drawer/sidebar component for React (TypeScript)",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",