@brycks/core-front 0.2.8 → 0.2.10

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.
Files changed (28) hide show
  1. package/dist/components/feedback/Drawer/Drawer.cjs +2 -2
  2. package/dist/components/feedback/Drawer/Drawer.cjs.map +1 -1
  3. package/dist/components/feedback/Drawer/Drawer.js +78 -74
  4. package/dist/components/feedback/Drawer/Drawer.js.map +1 -1
  5. package/dist/components/feedback/Modal/Modal.cjs +1 -1
  6. package/dist/components/feedback/Modal/Modal.cjs.map +1 -1
  7. package/dist/components/feedback/Modal/Modal.js +76 -70
  8. package/dist/components/feedback/Modal/Modal.js.map +1 -1
  9. package/dist/components/form/FormLabel/FormLabel.cjs +1 -1
  10. package/dist/components/form/FormLabel/FormLabel.cjs.map +1 -1
  11. package/dist/components/form/FormLabel/FormLabel.js +40 -40
  12. package/dist/components/form/FormLabel/FormLabel.js.map +1 -1
  13. package/dist/components/form/Input/Input.cjs +1 -1
  14. package/dist/components/form/Input/Input.js +13 -13
  15. package/dist/components/primitives/Button/Button.styles.cjs +1 -1
  16. package/dist/components/primitives/Button/Button.styles.cjs.map +1 -1
  17. package/dist/components/primitives/Button/Button.styles.js +24 -24
  18. package/dist/components/primitives/Button/Button.styles.js.map +1 -1
  19. package/dist/components/typography/Heading/Heading.cjs +1 -1
  20. package/dist/components/typography/Heading/Heading.cjs.map +1 -1
  21. package/dist/components/typography/Heading/Heading.js +42 -32
  22. package/dist/components/typography/Heading/Heading.js.map +1 -1
  23. package/dist/components/typography/Text/Text.cjs +1 -1
  24. package/dist/components/typography/Text/Text.cjs.map +1 -1
  25. package/dist/components/typography/Text/Text.js +59 -36
  26. package/dist/components/typography/Text/Text.js.map +1 -1
  27. package/dist/styles.css +1 -1
  28. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),a=require("react"),P=require("react-dom"),X=require("../../../utils/styles.cjs"),Y={sm:"320px",md:"400px",lg:"560px",xl:"720px",full:"100%"};function K(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:r.jsx("path",{d:"M4 4l8 8M12 4l-8 8",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})}const g=a.forwardRef(function({isOpen:c,onClose:n,placement:t="right",size:d="md",title:s,closeOnOverlayClick:u=!0,closeOnEscape:y=!0,showCloseButton:f=!0,showOverlay:x=!0,className:p,style:j,children:S,testId:E,...D},l){const b=a.useRef(null),m=a.useRef(null);a.useEffect(()=>{if(!c)return;m.current=document.activeElement;const e=b.current;e&&e.focus();const v=o=>{if(o.key==="Escape"&&y){n();return}if(o.key==="Tab"&&e){const i=e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),w=i[0],h=i[i.length-1];o.shiftKey&&document.activeElement===w?(o.preventDefault(),h?.focus()):!o.shiftKey&&document.activeElement===h&&(o.preventDefault(),w?.focus())}};return document.addEventListener("keydown",v),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",v),document.body.style.overflow="",m.current?.focus()}},[c,y,n]);const C=a.useCallback(e=>{e.target===e.currentTarget&&u&&n()},[u,n]);if(!c)return null;const R=t==="left"||t==="right",k=Y[d],T={position:"fixed",inset:0,zIndex:"var(--brycks-z-drawer)",display:"flex",backgroundColor:x?"var(--brycks-background-overlay)":"transparent",animation:"brycks-drawer-overlay-in 200ms ease-out"},z={...(()=>{const e={position:"absolute",backgroundColor:"var(--brycks-background-elevated)",boxShadow:"var(--brycks-shadow-2xl)",display:"flex",flexDirection:"column",overflow:"hidden"};return R?{...e,top:0,bottom:0,[t]:0,width:k,maxWidth:"100vw"}:{...e,left:0,right:0,[t]:0,height:k,maxHeight:"100vh"}})(),animation:`brycks-drawer-${t}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,outline:"none",...j},M={display:"flex",alignItems:"center",justifyContent:"space-between",padding:"16px 20px",borderBottom:"1px solid var(--brycks-border-muted)",flexShrink:0},N={fontSize:18,fontWeight:600,color:"var(--brycks-foreground-default)",margin:0},q={display:"flex",alignItems:"center",justifyContent:"center",width:32,height:32,borderRadius:"var(--brycks-radius-md)",color:"var(--brycks-foreground-muted)",transition:"all 150ms ease-out"},I={flex:1,overflow:"auto",padding:20},L=r.jsxs(r.Fragment,{children:[r.jsx("style",{children:`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),t=require("react"),X=require("react-dom"),Y=require("../../../utils/styles.cjs"),K={sm:"320px",md:"400px",lg:"560px",xl:"720px",full:"100%"};function W(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",children:r.jsx("path",{d:"M4 4l8 8M12 4l-8 8",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})}const x=t.forwardRef(function({isOpen:s,onClose:n,placement:o="right",size:u="md",title:c,closeOnOverlayClick:f=!0,closeOnEscape:y=!0,showCloseButton:b=!0,showOverlay:p=!0,className:j,style:S,children:E,testId:D,...R},l){const i=t.useRef(null),m=t.useRef(null),k=t.useRef(n);t.useEffect(()=>{k.current=n},[n]),t.useEffect(()=>{if(!s)return;m.current=document.activeElement;const e=i.current;e&&e.focus()},[s]),t.useEffect(()=>{if(!s)return;const e=i.current,v=a=>{if(a.key==="Escape"&&y){k.current();return}if(a.key==="Tab"&&e){const d=e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),h=d[0],g=d[d.length-1];a.shiftKey&&document.activeElement===h?(a.preventDefault(),g?.focus()):!a.shiftKey&&document.activeElement===g&&(a.preventDefault(),h?.focus())}};return document.addEventListener("keydown",v),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",v),document.body.style.overflow="",m.current?.focus()}},[s,y]);const C=t.useCallback(e=>{e.target===e.currentTarget&&f&&n()},[f,n]);if(!s)return null;const T=o==="left"||o==="right",w=K[u],z={position:"fixed",inset:0,zIndex:"var(--brycks-z-drawer)",display:"flex",backgroundColor:p?"var(--brycks-background-overlay)":"transparent",animation:"brycks-drawer-overlay-in 200ms ease-out"},M={...(()=>{const e={position:"absolute",backgroundColor:"var(--brycks-background-elevated)",boxShadow:"var(--brycks-shadow-2xl)",display:"flex",flexDirection:"column",overflow:"hidden"};return T?{...e,top:0,bottom:0,[o]:0,width:w,maxWidth:"100vw"}:{...e,left:0,right:0,[o]:0,height:w,maxHeight:"100vh"}})(),animation:`brycks-drawer-${o}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,outline:"none",...S},N={display:"flex",alignItems:"center",justifyContent:"space-between",padding:"16px 20px",borderBottom:"1px solid var(--brycks-border-muted)",flexShrink:0},q={fontSize:18,fontWeight:600,color:"var(--brycks-foreground-default)",margin:0},I={display:"flex",alignItems:"center",justifyContent:"center",width:32,height:32,borderRadius:"var(--brycks-radius-md)",color:"var(--brycks-foreground-muted)",transition:"all 150ms ease-out"},L={flex:1,overflow:"auto",padding:20},P=r.jsxs(r.Fragment,{children:[r.jsx("style",{children:`
2
2
  @keyframes brycks-drawer-overlay-in {
3
3
  from { opacity: 0; }
4
4
  to { opacity: 1; }
@@ -19,5 +19,5 @@
19
19
  from { transform: translateY(100%); }
20
20
  to { transform: translateY(0); }
21
21
  }
22
- `}),r.jsx("div",{className:"brycks-drawer-overlay",style:T,onClick:C,"aria-hidden":"true",children:r.jsxs("div",{ref:e=>{b.current=e,typeof l=="function"?l(e):l&&(l.current=e)},role:"dialog","aria-modal":"true","aria-labelledby":s?"brycks-drawer-title":void 0,className:X.cx("brycks-drawer",`brycks-drawer--${t}`,`brycks-drawer--${d}`,p),style:z,tabIndex:-1,"data-testid":E,...D,children:[(s||f)&&r.jsxs("div",{className:"brycks-drawer-header",style:M,children:[s&&r.jsx("h2",{id:"brycks-drawer-title",style:N,children:s}),f&&r.jsx("button",{type:"button",className:"brycks-drawer-close",style:q,onClick:n,"aria-label":"Close drawer",onMouseEnter:e=>{e.currentTarget.style.backgroundColor="var(--brycks-background-muted)",e.currentTarget.style.color="var(--brycks-foreground-default)"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="transparent",e.currentTarget.style.color="var(--brycks-foreground-muted)"},children:r.jsx(K,{})})]}),r.jsx("div",{className:"brycks-drawer-content",style:I,children:S})]})})]});return P.createPortal(L,document.body)});g.displayName="Drawer";exports.Drawer=g;
22
+ `}),r.jsx("div",{className:"brycks-drawer-overlay",style:z,onClick:C,"aria-hidden":"true",children:r.jsxs("div",{ref:e=>{i.current=e,typeof l=="function"?l(e):l&&(l.current=e)},role:"dialog","aria-modal":"true","aria-labelledby":c?"brycks-drawer-title":void 0,className:Y.cx("brycks-drawer",`brycks-drawer--${o}`,`brycks-drawer--${u}`,j),style:M,tabIndex:-1,"data-testid":D,...R,children:[(c||b)&&r.jsxs("div",{className:"brycks-drawer-header",style:N,children:[c&&r.jsx("h2",{id:"brycks-drawer-title",style:q,children:c}),b&&r.jsx("button",{type:"button",className:"brycks-drawer-close",style:I,onClick:n,"aria-label":"Close drawer",onMouseEnter:e=>{e.currentTarget.style.backgroundColor="var(--brycks-background-muted)",e.currentTarget.style.color="var(--brycks-foreground-default)"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="transparent",e.currentTarget.style.color="var(--brycks-foreground-muted)"},children:r.jsx(W,{})})]}),r.jsx("div",{className:"brycks-drawer-content",style:L,children:E})]})})]});return X.createPortal(P,document.body)});x.displayName="Drawer";exports.Drawer=x;
23
23
  //# sourceMappingURL=Drawer.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Drawer.cjs","sources":["../../../../src/components/feedback/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * Drawer Component\n *\n * A slide-out panel that can appear from any edge of the screen.\n * Supports focus trapping and keyboard navigation.\n */\n\nimport {\n forwardRef,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\n\nexport type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom'\nexport type DrawerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Whether the drawer is open */\n isOpen: boolean\n /** Callback when drawer should close */\n onClose: () => void\n /** Drawer placement */\n placement?: DrawerPlacement\n /** Drawer size */\n size?: DrawerSize\n /** Drawer title */\n title?: ReactNode\n /** Whether to close on overlay click */\n closeOnOverlayClick?: boolean\n /** Whether to close on escape key */\n closeOnEscape?: boolean\n /** Whether to show close button */\n showCloseButton?: boolean\n /** Whether to show overlay */\n showOverlay?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeMap: Record<DrawerSize, string> = {\n sm: '320px',\n md: '400px',\n lg: '560px',\n xl: '720px',\n full: '100%',\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n placement = 'right',\n size = 'md',\n title,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n showCloseButton = true,\n showOverlay = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const drawerRef = useRef<HTMLDivElement>(null)\n const previousActiveElement = useRef<HTMLElement | null>(null)\n\n // Focus trap and keyboard handling\n useEffect(() => {\n if (!isOpen) return\n\n previousActiveElement.current = document.activeElement as HTMLElement\n const drawer = drawerRef.current\n if (drawer) {\n drawer.focus()\n }\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && closeOnEscape) {\n onClose()\n return\n }\n\n if (e.key === 'Tab' && drawer) {\n const focusableElements = drawer.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n const firstElement = focusableElements[0]\n const lastElement = focusableElements[focusableElements.length - 1]\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault()\n lastElement?.focus()\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault()\n firstElement?.focus()\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n document.body.style.overflow = 'hidden'\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown)\n document.body.style.overflow = ''\n previousActiveElement.current?.focus()\n }\n }, [isOpen, closeOnEscape, onClose])\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget && closeOnOverlayClick) {\n onClose()\n }\n },\n [closeOnOverlayClick, onClose]\n )\n\n if (!isOpen) return null\n\n const isHorizontal = placement === 'left' || placement === 'right'\n const drawerSize = sizeMap[size]\n\n const overlayStyle: CSSProperties = {\n position: 'fixed',\n inset: 0,\n zIndex: 'var(--brycks-z-drawer)' as unknown as number,\n display: 'flex',\n backgroundColor: showOverlay ? 'var(--brycks-background-overlay)' : 'transparent',\n animation: 'brycks-drawer-overlay-in 200ms ease-out',\n }\n\n const getPositionStyles = (): CSSProperties => {\n const base: CSSProperties = {\n position: 'absolute',\n backgroundColor: 'var(--brycks-background-elevated)',\n boxShadow: 'var(--brycks-shadow-2xl)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }\n\n if (isHorizontal) {\n return {\n ...base,\n top: 0,\n bottom: 0,\n [placement]: 0,\n width: drawerSize,\n maxWidth: '100vw',\n }\n }\n\n return {\n ...base,\n left: 0,\n right: 0,\n [placement]: 0,\n height: drawerSize,\n maxHeight: '100vh',\n }\n }\n\n const drawerStyle: CSSProperties = {\n ...getPositionStyles(),\n animation: `brycks-drawer-${placement}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,\n outline: 'none',\n ...style,\n }\n\n const headerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '16px 20px',\n borderBottom: '1px solid var(--brycks-border-muted)',\n flexShrink: 0,\n }\n\n const titleStyle: CSSProperties = {\n fontSize: 18,\n fontWeight: 600,\n color: 'var(--brycks-foreground-default)',\n margin: 0,\n }\n\n const closeButtonStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'var(--brycks-radius-md)',\n color: 'var(--brycks-foreground-muted)',\n transition: 'all 150ms ease-out',\n }\n\n const contentStyle: CSSProperties = {\n flex: 1,\n overflow: 'auto',\n padding: 20,\n }\n\n const drawerContent = (\n <>\n <style>\n {`\n @keyframes brycks-drawer-overlay-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes brycks-drawer-left-in {\n from { transform: translateX(-100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-right-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-top-in {\n from { transform: translateY(-100%); }\n to { transform: translateY(0); }\n }\n @keyframes brycks-drawer-bottom-in {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n }\n `}\n </style>\n <div\n className=\"brycks-drawer-overlay\"\n style={overlayStyle}\n onClick={handleOverlayClick}\n aria-hidden=\"true\"\n >\n <div\n ref={(node) => {\n (drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'brycks-drawer-title' : undefined}\n className={cx('brycks-drawer', `brycks-drawer--${placement}`, `brycks-drawer--${size}`, className)}\n style={drawerStyle}\n tabIndex={-1}\n data-testid={testId}\n {...props}\n >\n {(title || showCloseButton) && (\n <div className=\"brycks-drawer-header\" style={headerStyle}>\n {title && <h2 id=\"brycks-drawer-title\" style={titleStyle}>{title}</h2>}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"brycks-drawer-close\"\n style={closeButtonStyle}\n onClick={onClose}\n aria-label=\"Close drawer\"\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\n }}\n >\n <CloseIcon />\n </button>\n )}\n </div>\n )}\n <div className=\"brycks-drawer-content\" style={contentStyle}>\n {children}\n </div>\n </div>\n </div>\n </>\n )\n\n return createPortal(drawerContent, document.body)\n})\n\nDrawer.displayName = 'Drawer'\n"],"names":["sizeMap","CloseIcon","jsx","Drawer","forwardRef","isOpen","onClose","placement","size","title","closeOnOverlayClick","closeOnEscape","showCloseButton","showOverlay","className","style","children","testId","props","ref","drawerRef","useRef","previousActiveElement","useEffect","drawer","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","isHorizontal","drawerSize","overlayStyle","drawerStyle","base","headerStyle","titleStyle","closeButtonStyle","contentStyle","drawerContent","jsxs","Fragment","node","cx","createPortal"],"mappings":"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"}
