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 +2 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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 =
|
|
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
|
-
}, [
|
|
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 =
|
|
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
|
-
}, [
|
|
132
|
+
}, [isMounted, lockScroll, lockScrollHtml]);
|
|
133
133
|
const closePanel = useCallback(() => {
|
|
134
134
|
onOpenChange(false);
|
|
135
135
|
}, [onOpenChange]);
|
package/dist/index.mjs.map
CHANGED
|
@@ -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"]}
|