@brycks/core-front 0.3.1 → 0.3.2

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 (109) hide show
  1. package/dist/components/data/List/List.cjs +1 -1
  2. package/dist/components/data/List/List.cjs.map +1 -1
  3. package/dist/components/data/List/List.js +92 -88
  4. package/dist/components/data/List/List.js.map +1 -1
  5. package/dist/components/data/Table/Table.cjs +1 -1
  6. package/dist/components/data/Table/Table.cjs.map +1 -1
  7. package/dist/components/data/Table/Table.js +129 -125
  8. package/dist/components/data/Table/Table.js.map +1 -1
  9. package/dist/components/data/TreeView/TreeView.cjs +2 -0
  10. package/dist/components/data/TreeView/TreeView.cjs.map +1 -0
  11. package/dist/components/data/TreeView/TreeView.js +256 -0
  12. package/dist/components/data/TreeView/TreeView.js.map +1 -0
  13. package/dist/components/data/VirtualList/VirtualList.cjs +2 -0
  14. package/dist/components/data/VirtualList/VirtualList.cjs.map +1 -0
  15. package/dist/components/data/VirtualList/VirtualList.js +186 -0
  16. package/dist/components/data/VirtualList/VirtualList.js.map +1 -0
  17. package/dist/components/data.cjs +1 -1
  18. package/dist/components/data.js +21 -16
  19. package/dist/components/data.js.map +1 -1
  20. package/dist/components/feedback/Modal/Modal.cjs +1 -1
  21. package/dist/components/feedback/Modal/Modal.cjs.map +1 -1
  22. package/dist/components/feedback/Modal/Modal.js +81 -77
  23. package/dist/components/feedback/Modal/Modal.js.map +1 -1
  24. package/dist/components/form/Combobox/Combobox.cjs +7 -0
  25. package/dist/components/form/Combobox/Combobox.cjs.map +1 -0
  26. package/dist/components/form/Combobox/Combobox.js +338 -0
  27. package/dist/components/form/Combobox/Combobox.js.map +1 -0
  28. package/dist/components/form/DateRangePicker/DateRangePicker.cjs +2 -0
  29. package/dist/components/form/DateRangePicker/DateRangePicker.cjs.map +1 -0
  30. package/dist/components/form/DateRangePicker/DateRangePicker.js +372 -0
  31. package/dist/components/form/DateRangePicker/DateRangePicker.js.map +1 -0
  32. package/dist/components/form/MultiSelect/MultiSelect.cjs +2 -0
  33. package/dist/components/form/MultiSelect/MultiSelect.cjs.map +1 -0
  34. package/dist/components/form/MultiSelect/MultiSelect.js +393 -0
  35. package/dist/components/form/MultiSelect/MultiSelect.js.map +1 -0
  36. package/dist/components/form/Rating/Rating.cjs +2 -0
  37. package/dist/components/form/Rating/Rating.cjs.map +1 -0
  38. package/dist/components/form/Rating/Rating.js +163 -0
  39. package/dist/components/form/Rating/Rating.js.map +1 -0
  40. package/dist/components/form/Slider/Slider.cjs +1 -1
  41. package/dist/components/form/Slider/Slider.cjs.map +1 -1
  42. package/dist/components/form/Slider/Slider.js +120 -85
  43. package/dist/components/form/Slider/Slider.js.map +1 -1
  44. package/dist/components/form/TagInput/TagInput.cjs +2 -0
  45. package/dist/components/form/TagInput/TagInput.cjs.map +1 -0
  46. package/dist/components/form/TagInput/TagInput.js +286 -0
  47. package/dist/components/form/TagInput/TagInput.js.map +1 -0
  48. package/dist/components/form/TimePicker/TimePicker.cjs +2 -0
  49. package/dist/components/form/TimePicker/TimePicker.cjs.map +1 -0
  50. package/dist/components/form/TimePicker/TimePicker.js +328 -0
  51. package/dist/components/form/TimePicker/TimePicker.js.map +1 -0
  52. package/dist/components/form.cjs +1 -1
  53. package/dist/components/form.js +34 -22
  54. package/dist/components/form.js.map +1 -1
  55. package/dist/components/layout/Card/Card.cjs +1 -1
  56. package/dist/components/layout/Card/Card.cjs.map +1 -1
  57. package/dist/components/layout/Card/Card.js +62 -59
  58. package/dist/components/layout/Card/Card.js.map +1 -1
  59. package/dist/components/layout/Collapse/Collapse.cjs +2 -0
  60. package/dist/components/layout/Collapse/Collapse.cjs.map +1 -0
  61. package/dist/components/layout/Collapse/Collapse.js +140 -0
  62. package/dist/components/layout/Collapse/Collapse.js.map +1 -0
  63. package/dist/components/layout.cjs +1 -1
  64. package/dist/components/layout.js +27 -24
  65. package/dist/components/layout.js.map +1 -1
  66. package/dist/components/navigation/Breadcrumb/Breadcrumb.cjs +1 -1
  67. package/dist/components/navigation/Breadcrumb/Breadcrumb.cjs.map +1 -1
  68. package/dist/components/navigation/Breadcrumb/Breadcrumb.js +66 -62
  69. package/dist/components/navigation/Breadcrumb/Breadcrumb.js.map +1 -1
  70. package/dist/components/navigation/ContextMenu/ContextMenu.cjs +2 -0
  71. package/dist/components/navigation/ContextMenu/ContextMenu.cjs.map +1 -0
  72. package/dist/components/navigation/ContextMenu/ContextMenu.js +227 -0
  73. package/dist/components/navigation/ContextMenu/ContextMenu.js.map +1 -0
  74. package/dist/components/navigation/Dropdown/Dropdown.cjs +2 -2
  75. package/dist/components/navigation/Dropdown/Dropdown.cjs.map +1 -1
  76. package/dist/components/navigation/Dropdown/Dropdown.js +84 -80
  77. package/dist/components/navigation/Dropdown/Dropdown.js.map +1 -1
  78. package/dist/components/navigation/Menu/Menu.cjs +1 -1
  79. package/dist/components/navigation/Menu/Menu.cjs.map +1 -1
  80. package/dist/components/navigation/Menu/Menu.js +132 -94
  81. package/dist/components/navigation/Menu/Menu.js.map +1 -1
  82. package/dist/components/navigation/Pagination/Pagination.cjs +1 -1
  83. package/dist/components/navigation/Pagination/Pagination.cjs.map +1 -1
  84. package/dist/components/navigation/Pagination/Pagination.js +111 -107
  85. package/dist/components/navigation/Pagination/Pagination.js.map +1 -1
  86. package/dist/components/navigation/Stepper/Stepper.cjs +2 -0
  87. package/dist/components/navigation/Stepper/Stepper.cjs.map +1 -0
  88. package/dist/components/navigation/Stepper/Stepper.js +187 -0
  89. package/dist/components/navigation/Stepper/Stepper.js.map +1 -0
  90. package/dist/components/navigation.cjs +1 -1
  91. package/dist/components/navigation.js +27 -21
  92. package/dist/components/navigation.js.map +1 -1
  93. package/dist/components/utility/Badge/Badge.cjs +1 -1
  94. package/dist/components/utility/Badge/Badge.cjs.map +1 -1
  95. package/dist/components/utility/Badge/Badge.js +38 -35
  96. package/dist/components/utility/Badge/Badge.js.map +1 -1
  97. package/dist/data.d.ts +116 -0
  98. package/dist/form.d.ts +316 -0
  99. package/dist/hooks/useInteractionState.cjs +2 -0
  100. package/dist/hooks/useInteractionState.cjs.map +1 -0
  101. package/dist/hooks/useInteractionState.js +67 -0
  102. package/dist/hooks/useInteractionState.js.map +1 -0
  103. package/dist/hooks.cjs +1 -1
  104. package/dist/hooks.d.ts +87 -0
  105. package/dist/hooks.js +16 -14
  106. package/dist/hooks.js.map +1 -1
  107. package/dist/layout.d.ts +44 -0
  108. package/dist/navigation.d.ts +88 -0
  109. package/package.json +1 -1