1
+ {"version":3,"file":"Drawer.cjs","sources":["../../../../src/components/feedback/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * Drawer Component\n *\n * A slide-out panel that can appear from any edge of the screen.\n * Supports focus trapping and keyboard navigation.\n */\n\nimport {\n forwardRef,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\n\nexport type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom'\nexport type DrawerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Whether the drawer is open */\n isOpen: boolean\n /** Callback when drawer should close */\n onClose: () => void\n /** Drawer placement */\n placement?: DrawerPlacement\n /** Drawer size */\n size?: DrawerSize\n /** Drawer title */\n title?: ReactNode\n /** Whether to close on overlay click */\n closeOnOverlayClick?: boolean\n /** Whether to close on escape key */\n closeOnEscape?: boolean\n /** Whether to show close button */\n showCloseButton?: boolean\n /** Whether to show overlay */\n showOverlay?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeMap: Record<DrawerSize, string> = {\n sm: '320px',\n md: '400px',\n lg: '560px',\n xl: '720px',\n full: '100%',\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n placement = 'right',\n size = 'md',\n title,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n showCloseButton = true,\n showOverlay = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const drawerRef = useRef<HTMLDivElement>(null)\n const previousActiveElement = useRef<HTMLElement | null>(null)\n\n // Store onClose in a ref to avoid re-running effects when it changes\n const onCloseRef = useRef(onClose)\n useEffect(() => {\n onCloseRef.current = onClose\n }, [onClose])\n\n // Focus drawer when it opens (only once)\n useEffect(() => {\n if (!isOpen) return\n\n previousActiveElement.current = document.activeElement as HTMLElement\n const drawer = drawerRef.current\n if (drawer) {\n drawer.focus()\n }\n }, [isOpen])\n\n // Keyboard handling and body scroll lock\n useEffect(() => {\n if (!isOpen) return\n\n const drawer = drawerRef.current\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && closeOnEscape) {\n onCloseRef.current()\n return\n }\n\n if (e.key === 'Tab' && drawer) {\n const focusableElements = drawer.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n const firstElement = focusableElements[0]\n const lastElement = focusableElements[focusableElements.length - 1]\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault()\n lastElement?.focus()\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault()\n firstElement?.focus()\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n document.body.style.overflow = 'hidden'\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown)\n document.body.style.overflow = ''\n previousActiveElement.current?.focus()\n }\n }, [isOpen, closeOnEscape])\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget && closeOnOverlayClick) {\n onClose()\n }\n },\n [closeOnOverlayClick, onClose]\n )\n\n if (!isOpen) return null\n\n const isHorizontal = placement === 'left' || placement === 'right'\n const drawerSize = sizeMap[size]\n\n const overlayStyle: CSSProperties = {\n position: 'fixed',\n inset: 0,\n zIndex: 'var(--brycks-z-drawer)' as unknown as number,\n display: 'flex',\n backgroundColor: showOverlay ? 'var(--brycks-background-overlay)' : 'transparent',\n animation: 'brycks-drawer-overlay-in 200ms ease-out',\n }\n\n const getPositionStyles = (): CSSProperties => {\n const base: CSSProperties = {\n position: 'absolute',\n backgroundColor: 'var(--brycks-background-elevated)',\n boxShadow: 'var(--brycks-shadow-2xl)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }\n\n if (isHorizontal) {\n return {\n ...base,\n top: 0,\n bottom: 0,\n [placement]: 0,\n width: drawerSize,\n maxWidth: '100vw',\n }\n }\n\n return {\n ...base,\n left: 0,\n right: 0,\n [placement]: 0,\n height: drawerSize,\n maxHeight: '100vh',\n }\n }\n\n const drawerStyle: CSSProperties = {\n ...getPositionStyles(),\n animation: `brycks-drawer-${placement}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,\n outline: 'none',\n ...style,\n }\n\n const headerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '16px 20px',\n borderBottom: '1px solid var(--brycks-border-muted)',\n flexShrink: 0,\n }\n\n const titleStyle: CSSProperties = {\n fontSize: 18,\n fontWeight: 600,\n color: 'var(--brycks-foreground-default)',\n margin: 0,\n }\n\n const closeButtonStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'var(--brycks-radius-md)',\n color: 'var(--brycks-foreground-muted)',\n transition: 'all 150ms ease-out',\n }\n\n const contentStyle: CSSProperties = {\n flex: 1,\n overflow: 'auto',\n padding: 20,\n }\n\n const drawerContent = (\n <>\n <style>\n {`\n @keyframes brycks-drawer-overlay-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes brycks-drawer-left-in {\n from { transform: translateX(-100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-right-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-top-in {\n from { transform: translateY(-100%); }\n to { transform: translateY(0); }\n }\n @keyframes brycks-drawer-bottom-in {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n }\n `}\n </style>\n <div\n className=\"brycks-drawer-overlay\"\n style={overlayStyle}\n onClick={handleOverlayClick}\n aria-hidden=\"true\"\n >\n <div\n ref={(node) => {\n (drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'brycks-drawer-title' : undefined}\n className={cx('brycks-drawer', `brycks-drawer--${placement}`, `brycks-drawer--${size}`, className)}\n style={drawerStyle}\n tabIndex={-1}\n data-testid={testId}\n {...props}\n >\n {(title || showCloseButton) && (\n <div className=\"brycks-drawer-header\" style={headerStyle}>\n {title && <h2 id=\"brycks-drawer-title\" style={titleStyle}>{title}</h2>}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"brycks-drawer-close\"\n style={closeButtonStyle}\n onClick={onClose}\n aria-label=\"Close drawer\"\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\n }}\n >\n <CloseIcon />\n </button>\n )}\n </div>\n )}\n <div className=\"brycks-drawer-content\" style={contentStyle}>\n {children}\n </div>\n </div>\n </div>\n </>\n )\n\n return createPortal(drawerContent, document.body)\n})\n\nDrawer.displayName = 'Drawer'\n"],"names":["sizeMap","CloseIcon","jsx","Drawer","forwardRef","isOpen","onClose","placement","size","title","closeOnOverlayClick","closeOnEscape","showCloseButton","showOverlay","className","style","children","testId","props","ref","drawerRef","useRef","previousActiveElement","onCloseRef","useEffect","drawer","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","isHorizontal","drawerSize","overlayStyle","drawerStyle","base","headerStyle","titleStyle","closeButtonStyle","contentStyle","drawerContent","jsxs","Fragment","node","cx","createPortal"],"mappings":"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"}
@@ -1,70 +1,74 @@
1
- import { jsxs as c, Fragment as Y, jsx as e } from "react/jsx-runtime";
2
- import { forwardRef as K, useRef as g, useEffect as P, useCallback as W } from "react";
3
- import { createPortal as $ } from "react-dom";
4
- import { cx as A } from "../../../utils/styles.js";
5
- const B = {
1
+ import { jsxs as d, Fragment as P, jsx as e } from "react/jsx-runtime";
2
+ import { forwardRef as W, useRef as u, useEffect as f, useCallback as $ } from "react";
3
+ import { createPortal as A } from "react-dom";
4
+ import { cx as B } from "../../../utils/styles.js";
5
+ const H = {
6
6
  sm: "320px",
7
7
  md: "400px",
8
8
  lg: "560px",
9
9
  xl: "720px",
10
10
  full: "100%"
11
11
  };
12
- function H() {
12
+ function q() {
13
13
  return /* @__PURE__ */ e("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ e("path", { d: "M4 4l8 8M12 4l-8 8", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }) });
14
14
  }
15
- const q = K(function({
16
- isOpen: l,
17
- onClose: a,
18
- placement: t = "right",
19
- size: d = "md",
20
- title: n,
21
- closeOnOverlayClick: u = !0,
22
- closeOnEscape: y = !0,
23
- showCloseButton: f = !0,
24
- showOverlay: p = !0,
25
- className: x,
26
- style: S,
27
- children: E,
28
- testId: C,
29
- ...D
30
- }, s) {
31
- const m = g(null), b = g(null);
32
- P(() => {
33
- if (!l) return;
34
- b.current = document.activeElement;
35
- const r = m.current;
15
+ const F = W(function({
16
+ isOpen: a,
17
+ onClose: t,
18
+ placement: o = "right",
19
+ size: y = "md",
20
+ title: s,
21
+ closeOnOverlayClick: m = !0,
22
+ closeOnEscape: b = !0,
23
+ showCloseButton: k = !0,
24
+ showOverlay: S = !0,
25
+ className: E,
26
+ style: C,
27
+ children: D,
28
+ testId: z,
29
+ ...N
30
+ }, l) {
31
+ const c = u(null), w = u(null), h = u(t);
32
+ f(() => {
33
+ h.current = t;
34
+ }, [t]), f(() => {
35
+ if (!a) return;
36
+ w.current = document.activeElement;
37
+ const r = c.current;
36
38
  r && r.focus();
37
- const h = (o) => {
38
- if (o.key === "Escape" && y) {
39
- a();
39
+ }, [a]), f(() => {
40
+ if (!a) return;
41
+ const r = c.current, g = (n) => {
42
+ if (n.key === "Escape" && b) {
43
+ h.current();
40
44
  return;
41
45
  }
42
- if (o.key === "Tab" && r) {
46
+ if (n.key === "Tab" && r) {
43
47
  const i = r.querySelectorAll(
44
48
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
45
- ), v = i[0], w = i[i.length - 1];
46
- o.shiftKey && document.activeElement === v ? (o.preventDefault(), w?.focus()) : !o.shiftKey && document.activeElement === w && (o.preventDefault(), v?.focus());
49
+ ), x = i[0], p = i[i.length - 1];
50
+ n.shiftKey && document.activeElement === x ? (n.preventDefault(), p?.focus()) : !n.shiftKey && document.activeElement === p && (n.preventDefault(), x?.focus());
47
51
  }
48
52
  };
49
- return document.addEventListener("keydown", h), document.body.style.overflow = "hidden", () => {
50
- document.removeEventListener("keydown", h), document.body.style.overflow = "", b.current?.focus();
53
+ return document.addEventListener("keydown", g), document.body.style.overflow = "hidden", () => {
54
+ document.removeEventListener("keydown", g), document.body.style.overflow = "", w.current?.focus();
51
55
  };
52
- }, [l, y, a]);
53
- const z = W(
56
+ }, [a, b]);
57
+ const T = $(
54
58
  (r) => {
55
- r.target === r.currentTarget && u && a();
59
+ r.target === r.currentTarget && m && t();
56
60
  },
57
- [u, a]
61
+ [m, t]
58
62
  );
59
- if (!l) return null;
60
- const N = t === "left" || t === "right", k = B[d], T = {
63
+ if (!a) return null;
64
+ const I = o === "left" || o === "right", v = H[y], M = {
61
65
  position: "fixed",
62
66
  inset: 0,
63
67
  zIndex: "var(--brycks-z-drawer)",
64
68
  display: "flex",
65
- backgroundColor: p ? "var(--brycks-background-overlay)" : "transparent",
69
+ backgroundColor: S ? "var(--brycks-background-overlay)" : "transparent",
66
70
  animation: "brycks-drawer-overlay-in 200ms ease-out"
67
- }, I = {
71
+ }, R = {
68
72
  ...(() => {
69
73
  const r = {
70
74
  position: "absolute",
@@ -74,38 +78,38 @@ const q = K(function({
74
78
  flexDirection: "column",
75
79
  overflow: "hidden"
76
80
  };
77
- return N ? {
81
+ return I ? {
78
82
  ...r,
79
83
  top: 0,
80
84
  bottom: 0,
81
- [t]: 0,
82
- width: k,
85
+ [o]: 0,
86
+ width: v,
83
87
  maxWidth: "100vw"
84
88
  } : {
85
89
  ...r,
86
90
  left: 0,
87
91
  right: 0,
88
- [t]: 0,
89
- height: k,
92
+ [o]: 0,
93
+ height: v,
90
94
  maxHeight: "100vh"
91
95
  };
92
96
  })(),
93
- animation: `brycks-drawer-${t}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,
97
+ animation: `brycks-drawer-${o}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,
94
98
  outline: "none",
95
- ...S
96
- }, M = {
99
+ ...C
100
+ }, j = {
97
101
  display: "flex",
98
102
  alignItems: "center",
99
103
  justifyContent: "space-between",
100
104
  padding: "16px 20px",
101
105
  borderBottom: "1px solid var(--brycks-border-muted)",
102
106
  flexShrink: 0
103
- }, j = {
107
+ }, L = {
104
108
  fontSize: 18,
105
109
  fontWeight: 600,
106
110
  color: "var(--brycks-foreground-default)",
107
111
  margin: 0
108
- }, L = {
112
+ }, X = {
109
113
  display: "flex",
110
114
  alignItems: "center",
111
115
  justifyContent: "center",
@@ -114,11 +118,11 @@ const q = K(function({
114
118
  borderRadius: "var(--brycks-radius-md)",
115
119
  color: "var(--brycks-foreground-muted)",
116
120
  transition: "all 150ms ease-out"
117
- }, R = {
121
+ }, Y = {
118
122
  flex: 1,
119
123
  overflow: "auto",
120
124
  padding: 20
121
- }, X = /* @__PURE__ */ c(Y, { children: [
125
+ }, K = /* @__PURE__ */ d(P, { children: [
122
126
  /* @__PURE__ */ e("style", { children: `
123
127
  @keyframes brycks-drawer-overlay-in {
124
128
  from { opacity: 0; }
@@ -145,33 +149,33 @@ const q = K(function({
145
149
  "div",
146
150
  {
147
151
  className: "brycks-drawer-overlay",
148
- style: T,
149
- onClick: z,
152
+ style: M,
153
+ onClick: T,
150
154
  "aria-hidden": "true",
151
- children: /* @__PURE__ */ c(
155
+ children: /* @__PURE__ */ d(
152
156
  "div",
153
157
  {
154
158
  ref: (r) => {
155
- m.current = r, typeof s == "function" ? s(r) : s && (s.current = r);
159
+ c.current = r, typeof l == "function" ? l(r) : l && (l.current = r);
156
160
  },
157
161
  role: "dialog",
158
162
  "aria-modal": "true",
159
- "aria-labelledby": n ? "brycks-drawer-title" : void 0,
160
- className: A("brycks-drawer", `brycks-drawer--${t}`, `brycks-drawer--${d}`, x),
161
- style: I,
163
+ "aria-labelledby": s ? "brycks-drawer-title" : void 0,
164
+ className: B("brycks-drawer", `brycks-drawer--${o}`, `brycks-drawer--${y}`, E),
165
+ style: R,
162
166
  tabIndex: -1,
163
- "data-testid": C,
164
- ...D,
167
+ "data-testid": z,
168
+ ...N,
165
169
  children: [
166
- (n || f) && /* @__PURE__ */ c("div", { className: "brycks-drawer-header", style: M, children: [
167
- n && /* @__PURE__ */ e("h2", { id: "brycks-drawer-title", style: j, children: n }),
168
- f && /* @__PURE__ */ e(
170
+ (s || k) && /* @__PURE__ */ d("div", { className: "brycks-drawer-header", style: j, children: [
171
+ s && /* @__PURE__ */ e("h2", { id: "brycks-drawer-title", style: L, children: s }),
172
+ k && /* @__PURE__ */ e(
169
173
  "button",
170
174
  {
171
175
  type: "button",
172
176
  className: "brycks-drawer-close",
173
- style: L,
174
- onClick: a,
177
+ style: X,
178
+ onClick: t,
175
179
  "aria-label": "Close drawer",
176
180
  onMouseEnter: (r) => {
177
181
  r.currentTarget.style.backgroundColor = "var(--brycks-background-muted)", r.currentTarget.style.color = "var(--brycks-foreground-default)";
@@ -179,21 +183,21 @@ const q = K(function({
179
183
  onMouseLeave: (r) => {
180
184
  r.currentTarget.style.backgroundColor = "transparent", r.currentTarget.style.color = "var(--brycks-foreground-muted)";
181
185
  },
182
- children: /* @__PURE__ */ e(H, {})
186
+ children: /* @__PURE__ */ e(q, {})
183
187
  }
184
188
  )
185
189
  ] }),
186
- /* @__PURE__ */ e("div", { className: "brycks-drawer-content", style: R, children: E })
190
+ /* @__PURE__ */ e("div", { className: "brycks-drawer-content", style: Y, children: D })
187
191
  ]
188
192
  }
189
193
  )
190
194
  }
191
195
  )
192
196
  ] });
193
- return $(X, document.body);
197
+ return A(K, document.body);
194
198
  });
195
- q.displayName = "Drawer";
199
+ F.displayName = "Drawer";
196
200
  export {
197
- q as Drawer
201
+ F as Drawer
198
202
  };
199
203
  //# sourceMappingURL=Drawer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Drawer.js","sources":["../../../../src/components/feedback/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * Drawer Component\n *\n * A slide-out panel that can appear from any edge of the screen.\n * Supports focus trapping and keyboard navigation.\n */\n\nimport {\n forwardRef,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\n\nexport type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom'\nexport type DrawerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Whether the drawer is open */\n isOpen: boolean\n /** Callback when drawer should close */\n onClose: () => void\n /** Drawer placement */\n placement?: DrawerPlacement\n /** Drawer size */\n size?: DrawerSize\n /** Drawer title */\n title?: ReactNode\n /** Whether to close on overlay click */\n closeOnOverlayClick?: boolean\n /** Whether to close on escape key */\n closeOnEscape?: boolean\n /** Whether to show close button */\n showCloseButton?: boolean\n /** Whether to show overlay */\n showOverlay?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeMap: Record<DrawerSize, string> = {\n sm: '320px',\n md: '400px',\n lg: '560px',\n xl: '720px',\n full: '100%',\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n placement = 'right',\n size = 'md',\n title,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n showCloseButton = true,\n showOverlay = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const drawerRef = useRef<HTMLDivElement>(null)\n const previousActiveElement = useRef<HTMLElement | null>(null)\n\n // Focus trap and keyboard handling\n useEffect(() => {\n if (!isOpen) return\n\n previousActiveElement.current = document.activeElement as HTMLElement\n const drawer = drawerRef.current\n if (drawer) {\n drawer.focus()\n }\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && closeOnEscape) {\n onClose()\n return\n }\n\n if (e.key === 'Tab' && drawer) {\n const focusableElements = drawer.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n const firstElement = focusableElements[0]\n const lastElement = focusableElements[focusableElements.length - 1]\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault()\n lastElement?.focus()\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault()\n firstElement?.focus()\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n document.body.style.overflow = 'hidden'\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown)\n document.body.style.overflow = ''\n previousActiveElement.current?.focus()\n }\n }, [isOpen, closeOnEscape, onClose])\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget && closeOnOverlayClick) {\n onClose()\n }\n },\n [closeOnOverlayClick, onClose]\n )\n\n if (!isOpen) return null\n\n const isHorizontal = placement === 'left' || placement === 'right'\n const drawerSize = sizeMap[size]\n\n const overlayStyle: CSSProperties = {\n position: 'fixed',\n inset: 0,\n zIndex: 'var(--brycks-z-drawer)' as unknown as number,\n display: 'flex',\n backgroundColor: showOverlay ? 'var(--brycks-background-overlay)' : 'transparent',\n animation: 'brycks-drawer-overlay-in 200ms ease-out',\n }\n\n const getPositionStyles = (): CSSProperties => {\n const base: CSSProperties = {\n position: 'absolute',\n backgroundColor: 'var(--brycks-background-elevated)',\n boxShadow: 'var(--brycks-shadow-2xl)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }\n\n if (isHorizontal) {\n return {\n ...base,\n top: 0,\n bottom: 0,\n [placement]: 0,\n width: drawerSize,\n maxWidth: '100vw',\n }\n }\n\n return {\n ...base,\n left: 0,\n right: 0,\n [placement]: 0,\n height: drawerSize,\n maxHeight: '100vh',\n }\n }\n\n const drawerStyle: CSSProperties = {\n ...getPositionStyles(),\n animation: `brycks-drawer-${placement}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,\n outline: 'none',\n ...style,\n }\n\n const headerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '16px 20px',\n borderBottom: '1px solid var(--brycks-border-muted)',\n flexShrink: 0,\n }\n\n const titleStyle: CSSProperties = {\n fontSize: 18,\n fontWeight: 600,\n color: 'var(--brycks-foreground-default)',\n margin: 0,\n }\n\n const closeButtonStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'var(--brycks-radius-md)',\n color: 'var(--brycks-foreground-muted)',\n transition: 'all 150ms ease-out',\n }\n\n const contentStyle: CSSProperties = {\n flex: 1,\n overflow: 'auto',\n padding: 20,\n }\n\n const drawerContent = (\n <>\n <style>\n {`\n @keyframes brycks-drawer-overlay-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes brycks-drawer-left-in {\n from { transform: translateX(-100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-right-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-top-in {\n from { transform: translateY(-100%); }\n to { transform: translateY(0); }\n }\n @keyframes brycks-drawer-bottom-in {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n }\n `}\n </style>\n <div\n className=\"brycks-drawer-overlay\"\n style={overlayStyle}\n onClick={handleOverlayClick}\n aria-hidden=\"true\"\n >\n <div\n ref={(node) => {\n (drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'brycks-drawer-title' : undefined}\n className={cx('brycks-drawer', `brycks-drawer--${placement}`, `brycks-drawer--${size}`, className)}\n style={drawerStyle}\n tabIndex={-1}\n data-testid={testId}\n {...props}\n >\n {(title || showCloseButton) && (\n <div className=\"brycks-drawer-header\" style={headerStyle}>\n {title && <h2 id=\"brycks-drawer-title\" style={titleStyle}>{title}</h2>}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"brycks-drawer-close\"\n style={closeButtonStyle}\n onClick={onClose}\n aria-label=\"Close drawer\"\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\n }}\n >\n <CloseIcon />\n </button>\n )}\n </div>\n )}\n <div className=\"brycks-drawer-content\" style={contentStyle}>\n {children}\n </div>\n </div>\n </div>\n </>\n )\n\n return createPortal(drawerContent, document.body)\n})\n\nDrawer.displayName = 'Drawer'\n"],"names":["sizeMap","CloseIcon","jsx","Drawer","forwardRef","isOpen","onClose","placement","size","title","closeOnOverlayClick","closeOnEscape","showCloseButton","showOverlay","className","style","children","testId","props","ref","drawerRef","useRef","previousActiveElement","useEffect","drawer","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","isHorizontal","drawerSize","overlayStyle","drawerStyle","base","headerStyle","titleStyle","closeButtonStyle","contentStyle","drawerContent","jsxs","Fragment","node","cx","createPortal"],"mappings":"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"}
1
+ {"version":3,"file":"Drawer.js","sources":["../../../../src/components/feedback/Drawer/Drawer.tsx"],"sourcesContent":["/**\n * Drawer Component\n *\n * A slide-out panel that can appear from any edge of the screen.\n * Supports focus trapping and keyboard navigation.\n */\n\nimport {\n forwardRef,\n useEffect,\n useRef,\n useCallback,\n type CSSProperties,\n type ReactNode,\n type HTMLAttributes,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\n\nexport type DrawerPlacement = 'left' | 'right' | 'top' | 'bottom'\nexport type DrawerSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\n\nexport interface DrawerProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\n /** Whether the drawer is open */\n isOpen: boolean\n /** Callback when drawer should close */\n onClose: () => void\n /** Drawer placement */\n placement?: DrawerPlacement\n /** Drawer size */\n size?: DrawerSize\n /** Drawer title */\n title?: ReactNode\n /** Whether to close on overlay click */\n closeOnOverlayClick?: boolean\n /** Whether to close on escape key */\n closeOnEscape?: boolean\n /** Whether to show close button */\n showCloseButton?: boolean\n /** Whether to show overlay */\n showOverlay?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeMap: Record<DrawerSize, string> = {\n sm: '320px',\n md: '400px',\n lg: '560px',\n xl: '720px',\n full: '100%',\n}\n\nfunction CloseIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\">\n <path d=\"M4 4l8 8M12 4l-8 8\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport const Drawer = forwardRef<HTMLDivElement, DrawerProps>(function Drawer(\n {\n isOpen,\n onClose,\n placement = 'right',\n size = 'md',\n title,\n closeOnOverlayClick = true,\n closeOnEscape = true,\n showCloseButton = true,\n showOverlay = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const drawerRef = useRef<HTMLDivElement>(null)\n const previousActiveElement = useRef<HTMLElement | null>(null)\n\n // Store onClose in a ref to avoid re-running effects when it changes\n const onCloseRef = useRef(onClose)\n useEffect(() => {\n onCloseRef.current = onClose\n }, [onClose])\n\n // Focus drawer when it opens (only once)\n useEffect(() => {\n if (!isOpen) return\n\n previousActiveElement.current = document.activeElement as HTMLElement\n const drawer = drawerRef.current\n if (drawer) {\n drawer.focus()\n }\n }, [isOpen])\n\n // Keyboard handling and body scroll lock\n useEffect(() => {\n if (!isOpen) return\n\n const drawer = drawerRef.current\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && closeOnEscape) {\n onCloseRef.current()\n return\n }\n\n if (e.key === 'Tab' && drawer) {\n const focusableElements = drawer.querySelectorAll<HTMLElement>(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\n )\n const firstElement = focusableElements[0]\n const lastElement = focusableElements[focusableElements.length - 1]\n\n if (e.shiftKey && document.activeElement === firstElement) {\n e.preventDefault()\n lastElement?.focus()\n } else if (!e.shiftKey && document.activeElement === lastElement) {\n e.preventDefault()\n firstElement?.focus()\n }\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n document.body.style.overflow = 'hidden'\n\n return () => {\n document.removeEventListener('keydown', handleKeyDown)\n document.body.style.overflow = ''\n previousActiveElement.current?.focus()\n }\n }, [isOpen, closeOnEscape])\n\n const handleOverlayClick = useCallback(\n (e: React.MouseEvent) => {\n if (e.target === e.currentTarget && closeOnOverlayClick) {\n onClose()\n }\n },\n [closeOnOverlayClick, onClose]\n )\n\n if (!isOpen) return null\n\n const isHorizontal = placement === 'left' || placement === 'right'\n const drawerSize = sizeMap[size]\n\n const overlayStyle: CSSProperties = {\n position: 'fixed',\n inset: 0,\n zIndex: 'var(--brycks-z-drawer)' as unknown as number,\n display: 'flex',\n backgroundColor: showOverlay ? 'var(--brycks-background-overlay)' : 'transparent',\n animation: 'brycks-drawer-overlay-in 200ms ease-out',\n }\n\n const getPositionStyles = (): CSSProperties => {\n const base: CSSProperties = {\n position: 'absolute',\n backgroundColor: 'var(--brycks-background-elevated)',\n boxShadow: 'var(--brycks-shadow-2xl)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }\n\n if (isHorizontal) {\n return {\n ...base,\n top: 0,\n bottom: 0,\n [placement]: 0,\n width: drawerSize,\n maxWidth: '100vw',\n }\n }\n\n return {\n ...base,\n left: 0,\n right: 0,\n [placement]: 0,\n height: drawerSize,\n maxHeight: '100vh',\n }\n }\n\n const drawerStyle: CSSProperties = {\n ...getPositionStyles(),\n animation: `brycks-drawer-${placement}-in 250ms cubic-bezier(0.32, 0.72, 0, 1)`,\n outline: 'none',\n ...style,\n }\n\n const headerStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'space-between',\n padding: '16px 20px',\n borderBottom: '1px solid var(--brycks-border-muted)',\n flexShrink: 0,\n }\n\n const titleStyle: CSSProperties = {\n fontSize: 18,\n fontWeight: 600,\n color: 'var(--brycks-foreground-default)',\n margin: 0,\n }\n\n const closeButtonStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: 32,\n height: 32,\n borderRadius: 'var(--brycks-radius-md)',\n color: 'var(--brycks-foreground-muted)',\n transition: 'all 150ms ease-out',\n }\n\n const contentStyle: CSSProperties = {\n flex: 1,\n overflow: 'auto',\n padding: 20,\n }\n\n const drawerContent = (\n <>\n <style>\n {`\n @keyframes brycks-drawer-overlay-in {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n @keyframes brycks-drawer-left-in {\n from { transform: translateX(-100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-right-in {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n @keyframes brycks-drawer-top-in {\n from { transform: translateY(-100%); }\n to { transform: translateY(0); }\n }\n @keyframes brycks-drawer-bottom-in {\n from { transform: translateY(100%); }\n to { transform: translateY(0); }\n }\n `}\n </style>\n <div\n className=\"brycks-drawer-overlay\"\n style={overlayStyle}\n onClick={handleOverlayClick}\n aria-hidden=\"true\"\n >\n <div\n ref={(node) => {\n (drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? 'brycks-drawer-title' : undefined}\n className={cx('brycks-drawer', `brycks-drawer--${placement}`, `brycks-drawer--${size}`, className)}\n style={drawerStyle}\n tabIndex={-1}\n data-testid={testId}\n {...props}\n >\n {(title || showCloseButton) && (\n <div className=\"brycks-drawer-header\" style={headerStyle}>\n {title && <h2 id=\"brycks-drawer-title\" style={titleStyle}>{title}</h2>}\n {showCloseButton && (\n <button\n type=\"button\"\n className=\"brycks-drawer-close\"\n style={closeButtonStyle}\n onClick={onClose}\n aria-label=\"Close drawer\"\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\n }}\n >\n <CloseIcon />\n </button>\n )}\n </div>\n )}\n <div className=\"brycks-drawer-content\" style={contentStyle}>\n {children}\n </div>\n </div>\n </div>\n </>\n )\n\n return createPortal(drawerContent, document.body)\n})\n\nDrawer.displayName = 'Drawer'\n"],"names":["sizeMap","CloseIcon","jsx","Drawer","forwardRef","isOpen","onClose","placement","size","title","closeOnOverlayClick","closeOnEscape","showCloseButton","showOverlay","className","style","children","testId","props","ref","drawerRef","useRef","previousActiveElement","onCloseRef","useEffect","drawer","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","isHorizontal","drawerSize","overlayStyle","drawerStyle","base","headerStyle","titleStyle","closeButtonStyle","contentStyle","drawerContent","jsxs","Fragment","node","cx","createPortal"],"mappings":";;;;AA+CA,MAAMA,IAAsC;AAAA,EAC1C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,MAAM;AACR;AAEA,SAASC,IAAY;AACnB,SACE,gBAAAC,EAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QACnD,4BAAC,QAAA,EAAK,GAAE,sBAAqB,QAAO,gBAAe,aAAY,OAAM,eAAc,SAAQ,EAAA,CAC7F;AAEJ;AAEO,MAAMC,IAASC,EAAwC,SAC5D;AAAA,EACE,QAAAC;AAAA,EACA,SAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,MAAAC,IAAO;AAAA,EACP,OAAAC;AAAA,EACA,qBAAAC,IAAsB;AAAA,EACtB,eAAAC,IAAgB;AAAA,EAChB,iBAAAC,IAAkB;AAAA,EAClB,aAAAC,IAAc;AAAA,EACd,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAMC,IAAYC,EAAuB,IAAI,GACvCC,IAAwBD,EAA2B,IAAI,GAGvDE,IAAaF,EAAOf,CAAO;AACjC,EAAAkB,EAAU,MAAM;AACd,IAAAD,EAAW,UAAUjB;AAAA,EACvB,GAAG,CAACA,CAAO,CAAC,GAGZkB,EAAU,MAAM;AACd,QAAI,CAACnB,EAAQ;AAEb,IAAAiB,EAAsB,UAAU,SAAS;AACzC,UAAMG,IAASL,EAAU;AACzB,IAAIK,KACFA,EAAO,MAAA;AAAA,EAEX,GAAG,CAACpB,CAAM,CAAC,GAGXmB,EAAU,MAAM;AACd,QAAI,CAACnB,EAAQ;AAEb,UAAMoB,IAASL,EAAU,SAEnBM,IAAgB,CAACC,MAAqB;AAC1C,UAAIA,EAAE,QAAQ,YAAYhB,GAAe;AACvC,QAAAY,EAAW,QAAA;AACX;AAAA,MACF;AAEA,UAAII,EAAE,QAAQ,SAASF,GAAQ;AAC7B,cAAMG,IAAoBH,EAAO;AAAA,UAC/B;AAAA,QAAA,GAEII,IAAeD,EAAkB,CAAC,GAClCE,IAAcF,EAAkBA,EAAkB,SAAS,CAAC;AAElE,QAAID,EAAE,YAAY,SAAS,kBAAkBE,KAC3CF,EAAE,eAAA,GACFG,GAAa,MAAA,KACJ,CAACH,EAAE,YAAY,SAAS,kBAAkBG,MACnDH,EAAE,eAAA,GACFE,GAAc,MAAA;AAAA,MAElB;AAAA,IACF;AAEA,oBAAS,iBAAiB,WAAWH,CAAa,GAClD,SAAS,KAAK,MAAM,WAAW,UAExB,MAAM;AACX,eAAS,oBAAoB,WAAWA,CAAa,GACrD,SAAS,KAAK,MAAM,WAAW,IAC/BJ,EAAsB,SAAS,MAAA;AAAA,IACjC;AAAA,EACF,GAAG,CAACjB,GAAQM,CAAa,CAAC;AAE1B,QAAMoB,IAAqBC;AAAA,IACzB,CAACL,MAAwB;AACvB,MAAIA,EAAE,WAAWA,EAAE,iBAAiBjB,KAClCJ,EAAA;AAAA,IAEJ;AAAA,IACA,CAACI,GAAqBJ,CAAO;AAAA,EAAA;AAG/B,MAAI,CAACD,EAAQ,QAAO;AAEpB,QAAM4B,IAAe1B,MAAc,UAAUA,MAAc,SACrD2B,IAAalC,EAAQQ,CAAI,GAEzB2B,IAA8B;AAAA,IAClC,UAAU;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,iBAAiBtB,IAAc,qCAAqC;AAAA,IACpE,WAAW;AAAA,EAAA,GAkCPuB,IAA6B;AAAA,IACjC,IAhCwB,MAAqB;AAC7C,YAAMC,IAAsB;AAAA,QAC1B,UAAU;AAAA,QACV,iBAAiB;AAAA,QACjB,WAAW;AAAA,QACX,SAAS;AAAA,QACT,eAAe;AAAA,QACf,UAAU;AAAA,MAAA;AAGZ,aAAIJ,IACK;AAAA,QACL,GAAGI;AAAA,QACH,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,CAAC9B,CAAS,GAAG;AAAA,QACb,OAAO2B;AAAA,QACP,UAAU;AAAA,MAAA,IAIP;AAAA,QACL,GAAGG;AAAA,QACH,MAAM;AAAA,QACN,OAAO;AAAA,QACP,CAAC9B,CAAS,GAAG;AAAA,QACb,QAAQ2B;AAAA,QACR,WAAW;AAAA,MAAA;AAAA,IAEf,GAGK;AAAA,IACH,WAAW,iBAAiB3B,CAAS;AAAA,IACrC,SAAS;AAAA,IACT,GAAGQ;AAAA,EAAA,GAGCuB,IAA6B;AAAA,IACjC,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,cAAc;AAAA,IACd,YAAY;AAAA,EAAA,GAGRC,IAA4B;AAAA,IAChC,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,EAAA,GAGJC,IAAkC;AAAA,IACtC,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,OAAO;AAAA,IACP,YAAY;AAAA,EAAA,GAGRC,IAA8B;AAAA,IAClC,MAAM;AAAA,IACN,UAAU;AAAA,IACV,SAAS;AAAA,EAAA,GAGLC,IACJ,gBAAAC,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAA1C,EAAC,SAAA,EACE,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAsBH;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAOiC;AAAA,QACP,SAASJ;AAAA,QACT,eAAY;AAAA,QAEZ,UAAA,gBAAAY;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK,CAACE,MAAS;AACZ,cAAAzB,EAA4D,UAAUyB,GACnE,OAAO1B,KAAQ,aAAYA,EAAI0B,CAAI,IAC9B1B,QAAS,UAAU0B;AAAA,YAC9B;AAAA,YACA,MAAK;AAAA,YACL,cAAW;AAAA,YACX,mBAAiBpC,IAAQ,wBAAwB;AAAA,YACjD,WAAWqC,EAAG,iBAAiB,kBAAkBvC,CAAS,IAAI,kBAAkBC,CAAI,IAAIM,CAAS;AAAA,YACjG,OAAOsB;AAAA,YACP,UAAU;AAAA,YACV,eAAanB;AAAA,YACZ,GAAGC;AAAA,YAEF,UAAA;AAAA,eAAAT,KAASG,MACT,gBAAA+B,EAAC,OAAA,EAAI,WAAU,wBAAuB,OAAOL,GAC1C,UAAA;AAAA,gBAAA7B,uBAAU,MAAA,EAAG,IAAG,uBAAsB,OAAO8B,GAAa,UAAA9B,GAAM;AAAA,gBAChEG,KACC,gBAAAV;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,WAAU;AAAA,oBACV,OAAOsC;AAAA,oBACP,SAASlC;AAAA,oBACT,cAAW;AAAA,oBACX,cAAc,CAACqB,MAAM;AACnB,sBAAAA,EAAE,cAAc,MAAM,kBAAkB,kCACxCA,EAAE,cAAc,MAAM,QAAQ;AAAA,oBAChC;AAAA,oBACA,cAAc,CAACA,MAAM;AACnB,sBAAAA,EAAE,cAAc,MAAM,kBAAkB,eACxCA,EAAE,cAAc,MAAM,QAAQ;AAAA,oBAChC;AAAA,oBAEA,4BAAC1B,GAAA,CAAA,CAAU;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACb,GAEJ;AAAA,gCAED,OAAA,EAAI,WAAU,yBAAwB,OAAOwC,GAC3C,UAAAzB,EAAA,CACH;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EACF,GACF;AAGF,SAAO+B,EAAaL,GAAe,SAAS,IAAI;AAClD,CAAC;AAEDvC,EAAO,cAAc;"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),r=require("react"),K=require("react-dom"),z=require("../../../utils/styles.cjs"),A={sm:"400px",md:"500px",lg:"640px",xl:"800px",full:"calc(100vw - 48px)"},B=r.memo(function(){return t.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:t.jsx("path",{d:"M4 4L12 12M12 4L4 12",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})}),p=r.forwardRef(function({isOpen:o,onClose:a,size:l="md",title:s,description:i,closeOnOverlayClick:m=!0,closeOnEscape:f=!0,showCloseButton:b=!0,className:w,style:v,testId:E,children:S,...M},c){const k=r.useRef(null),u=r.useRef(null),d=r.useRef(!1);r.useEffect(()=>{o&&!d.current?(u.current=document.activeElement,d.current=!0):!o&&d.current&&(document.activeElement instanceof HTMLElement&&document.activeElement.blur(),u.current?.focus(),u.current=null,d.current=!1)},[o]),r.useEffect(()=>{if(!o)return;const e=k.current;e&&e.focus();const x=n=>{if(n.key==="Escape"&&f){a();return}if(n.key==="Tab"&&e){const y=e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),g=y[0],h=y[y.length-1];n.shiftKey&&document.activeElement===g?(n.preventDefault(),h?.focus()):!n.shiftKey&&document.activeElement===h&&(n.preventDefault(),g?.focus())}};return document.addEventListener("keydown",x),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",x),document.body.style.overflow=""}},[o,f,a]);const C=r.useCallback(e=>{e.target===e.currentTarget&&m&&a()},[m,a]),R=r.useMemo(()=>({position:"fixed",inset:0,zIndex:"var(--brycks-z-modal)",display:"flex",alignItems:"center",justifyContent:"center",padding:24,backgroundColor:"var(--brycks-background-overlay)",backdropFilter:"blur(4px)",animation:"brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)"}),[]),L=r.useMemo(()=>({position:"relative",width:"100%",maxWidth:A[l],maxHeight:l==="full"?"calc(100vh - 48px)":"85vh",backgroundColor:"var(--brycks-background-elevated)",borderRadius:"var(--brycks-radius-xl)",boxShadow:"var(--brycks-shadow-2xl)",display:"flex",flexDirection:"column",overflow:"hidden",animation:"brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)",outline:"none",...v}),[l,v]);if(!o)return null;const T={display:"flex",alignItems:"flex-start",justifyContent:"space-between",padding:"20px 24px",borderBottom:"1px solid var(--brycks-border-muted)"},I={display:"flex",flexDirection:"column",gap:4},D={fontSize:18,fontWeight:600,color:"var(--brycks-foreground-default)",margin:0,lineHeight:1.3},N={fontSize:14,color:"var(--brycks-foreground-muted)",margin:0},q={display:"flex",alignItems:"center",justifyContent:"center",width:32,height:32,borderRadius:"var(--brycks-radius-md)",color:"var(--brycks-foreground-muted)",transition:"all var(--brycks-duration-fast) var(--brycks-ease-out)",marginLeft:12,flexShrink:0},W={flex:1,overflow:"auto",padding:24},H=t.jsx("div",{className:"brycks-modal-overlay",style:R,onClick:C,children:t.jsxs("div",{ref:e=>{k.current=e,typeof c=="function"?c(e):c&&(c.current=e)},role:"dialog","aria-modal":"true","aria-labelledby":s?"brycks-modal-title":void 0,"aria-describedby":i?"brycks-modal-description":void 0,className:z.cx("brycks-modal",`brycks-modal--${l}`,w),style:L,tabIndex:-1,"data-testid":E,...M,children:[(s||b)&&t.jsxs("div",{className:"brycks-modal-header",style:T,children:[t.jsxs("div",{style:I,children:[s&&t.jsx("h2",{id:"brycks-modal-title",style:D,children:s}),i&&t.jsx("p",{id:"brycks-modal-description",style:N,children:i})]}),b&&t.jsx("button",{type:"button",className:"brycks-modal-close",style:q,onClick:a,"aria-label":"Close modal",onMouseEnter:e=>{e.currentTarget.style.backgroundColor="var(--brycks-background-muted)",e.currentTarget.style.color="var(--brycks-foreground-default)"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="transparent",e.currentTarget.style.color="var(--brycks-foreground-muted)"},children:t.jsx(B,{})})]}),t.jsx("div",{className:"brycks-modal-content",style:W,children:S})]})});return K.createPortal(H,document.body)});p.displayName="Modal";exports.Modal=p;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),t=require("react"),z=require("react-dom"),A=require("../../../utils/styles.cjs"),B={sm:"400px",md:"500px",lg:"640px",xl:"800px",full:"calc(100vw - 48px)"},P=t.memo(function(){return r.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:r.jsx("path",{d:"M4 4L12 12M12 4L4 12",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})}),E=t.forwardRef(function({isOpen:o,onClose:n,size:l="md",title:c,description:i,closeOnOverlayClick:m=!0,closeOnEscape:b=!0,showCloseButton:v=!0,className:w,style:k,testId:S,children:M,...C},s){const u=t.useRef(null),y=t.useRef(null),d=t.useRef(!1);t.useEffect(()=>{o&&!d.current?(y.current=document.activeElement,d.current=!0):!o&&d.current&&(document.activeElement instanceof HTMLElement&&document.activeElement.blur(),y.current?.focus(),y.current=null,d.current=!1)},[o]);const x=t.useRef(n);t.useEffect(()=>{x.current=n},[n]),t.useEffect(()=>{if(!o)return;const e=u.current;e&&e.focus()},[o]),t.useEffect(()=>{if(!o)return;const e=u.current,g=a=>{if(a.key==="Escape"&&b){x.current();return}if(a.key==="Tab"&&e){const f=e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),h=f[0],p=f[f.length-1];a.shiftKey&&document.activeElement===h?(a.preventDefault(),p?.focus()):!a.shiftKey&&document.activeElement===p&&(a.preventDefault(),h?.focus())}};return document.addEventListener("keydown",g),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",g),document.body.style.overflow=""}},[o,b]);const R=t.useCallback(e=>{e.target===e.currentTarget&&m&&n()},[m,n]),L=t.useMemo(()=>({position:"fixed",inset:0,zIndex:"var(--brycks-z-modal)",display:"flex",alignItems:"center",justifyContent:"center",padding:24,backgroundColor:"var(--brycks-background-overlay)",backdropFilter:"blur(4px)",animation:"brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)"}),[]),T=t.useMemo(()=>({position:"relative",width:"100%",maxWidth:B[l],maxHeight:l==="full"?"calc(100vh - 48px)":"85vh",backgroundColor:"var(--brycks-background-elevated)",borderRadius:"var(--brycks-radius-xl)",boxShadow:"var(--brycks-shadow-2xl)",display:"flex",flexDirection:"column",overflow:"hidden",animation:"brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)",outline:"none",...k}),[l,k]);if(!o)return null;const I={display:"flex",alignItems:"flex-start",justifyContent:"space-between",padding:"20px 24px",borderBottom:"1px solid var(--brycks-border-muted)"},D={display:"flex",flexDirection:"column",gap:4},N={fontSize:18,fontWeight:600,color:"var(--brycks-foreground-default)",margin:0,lineHeight:1.3},q={fontSize:14,color:"var(--brycks-foreground-muted)",margin:0},W={display:"flex",alignItems:"center",justifyContent:"center",width:32,height:32,borderRadius:"var(--brycks-radius-md)",color:"var(--brycks-foreground-muted)",transition:"all var(--brycks-duration-fast) var(--brycks-ease-out)",marginLeft:12,flexShrink:0},H={flex:1,overflow:"auto",padding:24},K=r.jsx("div",{className:"brycks-modal-overlay",style:L,onClick:R,children:r.jsxs("div",{ref:e=>{u.current=e,typeof s=="function"?s(e):s&&(s.current=e)},role:"dialog","aria-modal":"true","aria-labelledby":c?"brycks-modal-title":void 0,"aria-describedby":i?"brycks-modal-description":void 0,className:A.cx("brycks-modal",`brycks-modal--${l}`,w),style:T,tabIndex:-1,"data-testid":S,...C,children:[(c||v)&&r.jsxs("div",{className:"brycks-modal-header",style:I,children:[r.jsxs("div",{style:D,children:[c&&r.jsx("h2",{id:"brycks-modal-title",style:N,children:c}),i&&r.jsx("p",{id:"brycks-modal-description",style:q,children:i})]}),v&&r.jsx("button",{type:"button",className:"brycks-modal-close",style:W,onClick:n,"aria-label":"Close modal",onMouseEnter:e=>{e.currentTarget.style.backgroundColor="var(--brycks-background-muted)",e.currentTarget.style.color="var(--brycks-foreground-default)"},onMouseLeave:e=>{e.currentTarget.style.backgroundColor="transparent",e.currentTarget.style.color="var(--brycks-foreground-muted)"},children:r.jsx(P,{})})]}),r.jsx("div",{className:"brycks-modal-content",style:H,children:M})]})});return z.createPortal(K,document.body)});E.displayName="Modal";exports.Modal=E;
2
2
  //# sourceMappingURL=Modal.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.cjs","sources":["../../../../src/components/feedback/Modal/Modal.tsx"],"sourcesContent":["/**\r\n * Modal Component\r\n *\r\n * Accessible modal dialog with smooth Apple-inspired animations.\r\n * Supports focus trapping and keyboard navigation.\r\n */\r\n\r\nimport {\r\n forwardRef,\r\n useEffect,\r\n useRef,\r\n useCallback,\r\n useMemo,\r\n memo,\r\n type CSSProperties,\r\n type ReactNode,\r\n type HTMLAttributes,\r\n} from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { cx } from '../../../utils/styles'\r\n\r\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\r\n\r\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\r\n /** Whether the modal is open */\r\n isOpen: boolean\r\n /** Callback when modal should close */\r\n onClose: () => void\r\n /** Modal size */\r\n size?: ModalSize\r\n /** Modal title */\r\n title?: ReactNode\r\n /** Modal description */\r\n description?: ReactNode\r\n /** Whether to close on overlay click */\r\n closeOnOverlayClick?: boolean\r\n /** Whether to close on escape key */\r\n closeOnEscape?: boolean\r\n /** Whether to show close button */\r\n showCloseButton?: boolean\r\n /** Custom class name */\r\n className?: string\r\n /** Test ID */\r\n testId?: string\r\n /** Modal content */\r\n children?: ReactNode\r\n}\r\n\r\nconst sizeWidths: Record<ModalSize, string> = {\r\n sm: '400px',\r\n md: '500px',\r\n lg: '640px',\r\n xl: '800px',\r\n full: 'calc(100vw - 48px)',\r\n}\r\n\r\n/** Close icon - memoized to prevent re-renders */\r\nconst CloseIcon = memo(function CloseIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\r\n <path\r\n d=\"M4 4L12 12M12 4L4 12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\r\n {\r\n isOpen,\r\n onClose,\r\n size = 'md',\r\n title,\r\n description,\r\n closeOnOverlayClick = true,\r\n closeOnEscape = true,\r\n showCloseButton = true,\r\n className,\r\n style,\r\n testId,\r\n children,\r\n ...props\r\n },\r\n ref\r\n) {\r\n const modalRef = useRef<HTMLDivElement>(null)\r\n const previousActiveElement = useRef<HTMLElement | null>(null)\r\n const wasOpen = useRef(false)\r\n\r\n // Track previous open state to handle focus restoration before unmount\r\n useEffect(() => {\r\n // Modal just opened\r\n if (isOpen && !wasOpen.current) {\r\n previousActiveElement.current = document.activeElement as HTMLElement\r\n wasOpen.current = true\r\n }\r\n // Modal is about to close - restore focus BEFORE unmount\r\n else if (!isOpen && wasOpen.current) {\r\n // Blur any focused element inside modal to prevent aria-hidden warning\r\n if (document.activeElement instanceof HTMLElement) {\r\n document.activeElement.blur()\r\n }\r\n // Restore focus to previous element\r\n previousActiveElement.current?.focus()\r\n previousActiveElement.current = null\r\n wasOpen.current = false\r\n }\r\n }, [isOpen])\r\n\r\n // Focus trap and keyboard handling\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n if (modal) {\r\n modal.focus()\r\n }\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && closeOnEscape) {\r\n onClose()\r\n return\r\n }\r\n\r\n if (e.key === 'Tab' && modal) {\r\n const focusableElements = modal.querySelectorAll<HTMLElement>(\r\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\r\n )\r\n const firstElement = focusableElements[0]\r\n const lastElement = focusableElements[focusableElements.length - 1]\r\n\r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault()\r\n lastElement?.focus()\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault()\r\n firstElement?.focus()\r\n }\r\n }\r\n }\r\n\r\n document.addEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = 'hidden'\r\n\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = ''\r\n }\r\n }, [isOpen, closeOnEscape, onClose])\r\n\r\n const handleOverlayClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget && closeOnOverlayClick) {\r\n onClose()\r\n }\r\n },\r\n [closeOnOverlayClick, onClose]\r\n )\r\n\r\n // Memoize styles to prevent object recreation on every render\r\n const overlayStyle = useMemo<CSSProperties>(() => ({\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 'var(--brycks-z-modal)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: 24,\r\n backgroundColor: 'var(--brycks-background-overlay)',\r\n backdropFilter: 'blur(4px)',\r\n animation: 'brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n }), [])\r\n\r\n const modalStyle = useMemo<CSSProperties>(() => ({\r\n position: 'relative',\r\n width: '100%',\r\n maxWidth: sizeWidths[size],\r\n maxHeight: size === 'full' ? 'calc(100vh - 48px)' : '85vh',\r\n backgroundColor: 'var(--brycks-background-elevated)',\r\n borderRadius: 'var(--brycks-radius-xl)',\r\n boxShadow: 'var(--brycks-shadow-2xl)',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n animation: 'brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)',\r\n outline: 'none',\r\n ...style,\r\n }), [size, style])\r\n\r\n if (!isOpen) return null\r\n\r\n const headerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n padding: '20px 24px',\r\n borderBottom: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const titleContainerStyle: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: 4,\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: 18,\r\n fontWeight: 600,\r\n color: 'var(--brycks-foreground-default)',\r\n margin: 0,\r\n lineHeight: 1.3,\r\n }\r\n\r\n const descriptionStyle: CSSProperties = {\r\n fontSize: 14,\r\n color: 'var(--brycks-foreground-muted)',\r\n margin: 0,\r\n }\r\n\r\n const closeButtonStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: 32,\r\n height: 32,\r\n borderRadius: 'var(--brycks-radius-md)',\r\n color: 'var(--brycks-foreground-muted)',\r\n transition: 'all var(--brycks-duration-fast) var(--brycks-ease-out)',\r\n marginLeft: 12,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: 24,\r\n }\r\n\r\n const modalContent = (\r\n <div\r\n className=\"brycks-modal-overlay\"\r\n style={overlayStyle}\r\n onClick={handleOverlayClick}\r\n >\r\n <div\r\n ref={(node) => {\r\n (modalRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n if (typeof ref === 'function') ref(node)\r\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-labelledby={title ? 'brycks-modal-title' : undefined}\r\n aria-describedby={description ? 'brycks-modal-description' : undefined}\r\n className={cx('brycks-modal', `brycks-modal--${size}`, className)}\r\n style={modalStyle}\r\n tabIndex={-1}\r\n data-testid={testId}\r\n {...props}\r\n >\r\n {(title || showCloseButton) && (\r\n <div className=\"brycks-modal-header\" style={headerStyle}>\r\n <div style={titleContainerStyle}>\r\n {title && (\r\n <h2 id=\"brycks-modal-title\" style={titleStyle}>\r\n {title}\r\n </h2>\r\n )}\r\n {description && (\r\n <p id=\"brycks-modal-description\" style={descriptionStyle}>\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"brycks-modal-close\"\r\n style={closeButtonStyle}\r\n onClick={onClose}\r\n aria-label=\"Close modal\"\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = 'transparent'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\r\n }}\r\n >\r\n <CloseIcon />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"brycks-modal-content\" style={contentStyle}>\r\n {children}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n\r\n return createPortal(modalContent, document.body)\r\n})\r\n\r\nModal.displayName = 'Modal'\r\n"],"names":["sizeWidths","CloseIcon","memo","jsx","Modal","forwardRef","isOpen","onClose","size","title","description","closeOnOverlayClick","closeOnEscape","showCloseButton","className","style","testId","children","props","ref","modalRef","useRef","previousActiveElement","wasOpen","useEffect","modal","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","overlayStyle","useMemo","modalStyle","headerStyle","titleContainerStyle","titleStyle","descriptionStyle","closeButtonStyle","contentStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
1
+ {"version":3,"file":"Modal.cjs","sources":["../../../../src/components/feedback/Modal/Modal.tsx"],"sourcesContent":["/**\r\n * Modal Component\r\n *\r\n * Accessible modal dialog with smooth Apple-inspired animations.\r\n * Supports focus trapping and keyboard navigation.\r\n */\r\n\r\nimport {\r\n forwardRef,\r\n useEffect,\r\n useRef,\r\n useCallback,\r\n useMemo,\r\n memo,\r\n type CSSProperties,\r\n type ReactNode,\r\n type HTMLAttributes,\r\n} from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { cx } from '../../../utils/styles'\r\n\r\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'full'\r\n\r\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\r\n /** Whether the modal is open */\r\n isOpen: boolean\r\n /** Callback when modal should close */\r\n onClose: () => void\r\n /** Modal size */\r\n size?: ModalSize\r\n /** Modal title */\r\n title?: ReactNode\r\n /** Modal description */\r\n description?: ReactNode\r\n /** Whether to close on overlay click */\r\n closeOnOverlayClick?: boolean\r\n /** Whether to close on escape key */\r\n closeOnEscape?: boolean\r\n /** Whether to show close button */\r\n showCloseButton?: boolean\r\n /** Custom class name */\r\n className?: string\r\n /** Test ID */\r\n testId?: string\r\n /** Modal content */\r\n children?: ReactNode\r\n}\r\n\r\nconst sizeWidths: Record<ModalSize, string> = {\r\n sm: '400px',\r\n md: '500px',\r\n lg: '640px',\r\n xl: '800px',\r\n full: 'calc(100vw - 48px)',\r\n}\r\n\r\n/** Close icon - memoized to prevent re-renders */\r\nconst CloseIcon = memo(function CloseIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\r\n <path\r\n d=\"M4 4L12 12M12 4L4 12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\r\n {\r\n isOpen,\r\n onClose,\r\n size = 'md',\r\n title,\r\n description,\r\n closeOnOverlayClick = true,\r\n closeOnEscape = true,\r\n showCloseButton = true,\r\n className,\r\n style,\r\n testId,\r\n children,\r\n ...props\r\n },\r\n ref\r\n) {\r\n const modalRef = useRef<HTMLDivElement>(null)\r\n const previousActiveElement = useRef<HTMLElement | null>(null)\r\n const wasOpen = useRef(false)\r\n\r\n // Track previous open state to handle focus restoration before unmount\r\n useEffect(() => {\r\n // Modal just opened\r\n if (isOpen && !wasOpen.current) {\r\n previousActiveElement.current = document.activeElement as HTMLElement\r\n wasOpen.current = true\r\n }\r\n // Modal is about to close - restore focus BEFORE unmount\r\n else if (!isOpen && wasOpen.current) {\r\n // Blur any focused element inside modal to prevent aria-hidden warning\r\n if (document.activeElement instanceof HTMLElement) {\r\n document.activeElement.blur()\r\n }\r\n // Restore focus to previous element\r\n previousActiveElement.current?.focus()\r\n previousActiveElement.current = null\r\n wasOpen.current = false\r\n }\r\n }, [isOpen])\r\n\r\n // Store onClose in a ref to avoid re-running effects when it changes\r\n const onCloseRef = useRef(onClose)\r\n useEffect(() => {\r\n onCloseRef.current = onClose\r\n }, [onClose])\r\n\r\n // Focus modal when it opens (only once)\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n if (modal) {\r\n modal.focus()\r\n }\r\n }, [isOpen])\r\n\r\n // Keyboard handling and body scroll lock\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && closeOnEscape) {\r\n onCloseRef.current()\r\n return\r\n }\r\n\r\n if (e.key === 'Tab' && modal) {\r\n const focusableElements = modal.querySelectorAll<HTMLElement>(\r\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\r\n )\r\n const firstElement = focusableElements[0]\r\n const lastElement = focusableElements[focusableElements.length - 1]\r\n\r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault()\r\n lastElement?.focus()\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault()\r\n firstElement?.focus()\r\n }\r\n }\r\n }\r\n\r\n document.addEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = 'hidden'\r\n\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = ''\r\n }\r\n }, [isOpen, closeOnEscape])\r\n\r\n const handleOverlayClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget && closeOnOverlayClick) {\r\n onClose()\r\n }\r\n },\r\n [closeOnOverlayClick, onClose]\r\n )\r\n\r\n // Memoize styles to prevent object recreation on every render\r\n const overlayStyle = useMemo<CSSProperties>(() => ({\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 'var(--brycks-z-modal)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: 24,\r\n backgroundColor: 'var(--brycks-background-overlay)',\r\n backdropFilter: 'blur(4px)',\r\n animation: 'brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n }), [])\r\n\r\n const modalStyle = useMemo<CSSProperties>(() => ({\r\n position: 'relative',\r\n width: '100%',\r\n maxWidth: sizeWidths[size],\r\n maxHeight: size === 'full' ? 'calc(100vh - 48px)' : '85vh',\r\n backgroundColor: 'var(--brycks-background-elevated)',\r\n borderRadius: 'var(--brycks-radius-xl)',\r\n boxShadow: 'var(--brycks-shadow-2xl)',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n animation: 'brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)',\r\n outline: 'none',\r\n ...style,\r\n }), [size, style])\r\n\r\n if (!isOpen) return null\r\n\r\n const headerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n padding: '20px 24px',\r\n borderBottom: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const titleContainerStyle: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: 4,\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: 18,\r\n fontWeight: 600,\r\n color: 'var(--brycks-foreground-default)',\r\n margin: 0,\r\n lineHeight: 1.3,\r\n }\r\n\r\n const descriptionStyle: CSSProperties = {\r\n fontSize: 14,\r\n color: 'var(--brycks-foreground-muted)',\r\n margin: 0,\r\n }\r\n\r\n const closeButtonStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: 32,\r\n height: 32,\r\n borderRadius: 'var(--brycks-radius-md)',\r\n color: 'var(--brycks-foreground-muted)',\r\n transition: 'all var(--brycks-duration-fast) var(--brycks-ease-out)',\r\n marginLeft: 12,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: 24,\r\n }\r\n\r\n const modalContent = (\r\n <div\r\n className=\"brycks-modal-overlay\"\r\n style={overlayStyle}\r\n onClick={handleOverlayClick}\r\n >\r\n <div\r\n ref={(node) => {\r\n (modalRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n if (typeof ref === 'function') ref(node)\r\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-labelledby={title ? 'brycks-modal-title' : undefined}\r\n aria-describedby={description ? 'brycks-modal-description' : undefined}\r\n className={cx('brycks-modal', `brycks-modal--${size}`, className)}\r\n style={modalStyle}\r\n tabIndex={-1}\r\n data-testid={testId}\r\n {...props}\r\n >\r\n {(title || showCloseButton) && (\r\n <div className=\"brycks-modal-header\" style={headerStyle}>\r\n <div style={titleContainerStyle}>\r\n {title && (\r\n <h2 id=\"brycks-modal-title\" style={titleStyle}>\r\n {title}\r\n </h2>\r\n )}\r\n {description && (\r\n <p id=\"brycks-modal-description\" style={descriptionStyle}>\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"brycks-modal-close\"\r\n style={closeButtonStyle}\r\n onClick={onClose}\r\n aria-label=\"Close modal\"\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = 'transparent'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\r\n }}\r\n >\r\n <CloseIcon />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"brycks-modal-content\" style={contentStyle}>\r\n {children}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n\r\n return createPortal(modalContent, document.body)\r\n})\r\n\r\nModal.displayName = 'Modal'\r\n"],"names":["sizeWidths","CloseIcon","memo","jsx","Modal","forwardRef","isOpen","onClose","size","title","description","closeOnOverlayClick","closeOnEscape","showCloseButton","className","style","testId","children","props","ref","modalRef","useRef","previousActiveElement","wasOpen","useEffect","onCloseRef","modal","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","overlayStyle","useMemo","modalStyle","headerStyle","titleContainerStyle","titleStyle","descriptionStyle","closeButtonStyle","contentStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}