@@ -1,2 +1,2 @@
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;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react/jsx-runtime"),t=require("react"),A=require("react-dom"),P=require("../../../utils/styles.cjs"),E=require("../../../design-system/primitives/layout.cjs"),s=require("../../../design-system/tokens/spacing.cjs"),j=require("../../../design-system/tokens/typography.cjs"),F=require("../../../design-system/primitives/sizing.cjs"),G={sm:"400px",md:"500px",lg:"640px",xl:"800px",full:"calc(100vw - 48px)"},J=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"})})}),w=t.forwardRef(function({isOpen:n,onClose:o,size:c="md",title:l,description:u,closeOnOverlayClick:b=!0,closeOnEscape:v=!0,showCloseButton:k=!0,className:C,style:g,testId:R,children:q,...L},i){const y=t.useRef(null),f=t.useRef(null),d=t.useRef(!1);t.useEffect(()=>{n&&!d.current?(f.current=document.activeElement,d.current=!0):!n&&d.current&&(document.activeElement instanceof HTMLElement&&document.activeElement.blur(),f.current?.focus(),f.current=null,d.current=!1)},[n]);const x=t.useRef(o);t.useEffect(()=>{x.current=o},[o]),t.useEffect(()=>{if(!n)return;const e=y.current;e&&e.focus()},[n]),t.useEffect(()=>{if(!n)return;const e=y.current,p=a=>{if(a.key==="Escape"&&v){x.current();return}if(a.key==="Tab"&&e){const m=e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),h=m[0],S=m[m.length-1];a.shiftKey&&document.activeElement===h?(a.preventDefault(),S?.focus()):!a.shiftKey&&document.activeElement===S&&(a.preventDefault(),h?.focus())}};return document.addEventListener("keydown",p),document.body.style.overflow="hidden",()=>{document.removeEventListener("keydown",p),document.body.style.overflow=""}},[n,v]);const T=t.useCallback(e=>{e.target===e.currentTarget&&b&&o()},[b,o]),z=t.useMemo(()=>({position:"fixed",inset:0,zIndex:"var(--brycks-z-modal)",display:"flex",alignItems:"center",justifyContent:"center",padding:s.spacing[6],backgroundColor:"var(--brycks-background-overlay)",backdropFilter:"blur(4px)",animation:"brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)"}),[]),I=t.useMemo(()=>({position:"relative",width:"100%",maxWidth:G[c],maxHeight:c==="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",...g}),[c,g]);if(!n)return null;const D={display:"flex",alignItems:"flex-start",justifyContent:"space-between",padding:`${s.spacing[5]}px ${s.spacing[6]}px`,borderBottom:"1px solid var(--brycks-border-muted)"},N={display:"flex",flexDirection:"column",gap:s.spacing[1]},B={fontSize:j.fontSizes.lg,fontWeight:600,color:"var(--brycks-foreground-default)",margin:0,lineHeight:1.3},W={fontSize:j.fontSizes.base,color:"var(--brycks-foreground-muted)",margin:0},H={display:"flex",alignItems:"center",justifyContent:"center",width:E.iconButtonSizes.sm,height:E.iconButtonSizes.sm,borderRadius:"var(--brycks-radius-md)",color:"var(--brycks-foreground-muted)",transition:"all var(--brycks-duration-fast) var(--brycks-ease-out)",marginLeft:F.componentGap.xl,flexShrink:0},K={flex:1,overflow:"auto",padding:s.spacing[6]},$=r.jsx("div",{className:"brycks-modal-overlay",style:z,onClick:T,children:r.jsxs("div",{ref:e=>{y.current=e,typeof i=="function"?i(e):i&&(i.current=e)},role:"dialog","aria-modal":"true","aria-labelledby":l?"brycks-modal-title":void 0,"aria-describedby":u?"brycks-modal-description":void 0,className:P.cx("brycks-modal",`brycks-modal--${c}`,C),style:I,tabIndex:-1,"data-testid":R,...L,children:[(l||k)&&r.jsxs("div",{className:"brycks-modal-header",style:D,children:[r.jsxs("div",{style:N,children:[l&&r.jsx("h2",{id:"brycks-modal-title",style:B,children:l}),u&&r.jsx("p",{id:"brycks-modal-description",style:W,children:u})]}),k&&r.jsx("button",{type:"button",className:"brycks-modal-close",style:H,onClick:o,"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(J,{})})]}),r.jsx("div",{className:"brycks-modal-content",style:K,children:q})]})});return A.createPortal($,document.body)});w.displayName="Modal";exports.Modal=w;
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 // 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":"sMAgDMA,EAAwC,CAC5C,GAAI,QACJ,GAAI,QACJ,GAAI,QACJ,GAAI,QACJ,KAAM,oBACR,EAGMC,EAAYC,EAAAA,KAAK,UAAqB,CAC1C,OACEC,EAAAA,IAAC,MAAA,CAAI,MAAM,KAAK,OAAO,KAAK,QAAQ,YAAY,KAAK,OAAO,cAAY,OACtE,SAAAA,EAAAA,IAAC,OAAA,CACC,EAAE,uBACF,OAAO,eACP,YAAY,MACZ,cAAc,OAAA,CAAA,EAElB,CAEJ,CAAC,EAEYC,EAAQC,EAAAA,WAAuC,SAC1D,CACE,OAAAC,EACA,QAAAC,EACA,KAAAC,EAAO,KACP,MAAAC,EACA,YAAAC,EACA,oBAAAC,EAAsB,GACtB,cAAAC,EAAgB,GAChB,gBAAAC,EAAkB,GAClB,UAAAC,EACA,MAAAC,EACA,OAAAC,EACA,SAAAC,EACA,GAAGC,CACL,EACAC,EACA,CACA,MAAMC,EAAWC,EAAAA,OAAuB,IAAI,EACtCC,EAAwBD,EAAAA,OAA2B,IAAI,EACvDE,EAAUF,EAAAA,OAAO,EAAK,EAG5BG,EAAAA,UAAU,IAAM,CAEVlB,GAAU,CAACiB,EAAQ,SACrBD,EAAsB,QAAU,SAAS,cACzCC,EAAQ,QAAU,IAGX,CAACjB,GAAUiB,EAAQ,UAEtB,SAAS,yBAAyB,aACpC,SAAS,cAAc,KAAA,EAGzBD,EAAsB,SAAS,MAAA,EAC/BA,EAAsB,QAAU,KAChCC,EAAQ,QAAU,GAEtB,EAAG,CAACjB,CAAM,CAAC,EAGX,MAAMmB,EAAaJ,EAAAA,OAAOd,CAAO,EACjCiB,EAAAA,UAAU,IAAM,CACdC,EAAW,QAAUlB,CACvB,EAAG,CAACA,CAAO,CAAC,EAGZiB,EAAAA,UAAU,IAAM,CACd,GAAI,CAAClB,EAAQ,OAEb,MAAMoB,EAAQN,EAAS,QACnBM,GACFA,EAAM,MAAA,CAEV,EAAG,CAACpB,CAAM,CAAC,EAGXkB,EAAAA,UAAU,IAAM,CACd,GAAI,CAAClB,EAAQ,OAEb,MAAMoB,EAAQN,EAAS,QAEjBO,EAAiBC,GAAqB,CAC1C,GAAIA,EAAE,MAAQ,UAAYhB,EAAe,CACvCa,EAAW,QAAA,EACX,MACF,CAEA,GAAIG,EAAE,MAAQ,OAASF,EAAO,CAC5B,MAAMG,EAAoBH,EAAM,iBAC9B,0EAAA,EAEII,EAAeD,EAAkB,CAAC,EAClCE,EAAcF,EAAkBA,EAAkB,OAAS,CAAC,EAE9DD,EAAE,UAAY,SAAS,gBAAkBE,GAC3CF,EAAE,eAAA,EACFG,GAAa,MAAA,GACJ,CAACH,EAAE,UAAY,SAAS,gBAAkBG,IACnDH,EAAE,eAAA,EACFE,GAAc,MAAA,EAElB,CACF,EAEA,gBAAS,iBAAiB,UAAWH,CAAa,EAClD,SAAS,KAAK,MAAM,SAAW,SAExB,IAAM,CACX,SAAS,oBAAoB,UAAWA,CAAa,EACrD,SAAS,KAAK,MAAM,SAAW,EACjC,CACF,EAAG,CAACrB,EAAQM,CAAa,CAAC,EAE1B,MAAMoB,EAAqBC,EAAAA,YACxB,GAAwB,CACnB,EAAE,SAAW,EAAE,eAAiBtB,GAClCJ,EAAA,CAEJ,EACA,CAACI,EAAqBJ,CAAO,CAAA,EAIzB2B,EAAeC,EAAAA,QAAuB,KAAO,CACjD,SAAU,QACV,MAAO,EACP,OAAQ,wBACR,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,QAAS,GACT,gBAAiB,mCACjB,eAAgB,YAChB,UAAW,qEAAA,GACT,CAAA,CAAE,EAEAC,EAAaD,EAAAA,QAAuB,KAAO,CAC/C,SAAU,WACV,MAAO,OACP,SAAUnC,EAAWQ,CAAI,EACzB,UAAWA,IAAS,OAAS,qBAAuB,OACpD,gBAAiB,oCACjB,aAAc,0BACd,UAAW,2BACX,QAAS,OACT,cAAe,SACf,SAAU,SACV,UAAW,0EACX,QAAS,OACT,GAAGO,CAAA,GACD,CAACP,EAAMO,CAAK,CAAC,EAEjB,GAAI,CAACT,EAAQ,OAAO,KAEpB,MAAM+B,EAA6B,CACjC,QAAS,OACT,WAAY,aACZ,eAAgB,gBAChB,QAAS,YACT,aAAc,sCAAA,EAGVC,EAAqC,CACzC,QAAS,OACT,cAAe,SACf,IAAK,CAAA,EAGDC,EAA4B,CAChC,SAAU,GACV,WAAY,IACZ,MAAO,mCACP,OAAQ,EACR,WAAY,GAAA,EAGRC,EAAkC,CACtC,SAAU,GACV,MAAO,iCACP,OAAQ,CAAA,EAGJC,EAAkC,CACtC,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,MAAO,GACP,OAAQ,GACR,aAAc,0BACd,MAAO,iCACP,WAAY,yDACZ,WAAY,GACZ,WAAY,CAAA,EAGRC,EAA8B,CAClC,KAAM,EACN,SAAU,OACV,QAAS,EAAA,EAGLC,EACJxC,EAAAA,IAAC,MAAA,CACG,UAAU,uBACV,MAAO+B,EACP,QAASF,EAET,SAAAY,EAAAA,KAAC,MAAA,CACC,IAAMC,GAAS,CACZzB,EAA2D,QAAUyB,EAClE,OAAO1B,GAAQ,WAAYA,EAAI0B,CAAI,EAC9B1B,IAAMA,EAAsD,QAAU0B,EACjF,EACA,KAAK,SACL,aAAW,OACX,kBAAiBpC,EAAQ,qBAAuB,OAChD,mBAAkBC,EAAc,2BAA6B,OAC7D,UAAWoC,EAAAA,GAAG,eAAgB,iBAAiBtC,CAAI,GAAIM,CAAS,EAChE,MAAOsB,EACP,SAAU,GACV,cAAapB,EACZ,GAAGE,EAEF,SAAA,EAAAT,GAASI,IACT+B,EAAAA,KAAC,MAAA,CAAI,UAAU,sBAAsB,MAAOP,EAC1C,SAAA,CAAAO,EAAAA,KAAC,MAAA,CAAI,MAAON,EACT,SAAA,CAAA7B,SACE,KAAA,CAAG,GAAG,qBAAqB,MAAO8B,EAChC,SAAA9B,EACH,EAEDC,GACCP,EAAAA,IAAC,IAAA,CAAE,GAAG,2BAA2B,MAAOqC,EACrC,SAAA9B,CAAA,CACH,CAAA,EAEJ,EACCG,GACCV,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAU,qBACV,MAAOsC,EACP,QAASlC,EACT,aAAW,cACX,aAAe,GAAM,CACnB,EAAE,cAAc,MAAM,gBAAkB,iCACxC,EAAE,cAAc,MAAM,MAAQ,kCAChC,EACA,aAAe,GAAM,CACnB,EAAE,cAAc,MAAM,gBAAkB,cACxC,EAAE,cAAc,MAAM,MAAQ,gCAChC,EAEA,eAACN,EAAA,CAAA,CAAU,CAAA,CAAA,CACb,EAEJ,QAGD,MAAA,CAAI,UAAU,uBAAuB,MAAOyC,EAC1C,SAAAzB,CAAA,CACH,CAAA,CAAA,CAAA,CACF,CAAA,EAIN,OAAO8B,eAAaJ,EAAc,SAAS,IAAI,CACjD,CAAC,EAEDvC,EAAM,YAAc"}
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\nimport { spacing, fontSizes } from '../../../design-system'\r\nimport { componentGap, iconButtonSizes } from '../../../design-system/primitives'\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: spacing[6],\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: `${spacing[5]}px ${spacing[6]}px`,\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: spacing[1],\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: fontSizes.lg,\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: fontSizes.base,\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: iconButtonSizes.sm,\r\n height: iconButtonSizes.sm,\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: componentGap.xl,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: spacing[6],\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","spacing","modalStyle","headerStyle","titleContainerStyle","titleStyle","fontSizes","descriptionStyle","closeButtonStyle","iconButtonSizes","componentGap","contentStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
@@ -1,14 +1,18 @@
1
- import { jsx as r, jsxs as b } from "react/jsx-runtime";
2
- import { forwardRef as q, useRef as s, useEffect as i, useCallback as F, useMemo as S, memo as P } from "react";
3
- import { createPortal as $ } from "react-dom";
4
- import { cx as G } from "../../../utils/styles.js";
5
- const J = {
1
+ import { jsx as r, jsxs as v } from "react/jsx-runtime";
2
+ import { forwardRef as G, useRef as d, useEffect as u, useCallback as P, useMemo as C, memo as J } from "react";
3
+ import { createPortal as Q } from "react-dom";
4
+ import { cx as U } from "../../../utils/styles.js";
5
+ import { iconButtonSizes as M } from "../../../design-system/primitives/layout.js";
6
+ import { spacing as a } from "../../../design-system/tokens/spacing.js";
7
+ import { fontSizes as L } from "../../../design-system/tokens/typography.js";
8
+ import { componentGap as V } from "../../../design-system/primitives/sizing.js";
9
+ const X = {
6
10
  sm: "400px",
7
11
  md: "500px",
8
12
  lg: "640px",
9
13
  xl: "800px",
10
14
  full: "calc(100vw - 48px)"
11
- }, Q = P(function() {
15
+ }, Y = J(function() {
12
16
  return /* @__PURE__ */ r("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ r(
13
17
  "path",
14
18
  {
@@ -18,71 +22,71 @@ const J = {
18
22
  strokeLinecap: "round"
19
23
  }
20
24
  ) });
21
- }), U = q(function({
25
+ }), Z = G(function({
22
26
  isOpen: t,
23
27
  onClose: o,
24
- size: a = "md",
25
- title: l,
26
- description: u,
27
- closeOnOverlayClick: v = !0,
28
- closeOnEscape: k = !0,
28
+ size: l = "md",
29
+ title: c,
30
+ description: m,
31
+ closeOnOverlayClick: k = !0,
32
+ closeOnEscape: p = !0,
29
33
  showCloseButton: g = !0,
30
- className: M,
34
+ className: T,
31
35
  style: h,
32
- testId: L,
33
- children: I,
34
- ...T
35
- }, c) {
36
- const m = s(null), y = s(null), d = s(!1);
37
- i(() => {
38
- t && !d.current ? (y.current = document.activeElement, d.current = !0) : !t && d.current && (document.activeElement instanceof HTMLElement && document.activeElement.blur(), y.current?.focus(), y.current = null, d.current = !1);
36
+ testId: N,
37
+ children: R,
38
+ ...j
39
+ }, s) {
40
+ const f = d(null), y = d(null), i = d(!1);
41
+ u(() => {
42
+ t && !i.current ? (y.current = document.activeElement, i.current = !0) : !t && i.current && (document.activeElement instanceof HTMLElement && document.activeElement.blur(), y.current?.focus(), y.current = null, i.current = !1);
39
43
  }, [t]);
40
- const x = s(o);
41
- i(() => {
44
+ const x = d(o);
45
+ u(() => {
42
46
  x.current = o;
43
- }, [o]), i(() => {
47
+ }, [o]), u(() => {
44
48
  if (!t) return;
45
- const e = m.current;
49
+ const e = f.current;
46
50
  e && e.focus();
47
- }, [t]), i(() => {
51
+ }, [t]), u(() => {
48
52
  if (!t) return;
49
- const e = m.current, p = (n) => {
50
- if (n.key === "Escape" && k) {
53
+ const e = f.current, w = (n) => {
54
+ if (n.key === "Escape" && p) {
51
55
  x.current();
52
56
  return;
53
57
  }
54
58
  if (n.key === "Tab" && e) {
55
- const f = e.querySelectorAll(
59
+ const b = e.querySelectorAll(
56
60
  'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
57
- ), w = f[0], E = f[f.length - 1];
58
- n.shiftKey && document.activeElement === w ? (n.preventDefault(), E?.focus()) : !n.shiftKey && document.activeElement === E && (n.preventDefault(), w?.focus());
61
+ ), S = b[0], E = b[b.length - 1];
62
+ n.shiftKey && document.activeElement === S ? (n.preventDefault(), E?.focus()) : !n.shiftKey && document.activeElement === E && (n.preventDefault(), S?.focus());
59
63
  }
60
64
  };
61
- return document.addEventListener("keydown", p), document.body.style.overflow = "hidden", () => {
62
- document.removeEventListener("keydown", p), document.body.style.overflow = "";
65
+ return document.addEventListener("keydown", w), document.body.style.overflow = "hidden", () => {
66
+ document.removeEventListener("keydown", w), document.body.style.overflow = "";
63
67
  };
64
- }, [t, k]);
65
- const N = F(
68
+ }, [t, p]);
69
+ const D = P(
66
70
  (e) => {
67
- e.target === e.currentTarget && v && o();
71
+ e.target === e.currentTarget && k && o();
68
72
  },
69
- [v, o]
70
- ), R = S(() => ({
73
+ [k, o]
74
+ ), z = C(() => ({
71
75
  position: "fixed",
72
76
  inset: 0,
73
77
  zIndex: "var(--brycks-z-modal)",
74
78
  display: "flex",
75
79
  alignItems: "center",
76
80
  justifyContent: "center",
77
- padding: 24,
81
+ padding: a[6],
78
82
  backgroundColor: "var(--brycks-background-overlay)",
79
83
  backdropFilter: "blur(4px)",
80
84
  animation: "brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)"
81
- }), []), j = S(() => ({
85
+ }), []), W = C(() => ({
82
86
  position: "relative",
83
87
  width: "100%",
84
- maxWidth: J[a],
85
- maxHeight: a === "full" ? "calc(100vh - 48px)" : "85vh",
88
+ maxWidth: X[l],
89
+ maxHeight: l === "full" ? "calc(100vh - 48px)" : "85vh",
86
90
  backgroundColor: "var(--brycks-background-elevated)",
87
91
  borderRadius: "var(--brycks-radius-xl)",
88
92
  boxShadow: "var(--brycks-shadow-2xl)",
@@ -92,76 +96,76 @@ const J = {
92
96
  animation: "brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)",
93
97
  outline: "none",
94
98
  ...h
95
- }), [a, h]);
99
+ }), [l, h]);
96
100
  if (!t) return null;
97
- const D = {
101
+ const B = {
98
102
  display: "flex",
99
103
  alignItems: "flex-start",
100
104
  justifyContent: "space-between",
101
- padding: "20px 24px",
105
+ padding: `${a[5]}px ${a[6]}px`,
102
106
  borderBottom: "1px solid var(--brycks-border-muted)"
103
- }, W = {
107
+ }, H = {
104
108
  display: "flex",
105
109
  flexDirection: "column",
106
- gap: 4
107
- }, H = {
108
- fontSize: 18,
110
+ gap: a[1]
111
+ }, K = {
112
+ fontSize: L.lg,
109
113
  fontWeight: 600,
110
114
  color: "var(--brycks-foreground-default)",
111
115
  margin: 0,
112
116
  lineHeight: 1.3
113
- }, K = {
114
- fontSize: 14,
117
+ }, $ = {
118
+ fontSize: L.base,
115
119
  color: "var(--brycks-foreground-muted)",
116
120
  margin: 0
117
- }, z = {
121
+ }, A = {
118
122
  display: "flex",
119
123
  alignItems: "center",
120
124
  justifyContent: "center",
121
- width: 32,
122
- height: 32,
125
+ width: M.sm,
126
+ height: M.sm,
123
127
  borderRadius: "var(--brycks-radius-md)",
124
128
  color: "var(--brycks-foreground-muted)",
125
129
  transition: "all var(--brycks-duration-fast) var(--brycks-ease-out)",
126
- marginLeft: 12,
130
+ marginLeft: V.xl,
127
131
  flexShrink: 0
128
- }, A = {
132
+ }, q = {
129
133
  flex: 1,
130
134
  overflow: "auto",
131
- padding: 24
132
- }, B = /* @__PURE__ */ r(
135
+ padding: a[6]
136
+ }, F = /* @__PURE__ */ r(
133
137
  "div",
134
138
  {
135
139
  className: "brycks-modal-overlay",
136
- style: R,
137
- onClick: N,
138
- children: /* @__PURE__ */ b(
140
+ style: z,
141
+ onClick: D,
142
+ children: /* @__PURE__ */ v(
139
143
  "div",
140
144
  {
141
145
  ref: (e) => {
142
- m.current = e, typeof c == "function" ? c(e) : c && (c.current = e);
146
+ f.current = e, typeof s == "function" ? s(e) : s && (s.current = e);
143
147
  },
144
148
  role: "dialog",
145
149
  "aria-modal": "true",
146
- "aria-labelledby": l ? "brycks-modal-title" : void 0,
147
- "aria-describedby": u ? "brycks-modal-description" : void 0,
148
- className: G("brycks-modal", `brycks-modal--${a}`, M),
149
- style: j,
150
+ "aria-labelledby": c ? "brycks-modal-title" : void 0,
151
+ "aria-describedby": m ? "brycks-modal-description" : void 0,
152
+ className: U("brycks-modal", `brycks-modal--${l}`, T),
153
+ style: W,
150
154
  tabIndex: -1,
151
- "data-testid": L,
152
- ...T,
155
+ "data-testid": N,
156
+ ...j,
153
157
  children: [
154
- (l || g) && /* @__PURE__ */ b("div", { className: "brycks-modal-header", style: D, children: [
155
- /* @__PURE__ */ b("div", { style: W, children: [
156
- l && /* @__PURE__ */ r("h2", { id: "brycks-modal-title", style: H, children: l }),
157
- u && /* @__PURE__ */ r("p", { id: "brycks-modal-description", style: K, children: u })
158
+ (c || g) && /* @__PURE__ */ v("div", { className: "brycks-modal-header", style: B, children: [
159
+ /* @__PURE__ */ v("div", { style: H, children: [
160
+ c && /* @__PURE__ */ r("h2", { id: "brycks-modal-title", style: K, children: c }),
161
+ m && /* @__PURE__ */ r("p", { id: "brycks-modal-description", style: $, children: m })
158
162
  ] }),
159
163
  g && /* @__PURE__ */ r(
160
164
  "button",
161
165
  {
162
166
  type: "button",
163
167
  className: "brycks-modal-close",
164
- style: z,
168
+ style: A,
165
169
  onClick: o,
166
170
  "aria-label": "Close modal",
167
171
  onMouseEnter: (e) => {
@@ -170,20 +174,20 @@ const J = {
170
174
  onMouseLeave: (e) => {
171
175
  e.currentTarget.style.backgroundColor = "transparent", e.currentTarget.style.color = "var(--brycks-foreground-muted)";
172
176
  },
173
- children: /* @__PURE__ */ r(Q, {})
177
+ children: /* @__PURE__ */ r(Y, {})
174
178
  }
175
179
  )
176
180
  ] }),
177
- /* @__PURE__ */ r("div", { className: "brycks-modal-content", style: A, children: I })
181
+ /* @__PURE__ */ r("div", { className: "brycks-modal-content", style: q, children: R })
178
182
  ]
179
183
  }
180
184
  )
181
185
  }
182
186
  );
183
- return $(B, document.body);
187
+ return Q(F, document.body);
184
188
  });
185
- U.displayName = "Modal";
189
+ Z.displayName = "Modal";
186
190
  export {
187
- U as Modal
191
+ Z as Modal
188
192
  };
189
193
  //# sourceMappingURL=Modal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.js","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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"}
1
+ {"version":3,"file":"Modal.js","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\nimport { spacing, fontSizes } from '../../../design-system'\r\nimport { componentGap, iconButtonSizes } from '../../../design-system/primitives'\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: spacing[6],\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: `${spacing[5]}px ${spacing[6]}px`,\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: spacing[1],\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: fontSizes.lg,\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: fontSizes.base,\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: iconButtonSizes.sm,\r\n height: iconButtonSizes.sm,\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: componentGap.xl,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: spacing[6],\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","spacing","modalStyle","headerStyle","titleContainerStyle","titleStyle","fontSizes","descriptionStyle","closeButtonStyle","iconButtonSizes","componentGap","contentStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),o=require("react"),ce=require("react-dom"),de=require("../../../utils/styles.cjs"),l=require("../../../design-system/tokens/motion.cjs"),a=require("../../../design-system/primitives/sizing.cjs"),j=require("../../../design-system/tokens/typography.cjs"),f=require("../../../design-system/tokens/spacing.cjs"),ue={sm:{height:a.componentHeights.sm,fontSize:j.fontSizes.base-1,padding:a.componentPaddingX.sm,iconSize:a.iconSizes.sm},md:{height:a.componentHeights.md,fontSize:j.fontSizes.base,padding:a.componentPaddingX.md,iconSize:a.iconSizes.md},lg:{height:a.componentHeights.lg,fontSize:j.fontSizes.md,padding:a.componentPaddingX.lg,iconSize:a.iconSizes.lg}},fe=(x,p)=>{const b=p.toLowerCase();return x.label.toLowerCase().includes(b)||x.value.toLowerCase().includes(b)||(x.description?.toLowerCase().includes(b)??!1)};function be(){return t.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none","aria-hidden":"true",children:t.jsx("path",{d:"M4 6l4 4 4-4",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round",strokeLinejoin:"round"})})}function ge(){return t.jsx("svg",{width:"14",height:"14",viewBox:"0 0 14 14",fill:"none","aria-hidden":"true",children:t.jsx("path",{d:"M4 4l6 6M10 4l-6 6",stroke:"currentColor",strokeWidth:"1.5",strokeLinecap:"round"})})}const W=o.forwardRef(function({options:p,value:b,defaultValue:F=null,onChange:I,onInputChange:L,placeholder:X="Select or search...",size:R="md",disabled:y=!1,isInvalid:M=!1,required:A=!1,clearable:H=!0,"aria-label":O,"aria-labelledby":Y,filterFn:q=fe,emptyText:N="No options found",creatable:z=!1,onCreate:$,className:K,testId:V},w){const[G,U]=o.useState(F),[i,h]=o.useState(""),[r,m]=o.useState(!1),[s,c]=o.useState(-1),[P,D]=o.useState(!1),S=o.useRef(null),C=o.useRef(null),B=o.useRef(null),d=b!==void 0?b:G,v=ue[R],u=o.useMemo(()=>p.find(e=>e.value===d),[p,d]),g=o.useMemo(()=>i?p.filter(e=>q(e,i)):p,[p,i,q]);o.useEffect(()=>{!r&&u?h(u.label):!r&&!d&&h("")},[r,u,d]);const k=o.useCallback(e=>{b===void 0&&U(e),I?.(e),m(!1),c(-1),C.current?.focus()},[b,I]),J=o.useCallback(e=>{const n=e.target.value;h(n),L?.(n),r||m(!0),c(0)},[r,L]),Q=o.useCallback(e=>{e.stopPropagation(),k(null),h(""),C.current?.focus()},[k]),Z=o.useCallback(e=>{switch(e.key){case"ArrowDown":e.preventDefault(),r?c(n=>Math.min(n+1,g.length-1)):(m(!0),c(0));break;case"ArrowUp":e.preventDefault(),r&&c(n=>Math.max(n-1,0));break;case"Enter":e.preventDefault(),r&&s>=0&&g[s]?g[s].disabled||k(g[s].value):z&&i&&g.length===0&&$?.(i);break;case"Escape":e.preventDefault(),m(!1),c(-1),u&&h(u.label);break;case"Tab":r&&(m(!1),c(-1));break}},[r,s,g,k,z,i,$,u]);o.useEffect(()=>{if(!r)return;const e=n=>{S.current?.contains(n.target)||(m(!1),c(-1),u&&h(u.label))};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[r,u]),o.useEffect(()=>{if(!r||s<0)return;B.current?.children[s]?.scrollIntoView({block:"nearest"})},[r,s]);const[E,_]=o.useState({top:0,left:0,width:0});o.useEffect(()=>{if(!r||!S.current)return;const e=()=>{const n=S.current.getBoundingClientRect();_({top:n.bottom+window.scrollY+f.spacing[1],left:n.left+window.scrollX,width:n.width})};return e(),window.addEventListener("resize",e),window.addEventListener("scroll",e,!0),()=>{window.removeEventListener("resize",e),window.removeEventListener("scroll",e,!0)}},[r]);const ee={position:"relative",display:"inline-flex",width:"100%"},te={position:"relative",display:"flex",alignItems:"center",width:"100%",height:v.height,backgroundColor:y?"var(--brycks-background-muted)":"var(--brycks-background-default)",border:`1px solid ${M?"var(--brycks-error-default)":P?"var(--brycks-primary-default)":"var(--brycks-border-default)"}`,borderRadius:"var(--brycks-radius-md)",transition:`all ${l.durations.fast}ms ${l.easings.easeOut}`,cursor:y?"not-allowed":"text",boxShadow:P?"var(--brycks-focus-ring)":void 0},re={flex:1,height:"100%",padding:`0 ${v.padding}px`,paddingRight:f.spacing[10],fontSize:v.fontSize,color:y?"var(--brycks-foreground-disabled)":"var(--brycks-foreground-default)",backgroundColor:"transparent",border:"none",outline:"none",cursor:y?"not-allowed":"text"},ne={position:"absolute",right:v.padding,display:"flex",alignItems:"center",gap:f.spacing[1],pointerEvents:"none"},oe={display:"flex",alignItems:"center",justifyContent:"center",width:f.spacing[5],height:f.spacing[5],color:"var(--brycks-foreground-muted)",cursor:"pointer",borderRadius:"var(--brycks-radius-sm)",transition:`all ${l.durations.fast}ms ${l.easings.easeOut}`,pointerEvents:"auto"},se={display:"flex",alignItems:"center",justifyContent:"center",color:"var(--brycks-foreground-muted)",transform:r?"rotate(180deg)":"rotate(0)",transition:`transform ${l.durations.fast}ms ${l.easings.easeOut}`},ae={position:"absolute",top:E.top,left:E.left,width:E.width,maxHeight:300,overflowY:"auto",backgroundColor:"var(--brycks-background-elevated)",border:"1px solid var(--brycks-border-default)",borderRadius:"var(--brycks-radius-lg)",boxShadow:"var(--brycks-shadow-lg)",zIndex:"var(--brycks-z-dropdown)",padding:f.spacing[1],animation:`brycks-dropdown-in ${l.durations.quick}ms ${l.easings.easeOut}`},T=(e,n,le)=>({display:"flex",alignItems:"center",gap:a.componentGap.md,padding:`${f.spacing[2]}px ${a.componentPaddingX.sm}px`,fontSize:v.fontSize,color:n?"var(--brycks-foreground-disabled)":"var(--brycks-foreground-default)",backgroundColor:e?"var(--brycks-background-muted)":le?"var(--brycks-primary-50)":"transparent",borderRadius:"var(--brycks-radius-md)",cursor:n?"not-allowed":"pointer",transition:`background-color ${l.durations.fast}ms ${l.easings.easeOut}`}),ie={padding:`${f.spacing[4]}px ${f.spacing[3]}px`,fontSize:v.fontSize,color:"var(--brycks-foreground-muted)",textAlign:"center"};return t.jsxs("div",{ref:S,className:de.cx("brycks-combobox",`brycks-combobox--${R}`,K),style:ee,"data-testid":V,children:[t.jsxs("div",{style:te,onClick:()=>!y&&C.current?.focus(),children:[t.jsx("input",{ref:e=>{C.current=e,typeof w=="function"?w(e):w&&(w.current=e)},type:"text",role:"combobox","aria-expanded":r,"aria-haspopup":"listbox","aria-autocomplete":"list","aria-controls":r?"combobox-listbox":void 0,"aria-activedescendant":r&&s>=0?`combobox-option-${s}`:void 0,"aria-invalid":M,"aria-required":A,"aria-label":O,"aria-labelledby":Y,value:i,placeholder:X,disabled:y,style:re,onChange:J,onKeyDown:Z,onFocus:()=>{D(!0),m(!0)},onBlur:()=>D(!1)}),t.jsxs("div",{style:ne,children:[H&&d&&!y&&t.jsx("button",{type:"button","aria-label":"Clear selection",style:oe,onClick:Q,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(ge,{})}),t.jsx("span",{style:se,children:t.jsx(be,{})})]})]}),r&&ce.createPortal(t.jsxs(t.Fragment,{children:[t.jsx("style",{children:`
2
+ @keyframes brycks-dropdown-in {
3
+ from { opacity: 0; transform: translateY(-4px); }
4
+ to { opacity: 1; transform: translateY(0); }
5
+ }
6
+ `}),t.jsx("ul",{ref:B,id:"combobox-listbox",role:"listbox","aria-label":O||"Options",style:ae,children:g.length>0?g.map((e,n)=>t.jsxs("li",{id:`combobox-option-${n}`,role:"option","aria-selected":e.value===d,"aria-disabled":e.disabled,style:T(n===s,!!e.disabled,e.value===d),onClick:()=>!e.disabled&&k(e.value),onMouseEnter:()=>c(n),children:[e.icon&&t.jsx("span",{style:{flexShrink:0},children:e.icon}),t.jsxs("div",{style:{flex:1,minWidth:0},children:[t.jsx("div",{style:{fontWeight:e.value===d?500:400},children:e.label}),e.description&&t.jsx("div",{style:{fontSize:j.fontSizes.sm,color:"var(--brycks-foreground-muted)"},children:e.description})]}),e.value===d&&t.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",style:{flexShrink:0},children:t.jsx("path",{d:"M3 8l3 3 7-7",stroke:"var(--brycks-primary-default)",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})})]},e.value)):t.jsx("li",{style:ie,children:z&&i?t.jsxs("button",{type:"button",style:{...T(!0,!1,!1),width:"100%",border:"none",cursor:"pointer"},onClick:()=>$?.(i),children:['Create "',i,'"']}):N})})]}),document.body)]})});W.displayName="Combobox";exports.Combobox=W;
7
+ //# sourceMappingURL=Combobox.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Combobox.cjs","sources":["../../../../src/components/form/Combobox/Combobox.tsx"],"sourcesContent":["/**\n * Combobox Component\n *\n * A searchable select/autocomplete component with keyboard navigation.\n * Combines the functionality of Input with dropdown selection.\n *\n * @module components/form/Combobox\n */\n\nimport {\n forwardRef,\n useState,\n useCallback,\n useRef,\n useEffect,\n useMemo,\n type CSSProperties,\n type ReactNode,\n type KeyboardEvent,\n type ChangeEvent,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\nimport { spacing, fontSizes, durations, easings } from '../../../design-system'\nimport {\n componentHeights,\n componentPaddingX,\n componentGap,\n iconSizes,\n} from '../../../design-system/primitives'\n\nexport type ComboboxSize = 'sm' | 'md' | 'lg'\n\nexport interface ComboboxOption {\n /** Unique identifier for the option */\n value: string\n /** Display label */\n label: string\n /** Optional description */\n description?: string\n /** Optional icon */\n icon?: ReactNode\n /** Whether the option is disabled */\n disabled?: boolean\n}\n\nexport interface ComboboxProps {\n /** Available options */\n options: ComboboxOption[]\n /** Currently selected value (controlled) */\n value?: string | null\n /** Default selected value (uncontrolled) */\n defaultValue?: string | null\n /** Callback when selection changes */\n onChange?: (value: string | null) => void\n /** Callback when input value changes */\n onInputChange?: (value: string) => void\n /** Placeholder text */\n placeholder?: string\n /** Component size */\n size?: ComboboxSize\n /** Whether the combobox is disabled */\n disabled?: boolean\n /** Whether the combobox is invalid */\n isInvalid?: boolean\n /** Whether the combobox is required */\n required?: boolean\n /** Whether to allow clearing the selection */\n clearable?: boolean\n /** Label for accessibility */\n 'aria-label'?: string\n /** ID of element that labels this combobox */\n 'aria-labelledby'?: string\n /** Custom filter function */\n filterFn?: (option: ComboboxOption, inputValue: string) => boolean\n /** Text to show when no options match */\n emptyText?: string\n /** Whether to allow creating new options */\n creatable?: boolean\n /** Callback when creating new option */\n onCreate?: (inputValue: string) => void\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nconst sizeConfig: Record<ComboboxSize, { height: number; fontSize: number; padding: number; iconSize: number }> = {\n sm: { height: componentHeights.sm, fontSize: fontSizes.base - 1, padding: componentPaddingX.sm, iconSize: iconSizes.sm },\n md: { height: componentHeights.md, fontSize: fontSizes.base, padding: componentPaddingX.md, iconSize: iconSizes.md },\n lg: { height: componentHeights.lg, fontSize: fontSizes.md, padding: componentPaddingX.lg, iconSize: iconSizes.lg },\n}\n\nconst defaultFilterFn = (option: ComboboxOption, inputValue: string): boolean => {\n const search = inputValue.toLowerCase()\n return (\n option.label.toLowerCase().includes(search) ||\n option.value.toLowerCase().includes(search) ||\n (option.description?.toLowerCase().includes(search) ?? false)\n )\n}\n\nfunction ChevronDownIcon() {\n return (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M4 6l4 4 4-4\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n )\n}\n\nfunction ClearIcon() {\n return (\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" aria-hidden=\"true\">\n <path d=\"M4 4l6 6M10 4l-6 6\" stroke=\"currentColor\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n )\n}\n\nexport const Combobox = forwardRef<HTMLInputElement, ComboboxProps>(function Combobox(\n {\n options,\n value: controlledValue,\n defaultValue = null,\n onChange,\n onInputChange,\n placeholder = 'Select or search...',\n size = 'md',\n disabled = false,\n isInvalid = false,\n required = false,\n clearable = true,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n filterFn = defaultFilterFn,\n emptyText = 'No options found',\n creatable = false,\n onCreate,\n className,\n testId,\n },\n ref\n) {\n const [internalValue, setInternalValue] = useState(defaultValue)\n const [inputValue, setInputValue] = useState('')\n const [isOpen, setIsOpen] = useState(false)\n const [activeIndex, setActiveIndex] = useState(-1)\n const [isFocused, setIsFocused] = useState(false)\n\n const containerRef = useRef<HTMLDivElement>(null)\n const inputRef = useRef<HTMLInputElement>(null)\n const listRef = useRef<HTMLUListElement>(null)\n\n const value = controlledValue !== undefined ? controlledValue : internalValue\n const config = sizeConfig[size]\n\n // Get selected option\n const selectedOption = useMemo(\n () => options.find((opt) => opt.value === value),\n [options, value]\n )\n\n // Filter options based on input\n const filteredOptions = useMemo(() => {\n if (!inputValue) return options\n return options.filter((opt) => filterFn(opt, inputValue))\n }, [options, inputValue, filterFn])\n\n // Sync input with selected value\n useEffect(() => {\n if (!isOpen && selectedOption) {\n setInputValue(selectedOption.label)\n } else if (!isOpen && !value) {\n setInputValue('')\n }\n }, [isOpen, selectedOption, value])\n\n const handleSelect = useCallback(\n (optionValue: string | null) => {\n if (controlledValue === undefined) {\n setInternalValue(optionValue)\n }\n onChange?.(optionValue)\n setIsOpen(false)\n setActiveIndex(-1)\n inputRef.current?.focus()\n },\n [controlledValue, onChange]\n )\n\n const handleInputChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value\n setInputValue(newValue)\n onInputChange?.(newValue)\n if (!isOpen) setIsOpen(true)\n setActiveIndex(0)\n },\n [isOpen, onInputChange]\n )\n\n const handleClear = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation()\n handleSelect(null)\n setInputValue('')\n inputRef.current?.focus()\n },\n [handleSelect]\n )\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n if (!isOpen) {\n setIsOpen(true)\n setActiveIndex(0)\n } else {\n setActiveIndex((prev) => Math.min(prev + 1, filteredOptions.length - 1))\n }\n break\n case 'ArrowUp':\n e.preventDefault()\n if (isOpen) {\n setActiveIndex((prev) => Math.max(prev - 1, 0))\n }\n break\n case 'Enter':\n e.preventDefault()\n if (isOpen && activeIndex >= 0 && filteredOptions[activeIndex]) {\n if (!filteredOptions[activeIndex].disabled) {\n handleSelect(filteredOptions[activeIndex].value)\n }\n } else if (creatable && inputValue && filteredOptions.length === 0) {\n onCreate?.(inputValue)\n }\n break\n case 'Escape':\n e.preventDefault()\n setIsOpen(false)\n setActiveIndex(-1)\n if (selectedOption) {\n setInputValue(selectedOption.label)\n }\n break\n case 'Tab':\n if (isOpen) {\n setIsOpen(false)\n setActiveIndex(-1)\n }\n break\n }\n },\n [isOpen, activeIndex, filteredOptions, handleSelect, creatable, inputValue, onCreate, selectedOption]\n )\n\n // Close on outside click\n useEffect(() => {\n if (!isOpen) return\n\n const handleClick = (e: MouseEvent) => {\n if (!containerRef.current?.contains(e.target as Node)) {\n setIsOpen(false)\n setActiveIndex(-1)\n if (selectedOption) {\n setInputValue(selectedOption.label)\n }\n }\n }\n\n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [isOpen, selectedOption])\n\n // Scroll active option into view\n useEffect(() => {\n if (!isOpen || activeIndex < 0) return\n const list = listRef.current\n const activeItem = list?.children[activeIndex] as HTMLElement | undefined\n activeItem?.scrollIntoView({ block: 'nearest' })\n }, [isOpen, activeIndex])\n\n // Calculate dropdown position\n const [dropdownPosition, setDropdownPosition] = useState({ top: 0, left: 0, width: 0 })\n\n useEffect(() => {\n if (!isOpen || !containerRef.current) return\n\n const updatePosition = () => {\n const rect = containerRef.current!.getBoundingClientRect()\n setDropdownPosition({\n top: rect.bottom + window.scrollY + spacing[1],\n left: rect.left + window.scrollX,\n width: rect.width,\n })\n }\n\n updatePosition()\n window.addEventListener('resize', updatePosition)\n window.addEventListener('scroll', updatePosition, true)\n\n return () => {\n window.removeEventListener('resize', updatePosition)\n window.removeEventListener('scroll', updatePosition, true)\n }\n }, [isOpen])\n\n const containerStyle: CSSProperties = {\n position: 'relative',\n display: 'inline-flex',\n width: '100%',\n }\n\n const inputWrapperStyle: CSSProperties = {\n position: 'relative',\n display: 'flex',\n alignItems: 'center',\n width: '100%',\n height: config.height,\n backgroundColor: disabled ? 'var(--brycks-background-muted)' : 'var(--brycks-background-default)',\n border: `1px solid ${\n isInvalid\n ? 'var(--brycks-error-default)'\n : isFocused\n ? 'var(--brycks-primary-default)'\n : 'var(--brycks-border-default)'\n }`,\n borderRadius: 'var(--brycks-radius-md)',\n transition: `all ${durations.fast}ms ${easings.easeOut}`,\n cursor: disabled ? 'not-allowed' : 'text',\n boxShadow: isFocused ? 'var(--brycks-focus-ring)' : undefined,\n }\n\n const inputStyle: CSSProperties = {\n flex: 1,\n height: '100%',\n padding: `0 ${config.padding}px`,\n paddingRight: spacing[10],\n fontSize: config.fontSize,\n color: disabled ? 'var(--brycks-foreground-disabled)' : 'var(--brycks-foreground-default)',\n backgroundColor: 'transparent',\n border: 'none',\n outline: 'none',\n cursor: disabled ? 'not-allowed' : 'text',\n }\n\n const iconsStyle: CSSProperties = {\n position: 'absolute',\n right: config.padding,\n display: 'flex',\n alignItems: 'center',\n gap: spacing[1],\n pointerEvents: 'none',\n }\n\n const clearButtonStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: spacing[5],\n height: spacing[5],\n color: 'var(--brycks-foreground-muted)',\n cursor: 'pointer',\n borderRadius: 'var(--brycks-radius-sm)',\n transition: `all ${durations.fast}ms ${easings.easeOut}`,\n pointerEvents: 'auto',\n }\n\n const chevronStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n color: 'var(--brycks-foreground-muted)',\n transform: isOpen ? 'rotate(180deg)' : 'rotate(0)',\n transition: `transform ${durations.fast}ms ${easings.easeOut}`,\n }\n\n const dropdownStyle: CSSProperties = {\n position: 'absolute',\n top: dropdownPosition.top,\n left: dropdownPosition.left,\n width: dropdownPosition.width,\n maxHeight: 300,\n overflowY: 'auto',\n backgroundColor: 'var(--brycks-background-elevated)',\n border: '1px solid var(--brycks-border-default)',\n borderRadius: 'var(--brycks-radius-lg)',\n boxShadow: 'var(--brycks-shadow-lg)',\n zIndex: 'var(--brycks-z-dropdown)' as unknown as number,\n padding: spacing[1],\n animation: `brycks-dropdown-in ${durations.quick}ms ${easings.easeOut}`,\n }\n\n const optionStyle = (isActive: boolean, isDisabled: boolean, isSelected: boolean): CSSProperties => ({\n display: 'flex',\n alignItems: 'center',\n gap: componentGap.md,\n padding: `${spacing[2]}px ${componentPaddingX.sm}px`,\n fontSize: config.fontSize,\n color: isDisabled\n ? 'var(--brycks-foreground-disabled)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: isActive\n ? 'var(--brycks-background-muted)'\n : isSelected\n ? 'var(--brycks-primary-50)'\n : 'transparent',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: isDisabled ? 'not-allowed' : 'pointer',\n transition: `background-color ${durations.fast}ms ${easings.easeOut}`,\n })\n\n const emptyStyle: CSSProperties = {\n padding: `${spacing[4]}px ${spacing[3]}px`,\n fontSize: config.fontSize,\n color: 'var(--brycks-foreground-muted)',\n textAlign: 'center',\n }\n\n return (\n <div\n ref={containerRef}\n className={cx('brycks-combobox', `brycks-combobox--${size}`, className)}\n style={containerStyle}\n data-testid={testId}\n >\n <div\n style={inputWrapperStyle}\n onClick={() => !disabled && inputRef.current?.focus()}\n >\n <input\n ref={(node) => {\n (inputRef as React.MutableRefObject<HTMLInputElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n type=\"text\"\n role=\"combobox\"\n aria-expanded={isOpen}\n aria-haspopup=\"listbox\"\n aria-autocomplete=\"list\"\n aria-controls={isOpen ? 'combobox-listbox' : undefined}\n aria-activedescendant={isOpen && activeIndex >= 0 ? `combobox-option-${activeIndex}` : undefined}\n aria-invalid={isInvalid}\n aria-required={required}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n value={inputValue}\n placeholder={placeholder}\n disabled={disabled}\n style={inputStyle}\n onChange={handleInputChange}\n onKeyDown={handleKeyDown}\n onFocus={() => {\n setIsFocused(true)\n setIsOpen(true)\n }}\n onBlur={() => setIsFocused(false)}\n />\n <div style={iconsStyle}>\n {clearable && value && !disabled && (\n <button\n type=\"button\"\n aria-label=\"Clear selection\"\n style={clearButtonStyle}\n onClick={handleClear}\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 <ClearIcon />\n </button>\n )}\n <span style={chevronStyle}>\n <ChevronDownIcon />\n </span>\n </div>\n </div>\n\n {isOpen &&\n createPortal(\n <>\n <style>\n {`\n @keyframes brycks-dropdown-in {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n }\n `}\n </style>\n <ul\n ref={listRef}\n id=\"combobox-listbox\"\n role=\"listbox\"\n aria-label={ariaLabel || 'Options'}\n style={dropdownStyle}\n >\n {filteredOptions.length > 0 ? (\n filteredOptions.map((option, index) => (\n <li\n key={option.value}\n id={`combobox-option-${index}`}\n role=\"option\"\n aria-selected={option.value === value}\n aria-disabled={option.disabled}\n style={optionStyle(index === activeIndex, !!option.disabled, option.value === value)}\n onClick={() => !option.disabled && handleSelect(option.value)}\n onMouseEnter={() => setActiveIndex(index)}\n >\n {option.icon && <span style={{ flexShrink: 0 }}>{option.icon}</span>}\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontWeight: option.value === value ? 500 : 400 }}>{option.label}</div>\n {option.description && (\n <div style={{ fontSize: fontSizes.sm, color: 'var(--brycks-foreground-muted)' }}>\n {option.description}\n </div>\n )}\n </div>\n {option.value === value && (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" style={{ flexShrink: 0 }}>\n <path\n d=\"M3 8l3 3 7-7\"\n stroke=\"var(--brycks-primary-default)\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n </svg>\n )}\n </li>\n ))\n ) : (\n <li style={emptyStyle}>\n {creatable && inputValue ? (\n <button\n type=\"button\"\n style={{\n ...optionStyle(true, false, false),\n width: '100%',\n border: 'none',\n cursor: 'pointer',\n }}\n onClick={() => onCreate?.(inputValue)}\n >\n Create \"{inputValue}\"\n </button>\n ) : (\n emptyText\n )}\n </li>\n )}\n </ul>\n </>,\n document.body\n )}\n </div>\n )\n})\n\nCombobox.displayName = 'Combobox'\n"],"names":["sizeConfig","componentHeights","fontSizes","componentPaddingX","iconSizes","defaultFilterFn","option","inputValue","search","ChevronDownIcon","jsx","ClearIcon","Combobox","forwardRef","options","controlledValue","defaultValue","onChange","onInputChange","placeholder","size","disabled","isInvalid","required","clearable","ariaLabel","ariaLabelledby","filterFn","emptyText","creatable","onCreate","className","testId","ref","internalValue","setInternalValue","useState","setInputValue","isOpen","setIsOpen","activeIndex","setActiveIndex","isFocused","setIsFocused","containerRef","useRef","inputRef","listRef","value","config","selectedOption","useMemo","opt","filteredOptions","useEffect","handleSelect","useCallback","optionValue","handleInputChange","newValue","handleClear","handleKeyDown","prev","handleClick","e","dropdownPosition","setDropdownPosition","updatePosition","rect","spacing","containerStyle","inputWrapperStyle","durations","easings","inputStyle","iconsStyle","clearButtonStyle","chevronStyle","dropdownStyle","optionStyle","isActive","isDisabled","isSelected","componentGap","emptyStyle","jsxs","cx","node","createPortal","Fragment","index"],"mappings":"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"}