@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,7 +1,7 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("react/jsx-runtime"),r=require("react"),U=require("react-dom"),g=require("../../../utils/styles.cjs"),T=r.createContext(null);function G(){const b=r.useContext(T);if(!b)throw new Error("Dropdown components must be used within a Dropdown");return b}const K=r.forwardRef(function({trigger:t,isOpen:i,defaultOpen:d=!1,onOpenChange:l,placement:c="bottom-start",offset:p=4,closeOnSelect:v=!0,className:x,style:D,children:h,testId:S,...C},E){const[j,R]=r.useState(d),[o,O]=r.useState({top:0,left:0}),[y,m]=r.useState(-1),[I,W]=r.useState(!1),w=r.useRef(null),k=r.useRef(null),n=i??j;r.useEffect(()=>(W(!0),()=>W(!1)),[]);const u=r.useCallback(e=>{i===void 0&&R(e),l?.(e),e||m(-1)},[i,l]),B=r.useCallback(()=>u(!1),[u]);r.useEffect(()=>{if(!n||!w.current||!I)return;const e=()=>{const s=w.current;if(!s)return;const f=s.getBoundingClientRect(),A=window.scrollX,P=window.scrollY;let L=0,N=0;c.startsWith("bottom")?L=f.bottom+P+p:L=f.top+P-p,c.endsWith("start")?N=f.left+A:N=f.right+A,O({top:L,left:N})};return e(),window.addEventListener("resize",e),window.addEventListener("scroll",e,!0),()=>{window.removeEventListener("resize",e),window.removeEventListener("scroll",e,!0)}},[n,c,p,I]),r.useEffect(()=>{if(!n)return;const e=s=>{w.current?.contains(s.target)||k.current?.contains(s.target)||u(!1)};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[n,u]);const z=r.useCallback(e=>{if(!n){(e.key==="ArrowDown"||e.key==="Enter"||e.key===" ")&&(e.preventDefault(),u(!0));return}const s=k.current?.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])');if(s?.length)switch(e.key){case"ArrowDown":e.preventDefault(),m(f=>(f+1)%s.length);break;case"ArrowUp":e.preventDefault(),m(f=>(f-1+s.length)%s.length);break;case"Home":e.preventDefault(),m(0);break;case"End":e.preventDefault(),m(s.length-1);break;case"Escape":e.preventDefault(),u(!1),w.current?.focus();break;case"Enter":case" ":e.preventDefault(),y>=0&&s[y]?.click();break}},[n,y,u]);r.useEffect(()=>{if(!n||y<0)return;k.current?.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')?.[y]?.focus()},[n,y]);const q=t.props,X=r.cloneElement(t,{ref:w,"aria-haspopup":"menu","aria-expanded":n,onClick:e=>{q.onClick?.(e),u(!n)},onKeyDown:e=>{q.onKeyDown?.(e),z(e)}}),H={position:"absolute",top:o.top,left:c.endsWith("end")?"auto":o.left,right:c.endsWith("end")?window.innerWidth-o.left:"auto",zIndex:"var(--brycks-z-dropdown)",minWidth:w.current?.offsetWidth??160,backgroundColor:"var(--brycks-background-elevated)",border:"1px solid var(--brycks-border-default)",borderRadius:"var(--brycks-radius-lg)",boxShadow:"var(--brycks-shadow-lg)",padding:4,animation:"brycks-dropdown-in 150ms ease-out",outline:"none",...D};return a.jsx(T.Provider,{value:{isOpen:n,close:v?B:()=>{},activeIndex:y,setActiveIndex:m},children:a.jsxs("div",{ref:E,className:g.cx("brycks-dropdown",x),style:{display:"inline-block"},"data-testid":S,...C,children:[X,n&&I&&U.createPortal(a.jsxs(a.Fragment,{children:[a.jsx("style",{children:`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("react/jsx-runtime"),r=require("react"),Q=require("react-dom"),x=require("../../../utils/styles.cjs"),f=require("../../../design-system/primitives/sizing.cjs"),v=require("../../../design-system/tokens/motion.cjs"),N=require("../../../design-system/tokens/spacing.cjs"),$=require("../../../design-system/tokens/typography.cjs"),X=r.createContext(null);function V(){const b=r.useContext(X);if(!b)throw new Error("Dropdown components must be used within a Dropdown");return b}const K=r.forwardRef(function({trigger:t,isOpen:c,defaultOpen:l=!1,onOpenChange:d,placement:i="bottom-start",offset:m=4,closeOnSelect:D=!0,className:h,style:S,children:C,testId:E,...j},z){const[R,I]=r.useState(l),[n,G]=r.useState({top:0,left:0}),[y,g]=r.useState(-1),[L,W]=r.useState(!1),w=r.useRef(null),k=r.useRef(null),o=c??R;r.useEffect(()=>(W(!0),()=>W(!1)),[]);const u=r.useCallback(e=>{c===void 0&&I(e),d?.(e),e||g(-1)},[c,d]),H=r.useCallback(()=>u(!1),[u]);r.useEffect(()=>{if(!o||!w.current||!L)return;const e=()=>{const s=w.current;if(!s)return;const p=s.getBoundingClientRect(),Y=window.scrollX,O=window.scrollY;let q=0,P=0;i.startsWith("bottom")?q=p.bottom+O+m:q=p.top+O-m,i.endsWith("start")?P=p.left+Y:P=p.right+Y,G({top:q,left:P})};return e(),window.addEventListener("resize",e),window.addEventListener("scroll",e,!0),()=>{window.removeEventListener("resize",e),window.removeEventListener("scroll",e,!0)}},[o,i,m,L]),r.useEffect(()=>{if(!o)return;const e=s=>{w.current?.contains(s.target)||k.current?.contains(s.target)||u(!1)};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[o,u]);const A=r.useCallback(e=>{if(!o){(e.key==="ArrowDown"||e.key==="Enter"||e.key===" ")&&(e.preventDefault(),u(!0));return}const s=k.current?.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])');if(s?.length)switch(e.key){case"ArrowDown":e.preventDefault(),g(p=>(p+1)%s.length);break;case"ArrowUp":e.preventDefault(),g(p=>(p-1+s.length)%s.length);break;case"Home":e.preventDefault(),g(0);break;case"End":e.preventDefault(),g(s.length-1);break;case"Escape":e.preventDefault(),u(!1),w.current?.focus();break;case"Enter":case" ":e.preventDefault(),y>=0&&s[y]?.click();break}},[o,y,u]);r.useEffect(()=>{if(!o||y<0)return;k.current?.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')?.[y]?.focus()},[o,y]);const T=t.props,U=r.cloneElement(t,{ref:w,"aria-haspopup":"menu","aria-expanded":o,onClick:e=>{T.onClick?.(e),u(!o)},onKeyDown:e=>{T.onKeyDown?.(e),A(e)}}),J={position:"absolute",top:n.top,left:i.endsWith("end")?"auto":n.left,right:i.endsWith("end")?window.innerWidth-n.left:"auto",zIndex:"var(--brycks-z-dropdown)",minWidth:w.current?.offsetWidth??160,backgroundColor:"var(--brycks-background-elevated)",border:"1px solid var(--brycks-border-default)",borderRadius:"var(--brycks-radius-lg)",boxShadow:"var(--brycks-shadow-lg)",padding:N.spacing[1],animation:`brycks-dropdown-in ${v.durations.quick}ms ${v.easings.easeOut}`,outline:"none",...S};return a.jsx(X.Provider,{value:{isOpen:o,close:D?H:()=>{},activeIndex:y,setActiveIndex:g},children:a.jsxs("div",{ref:z,className:x.cx("brycks-dropdown",h),style:{display:"inline-block"},"data-testid":E,...j,children:[U,o&&L&&Q.createPortal(a.jsxs(a.Fragment,{children:[a.jsx("style",{children:`
2
2
  @keyframes brycks-dropdown-in {
3
3
  from { opacity: 0; transform: translateY(-4px); }
4
4
  to { opacity: 1; transform: translateY(0); }
5
5
  }
6
- `}),a.jsx("div",{ref:k,role:"menu",style:H,onKeyDown:z,tabIndex:-1,children:h})]}),document.body)]})})});K.displayName="Dropdown";const M=r.forwardRef(function({disabled:t=!1,icon:i,shortcut:d,destructive:l=!1,className:c,style:p,children:v,onClick:x,...D},h){const{close:S}=G(),C=o=>{t||(x?.(o),S())},E={display:"flex",alignItems:"center",gap:10,padding:"8px 12px",fontSize:14,color:t?"var(--brycks-foreground-disabled)":l?"var(--brycks-error-default)":"var(--brycks-foreground-default)",backgroundColor:"transparent",borderRadius:"var(--brycks-radius-md)",cursor:t?"not-allowed":"pointer",transition:"background-color 100ms ease-out",outline:"none",...p},j={width:16,height:16,color:t?"var(--brycks-foreground-disabled)":l?"var(--brycks-error-default)":"var(--brycks-foreground-muted)",flexShrink:0},R={marginLeft:"auto",fontSize:12,color:"var(--brycks-foreground-muted)",fontFamily:"var(--brycks-font-mono)"};return a.jsxs("div",{ref:h,role:"menuitem","aria-disabled":t,tabIndex:t?-1:0,className:g.cx("brycks-dropdown-item",t&&"brycks-dropdown-item--disabled",c),style:E,onClick:C,onMouseEnter:o=>{t||(o.currentTarget.style.backgroundColor="var(--brycks-background-muted)")},onMouseLeave:o=>{o.currentTarget.style.backgroundColor="transparent"},onFocus:o=>{o.currentTarget.style.backgroundColor="var(--brycks-background-muted)"},onBlur:o=>{o.currentTarget.style.backgroundColor="transparent"},...D,children:[i&&a.jsx("span",{style:j,children:i}),a.jsx("span",{style:{flex:1},children:v}),d&&a.jsx("span",{style:R,children:d})]})});M.displayName="DropdownItem";const Y=r.forwardRef(function({className:t,style:i,...d},l){const c={height:1,backgroundColor:"var(--brycks-border-muted)",margin:"4px 0",...i};return a.jsx("div",{ref:l,role:"separator",className:g.cx("brycks-dropdown-divider",t),style:c,...d})});Y.displayName="DropdownDivider";const F=r.forwardRef(function({className:t,style:i,children:d,...l},c){const p={padding:"8px 12px 4px 12px",fontSize:11,fontWeight:600,color:"var(--brycks-foreground-muted)",textTransform:"uppercase",letterSpacing:"0.05em",...i};return a.jsx("div",{ref:c,className:g.cx("brycks-dropdown-label",t),style:p,...l,children:d})});F.displayName="DropdownLabel";exports.Dropdown=K;exports.DropdownDivider=Y;exports.DropdownItem=M;exports.DropdownLabel=F;
6
+ `}),a.jsx("div",{ref:k,role:"menu",style:J,onKeyDown:A,tabIndex:-1,children:C})]}),document.body)]})})});K.displayName="Dropdown";const M=r.forwardRef(function({disabled:t=!1,icon:c,shortcut:l,destructive:d=!1,className:i,style:m,children:D,onClick:h,...S},C){const{close:E}=V(),j=n=>{t||(h?.(n),E())},z={display:"flex",alignItems:"center",gap:f.componentGap.lg,padding:`${f.componentPaddingY.md}px ${f.componentPaddingX.sm}px`,fontSize:$.fontSizes.base,color:t?"var(--brycks-foreground-disabled)":d?"var(--brycks-error-default)":"var(--brycks-foreground-default)",backgroundColor:"transparent",borderRadius:"var(--brycks-radius-md)",cursor:t?"not-allowed":"pointer",transition:`background-color ${v.durations.fast}ms ${v.easings.easeOut}`,outline:"none",...m},R={width:f.iconSizes.sm,height:f.iconSizes.sm,color:t?"var(--brycks-foreground-disabled)":d?"var(--brycks-error-default)":"var(--brycks-foreground-muted)",flexShrink:0},I={marginLeft:"auto",fontSize:$.fontSizes.sm,color:"var(--brycks-foreground-muted)",fontFamily:"var(--brycks-font-mono)"};return a.jsxs("div",{ref:C,role:"menuitem","aria-disabled":t,tabIndex:t?-1:0,className:x.cx("brycks-dropdown-item",t&&"brycks-dropdown-item--disabled",i),style:z,onClick:j,onMouseEnter:n=>{t||(n.currentTarget.style.backgroundColor="var(--brycks-background-muted)")},onMouseLeave:n=>{n.currentTarget.style.backgroundColor="transparent"},onFocus:n=>{n.currentTarget.style.backgroundColor="var(--brycks-background-muted)"},onBlur:n=>{n.currentTarget.style.backgroundColor="transparent"},...S,children:[c&&a.jsx("span",{style:R,children:c}),a.jsx("span",{style:{flex:1},children:D}),l&&a.jsx("span",{style:I,children:l})]})});M.displayName="DropdownItem";const F=r.forwardRef(function({className:t,style:c,...l},d){const i={height:1,backgroundColor:"var(--brycks-border-muted)",margin:`${N.spacing[1]}px 0`,...c};return a.jsx("div",{ref:d,role:"separator",className:x.cx("brycks-dropdown-divider",t),style:i,...l})});F.displayName="DropdownDivider";const B=r.forwardRef(function({className:t,style:c,children:l,...d},i){const m={padding:`${f.componentPaddingY.md}px ${f.componentPaddingX.sm}px ${N.spacing[1]}px ${f.componentPaddingX.sm}px`,fontSize:$.fontSizes.xs,fontWeight:600,color:"var(--brycks-foreground-muted)",textTransform:"uppercase",letterSpacing:"0.05em",...c};return a.jsx("div",{ref:i,className:x.cx("brycks-dropdown-label",t),style:m,...d,children:l})});B.displayName="DropdownLabel";exports.Dropdown=K;exports.DropdownDivider=F;exports.DropdownItem=M;exports.DropdownLabel=B;
7
7
  //# sourceMappingURL=Dropdown.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Dropdown.cjs","sources":["../../../../src/components/navigation/Dropdown/Dropdown.tsx"],"sourcesContent":["/**\n * Dropdown Component\n *\n * A dropdown menu with keyboard navigation support.\n * Can be used for actions, navigation, or selection.\n */\n\nimport {\n forwardRef,\n useState,\n useCallback,\n useRef,\n useEffect,\n cloneElement,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type ReactElement,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\n\nexport type DropdownPlacement = 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end'\n\ninterface DropdownContextValue {\n isOpen: boolean\n close: () => void\n activeIndex: number\n setActiveIndex: (index: number) => void\n}\n\nconst DropdownContext = createContext<DropdownContextValue | null>(null)\n\nfunction useDropdownContext() {\n const context = useContext(DropdownContext)\n if (!context) {\n throw new Error('Dropdown components must be used within a Dropdown')\n }\n return context\n}\n\nexport interface DropdownProps extends HTMLAttributes<HTMLDivElement> {\n /** Trigger element */\n trigger: ReactElement\n /** Whether the dropdown is open (controlled) */\n isOpen?: boolean\n /** Default open state (uncontrolled) */\n defaultOpen?: boolean\n /** Callback when open state changes */\n onOpenChange?: (isOpen: boolean) => void\n /** Dropdown placement */\n placement?: DropdownPlacement\n /** Offset from trigger */\n offset?: number\n /** Whether to close on item select */\n closeOnSelect?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(function Dropdown(\n {\n trigger,\n isOpen: controlledIsOpen,\n defaultOpen = false,\n onOpenChange,\n placement = 'bottom-start',\n offset = 4,\n closeOnSelect = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalIsOpen, setInternalIsOpen] = useState(defaultOpen)\n const [position, setPosition] = useState({ top: 0, left: 0 })\n const [activeIndex, setActiveIndex] = useState(-1)\n const [mounted, setMounted] = useState(false)\n const triggerRef = useRef<HTMLElement>(null)\n const menuRef = useRef<HTMLDivElement>(null)\n\n const isOpen = controlledIsOpen ?? internalIsOpen\n\n useEffect(() => {\n setMounted(true)\n return () => setMounted(false)\n }, [])\n\n const setIsOpen = useCallback(\n (open: boolean) => {\n if (controlledIsOpen === undefined) {\n setInternalIsOpen(open)\n }\n onOpenChange?.(open)\n if (!open) {\n setActiveIndex(-1)\n }\n },\n [controlledIsOpen, onOpenChange]\n )\n\n const close = useCallback(() => setIsOpen(false), [setIsOpen])\n\n // Calculate position\n useEffect(() => {\n if (!isOpen || !triggerRef.current || !mounted) return\n\n const updatePosition = () => {\n const trigger = triggerRef.current\n if (!trigger) return\n\n const rect = trigger.getBoundingClientRect()\n const scrollX = window.scrollX\n const scrollY = window.scrollY\n\n let top = 0\n let left = 0\n\n if (placement.startsWith('bottom')) {\n top = rect.bottom + scrollY + offset\n } else {\n top = rect.top + scrollY - offset\n }\n\n if (placement.endsWith('start')) {\n left = rect.left + scrollX\n } else {\n left = rect.right + scrollX\n }\n\n setPosition({ top, left })\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, placement, offset, mounted])\n\n // Close on outside click\n useEffect(() => {\n if (!isOpen) return\n\n const handleClick = (e: MouseEvent) => {\n if (\n triggerRef.current?.contains(e.target as Node) ||\n menuRef.current?.contains(e.target as Node)\n ) {\n return\n }\n setIsOpen(false)\n }\n\n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [isOpen, setIsOpen])\n\n // Keyboard navigation\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (!isOpen) {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n setIsOpen(true)\n }\n return\n }\n\n const items = menuRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])')\n if (!items?.length) return\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n setActiveIndex((prev) => (prev + 1) % items.length)\n break\n case 'ArrowUp':\n e.preventDefault()\n setActiveIndex((prev) => (prev - 1 + items.length) % items.length)\n break\n case 'Home':\n e.preventDefault()\n setActiveIndex(0)\n break\n case 'End':\n e.preventDefault()\n setActiveIndex(items.length - 1)\n break\n case 'Escape':\n e.preventDefault()\n setIsOpen(false)\n triggerRef.current?.focus()\n break\n case 'Enter':\n case ' ':\n e.preventDefault()\n if (activeIndex >= 0) {\n items[activeIndex]?.click()\n }\n break\n }\n },\n [isOpen, activeIndex, setIsOpen]\n )\n\n // Focus active item\n useEffect(() => {\n if (!isOpen || activeIndex < 0) return\n const items = menuRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])')\n items?.[activeIndex]?.focus()\n }, [isOpen, activeIndex])\n\n const triggerProps = trigger.props as Record<string, unknown>\n const triggerElement = cloneElement(trigger as ReactElement<Record<string, unknown>>, {\n ref: triggerRef,\n 'aria-haspopup': 'menu',\n 'aria-expanded': isOpen,\n onClick: (e: React.MouseEvent) => {\n (triggerProps.onClick as ((e: React.MouseEvent) => void) | undefined)?.(e)\n setIsOpen(!isOpen)\n },\n onKeyDown: (e: KeyboardEvent) => {\n (triggerProps.onKeyDown as ((e: KeyboardEvent) => void) | undefined)?.(e)\n handleKeyDown(e)\n },\n })\n\n const menuStyle: CSSProperties = {\n position: 'absolute',\n top: position.top,\n left: placement.endsWith('end') ? 'auto' : position.left,\n right: placement.endsWith('end') ? window.innerWidth - position.left : 'auto',\n zIndex: 'var(--brycks-z-dropdown)' as unknown as number,\n minWidth: triggerRef.current?.offsetWidth ?? 160,\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 padding: 4,\n animation: 'brycks-dropdown-in 150ms ease-out',\n outline: 'none',\n ...style,\n }\n\n return (\n <DropdownContext.Provider value={{ isOpen, close: closeOnSelect ? close : () => {}, activeIndex, setActiveIndex }}>\n <div\n ref={ref}\n className={cx('brycks-dropdown', className)}\n style={{ display: 'inline-block' }}\n data-testid={testId}\n {...props}\n >\n {triggerElement}\n {isOpen && mounted && 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 <div\n ref={menuRef}\n role=\"menu\"\n style={menuStyle}\n onKeyDown={handleKeyDown}\n tabIndex={-1}\n >\n {children}\n </div>\n </>,\n document.body\n )}\n </div>\n </DropdownContext.Provider>\n )\n})\n\nDropdown.displayName = 'Dropdown'\n\n// DropdownItem\nexport interface DropdownItemProps extends HTMLAttributes<HTMLDivElement> {\n /** Whether the item is disabled */\n disabled?: boolean\n /** Icon before the label */\n icon?: ReactNode\n /** Shortcut text */\n shortcut?: string\n /** Whether the item is destructive */\n destructive?: boolean\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownItem = forwardRef<HTMLDivElement, DropdownItemProps>(function DropdownItem(\n { disabled = false, icon, shortcut, destructive = false, className, style, children, onClick, ...props },\n ref\n) {\n const { close } = useDropdownContext()\n\n const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return\n onClick?.(e)\n close()\n }\n\n const itemStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 10,\n padding: '8px 12px',\n fontSize: 14,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : destructive\n ? 'var(--brycks-error-default)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: 'transparent',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: 'background-color 100ms ease-out',\n outline: 'none',\n ...style,\n }\n\n const iconStyle: CSSProperties = {\n width: 16,\n height: 16,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : destructive\n ? 'var(--brycks-error-default)'\n : 'var(--brycks-foreground-muted)',\n flexShrink: 0,\n }\n\n const shortcutStyle: CSSProperties = {\n marginLeft: 'auto',\n fontSize: 12,\n color: 'var(--brycks-foreground-muted)',\n fontFamily: 'var(--brycks-font-mono)',\n }\n\n return (\n <div\n ref={ref}\n role=\"menuitem\"\n aria-disabled={disabled}\n tabIndex={disabled ? -1 : 0}\n className={cx('brycks-dropdown-item', disabled && 'brycks-dropdown-item--disabled', className)}\n style={itemStyle}\n onClick={handleClick}\n onMouseEnter={(e) => {\n if (!disabled) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n onFocus={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }}\n onBlur={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n {...props}\n >\n {icon && <span style={iconStyle}>{icon}</span>}\n <span style={{ flex: 1 }}>{children}</span>\n {shortcut && <span style={shortcutStyle}>{shortcut}</span>}\n </div>\n )\n})\n\nDropdownItem.displayName = 'DropdownItem'\n\n// DropdownDivider\nexport interface DropdownDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownDivider = forwardRef<HTMLDivElement, DropdownDividerProps>(function DropdownDivider(\n { className, style, ...props },\n ref\n) {\n const dividerStyle: CSSProperties = {\n height: 1,\n backgroundColor: 'var(--brycks-border-muted)',\n margin: '4px 0',\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-dropdown-divider', className)}\n style={dividerStyle}\n {...props}\n />\n )\n})\n\nDropdownDivider.displayName = 'DropdownDivider'\n\n// DropdownLabel\nexport interface DropdownLabelProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownLabel = forwardRef<HTMLDivElement, DropdownLabelProps>(function DropdownLabel(\n { className, style, children, ...props },\n ref\n) {\n const labelStyle: CSSProperties = {\n padding: '8px 12px 4px 12px',\n fontSize: 11,\n fontWeight: 600,\n color: 'var(--brycks-foreground-muted)',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n ...style,\n }\n\n return (\n <div\n ref={ref}\n className={cx('brycks-dropdown-label', className)}\n style={labelStyle}\n {...props}\n >\n {children}\n </div>\n )\n})\n\nDropdownLabel.displayName = 'DropdownLabel'\n"],"names":["DropdownContext","createContext","useDropdownContext","context","useContext","Dropdown","forwardRef","trigger","controlledIsOpen","defaultOpen","onOpenChange","placement","offset","closeOnSelect","className","style","children","testId","props","ref","internalIsOpen","setInternalIsOpen","useState","position","setPosition","activeIndex","setActiveIndex","mounted","setMounted","triggerRef","useRef","menuRef","isOpen","useEffect","setIsOpen","useCallback","open","close","updatePosition","rect","scrollX","scrollY","top","left","handleClick","e","handleKeyDown","items","prev","triggerProps","triggerElement","cloneElement","menuStyle","jsx","jsxs","cx","createPortal","Fragment","DropdownItem","disabled","icon","shortcut","destructive","onClick","itemStyle","iconStyle","shortcutStyle","DropdownDivider","dividerStyle","DropdownLabel","labelStyle"],"mappings":"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"}
1
+ {"version":3,"file":"Dropdown.cjs","sources":["../../../../src/components/navigation/Dropdown/Dropdown.tsx"],"sourcesContent":["/**\n * Dropdown Component\n *\n * A dropdown menu with keyboard navigation support.\n * Can be used for actions, navigation, or selection.\n */\n\nimport {\n forwardRef,\n useState,\n useCallback,\n useRef,\n useEffect,\n cloneElement,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type ReactElement,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\nimport { spacing, fontSizes, durations, easings } from '../../../design-system'\nimport { componentGap, componentPaddingX, componentPaddingY, iconSizes } from '../../../design-system/primitives'\n\nexport type DropdownPlacement = 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end'\n\ninterface DropdownContextValue {\n isOpen: boolean\n close: () => void\n activeIndex: number\n setActiveIndex: (index: number) => void\n}\n\nconst DropdownContext = createContext<DropdownContextValue | null>(null)\n\nfunction useDropdownContext() {\n const context = useContext(DropdownContext)\n if (!context) {\n throw new Error('Dropdown components must be used within a Dropdown')\n }\n return context\n}\n\nexport interface DropdownProps extends HTMLAttributes<HTMLDivElement> {\n /** Trigger element */\n trigger: ReactElement\n /** Whether the dropdown is open (controlled) */\n isOpen?: boolean\n /** Default open state (uncontrolled) */\n defaultOpen?: boolean\n /** Callback when open state changes */\n onOpenChange?: (isOpen: boolean) => void\n /** Dropdown placement */\n placement?: DropdownPlacement\n /** Offset from trigger */\n offset?: number\n /** Whether to close on item select */\n closeOnSelect?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(function Dropdown(\n {\n trigger,\n isOpen: controlledIsOpen,\n defaultOpen = false,\n onOpenChange,\n placement = 'bottom-start',\n offset = 4,\n closeOnSelect = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalIsOpen, setInternalIsOpen] = useState(defaultOpen)\n const [position, setPosition] = useState({ top: 0, left: 0 })\n const [activeIndex, setActiveIndex] = useState(-1)\n const [mounted, setMounted] = useState(false)\n const triggerRef = useRef<HTMLElement>(null)\n const menuRef = useRef<HTMLDivElement>(null)\n\n const isOpen = controlledIsOpen ?? internalIsOpen\n\n useEffect(() => {\n setMounted(true)\n return () => setMounted(false)\n }, [])\n\n const setIsOpen = useCallback(\n (open: boolean) => {\n if (controlledIsOpen === undefined) {\n setInternalIsOpen(open)\n }\n onOpenChange?.(open)\n if (!open) {\n setActiveIndex(-1)\n }\n },\n [controlledIsOpen, onOpenChange]\n )\n\n const close = useCallback(() => setIsOpen(false), [setIsOpen])\n\n // Calculate position\n useEffect(() => {\n if (!isOpen || !triggerRef.current || !mounted) return\n\n const updatePosition = () => {\n const trigger = triggerRef.current\n if (!trigger) return\n\n const rect = trigger.getBoundingClientRect()\n const scrollX = window.scrollX\n const scrollY = window.scrollY\n\n let top = 0\n let left = 0\n\n if (placement.startsWith('bottom')) {\n top = rect.bottom + scrollY + offset\n } else {\n top = rect.top + scrollY - offset\n }\n\n if (placement.endsWith('start')) {\n left = rect.left + scrollX\n } else {\n left = rect.right + scrollX\n }\n\n setPosition({ top, left })\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, placement, offset, mounted])\n\n // Close on outside click\n useEffect(() => {\n if (!isOpen) return\n\n const handleClick = (e: MouseEvent) => {\n if (\n triggerRef.current?.contains(e.target as Node) ||\n menuRef.current?.contains(e.target as Node)\n ) {\n return\n }\n setIsOpen(false)\n }\n\n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [isOpen, setIsOpen])\n\n // Keyboard navigation\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (!isOpen) {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n setIsOpen(true)\n }\n return\n }\n\n const items = menuRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])')\n if (!items?.length) return\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n setActiveIndex((prev) => (prev + 1) % items.length)\n break\n case 'ArrowUp':\n e.preventDefault()\n setActiveIndex((prev) => (prev - 1 + items.length) % items.length)\n break\n case 'Home':\n e.preventDefault()\n setActiveIndex(0)\n break\n case 'End':\n e.preventDefault()\n setActiveIndex(items.length - 1)\n break\n case 'Escape':\n e.preventDefault()\n setIsOpen(false)\n triggerRef.current?.focus()\n break\n case 'Enter':\n case ' ':\n e.preventDefault()\n if (activeIndex >= 0) {\n items[activeIndex]?.click()\n }\n break\n }\n },\n [isOpen, activeIndex, setIsOpen]\n )\n\n // Focus active item\n useEffect(() => {\n if (!isOpen || activeIndex < 0) return\n const items = menuRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])')\n items?.[activeIndex]?.focus()\n }, [isOpen, activeIndex])\n\n const triggerProps = trigger.props as Record<string, unknown>\n const triggerElement = cloneElement(trigger as ReactElement<Record<string, unknown>>, {\n ref: triggerRef,\n 'aria-haspopup': 'menu',\n 'aria-expanded': isOpen,\n onClick: (e: React.MouseEvent) => {\n (triggerProps.onClick as ((e: React.MouseEvent) => void) | undefined)?.(e)\n setIsOpen(!isOpen)\n },\n onKeyDown: (e: KeyboardEvent) => {\n (triggerProps.onKeyDown as ((e: KeyboardEvent) => void) | undefined)?.(e)\n handleKeyDown(e)\n },\n })\n\n const menuStyle: CSSProperties = {\n position: 'absolute',\n top: position.top,\n left: placement.endsWith('end') ? 'auto' : position.left,\n right: placement.endsWith('end') ? window.innerWidth - position.left : 'auto',\n zIndex: 'var(--brycks-z-dropdown)' as unknown as number,\n minWidth: triggerRef.current?.offsetWidth ?? 160,\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 padding: spacing[1],\n animation: `brycks-dropdown-in ${durations.quick}ms ${easings.easeOut}`,\n outline: 'none',\n ...style,\n }\n\n return (\n <DropdownContext.Provider value={{ isOpen, close: closeOnSelect ? close : () => {}, activeIndex, setActiveIndex }}>\n <div\n ref={ref}\n className={cx('brycks-dropdown', className)}\n style={{ display: 'inline-block' }}\n data-testid={testId}\n {...props}\n >\n {triggerElement}\n {isOpen && mounted && 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 <div\n ref={menuRef}\n role=\"menu\"\n style={menuStyle}\n onKeyDown={handleKeyDown}\n tabIndex={-1}\n >\n {children}\n </div>\n </>,\n document.body\n )}\n </div>\n </DropdownContext.Provider>\n )\n})\n\nDropdown.displayName = 'Dropdown'\n\n// DropdownItem\nexport interface DropdownItemProps extends HTMLAttributes<HTMLDivElement> {\n /** Whether the item is disabled */\n disabled?: boolean\n /** Icon before the label */\n icon?: ReactNode\n /** Shortcut text */\n shortcut?: string\n /** Whether the item is destructive */\n destructive?: boolean\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownItem = forwardRef<HTMLDivElement, DropdownItemProps>(function DropdownItem(\n { disabled = false, icon, shortcut, destructive = false, className, style, children, onClick, ...props },\n ref\n) {\n const { close } = useDropdownContext()\n\n const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return\n onClick?.(e)\n close()\n }\n\n const itemStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: componentGap.lg,\n padding: `${componentPaddingY.md}px ${componentPaddingX.sm}px`,\n fontSize: fontSizes.base,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : destructive\n ? 'var(--brycks-error-default)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: 'transparent',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: `background-color ${durations.fast}ms ${easings.easeOut}`,\n outline: 'none',\n ...style,\n }\n\n const iconStyle: CSSProperties = {\n width: iconSizes.sm,\n height: iconSizes.sm,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : destructive\n ? 'var(--brycks-error-default)'\n : 'var(--brycks-foreground-muted)',\n flexShrink: 0,\n }\n\n const shortcutStyle: CSSProperties = {\n marginLeft: 'auto',\n fontSize: fontSizes.sm,\n color: 'var(--brycks-foreground-muted)',\n fontFamily: 'var(--brycks-font-mono)',\n }\n\n return (\n <div\n ref={ref}\n role=\"menuitem\"\n aria-disabled={disabled}\n tabIndex={disabled ? -1 : 0}\n className={cx('brycks-dropdown-item', disabled && 'brycks-dropdown-item--disabled', className)}\n style={itemStyle}\n onClick={handleClick}\n onMouseEnter={(e) => {\n if (!disabled) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n onFocus={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }}\n onBlur={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n {...props}\n >\n {icon && <span style={iconStyle}>{icon}</span>}\n <span style={{ flex: 1 }}>{children}</span>\n {shortcut && <span style={shortcutStyle}>{shortcut}</span>}\n </div>\n )\n})\n\nDropdownItem.displayName = 'DropdownItem'\n\n// DropdownDivider\nexport interface DropdownDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownDivider = forwardRef<HTMLDivElement, DropdownDividerProps>(function DropdownDivider(\n { className, style, ...props },\n ref\n) {\n const dividerStyle: CSSProperties = {\n height: 1,\n backgroundColor: 'var(--brycks-border-muted)',\n margin: `${spacing[1]}px 0`,\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-dropdown-divider', className)}\n style={dividerStyle}\n {...props}\n />\n )\n})\n\nDropdownDivider.displayName = 'DropdownDivider'\n\n// DropdownLabel\nexport interface DropdownLabelProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownLabel = forwardRef<HTMLDivElement, DropdownLabelProps>(function DropdownLabel(\n { className, style, children, ...props },\n ref\n) {\n const labelStyle: CSSProperties = {\n padding: `${componentPaddingY.md}px ${componentPaddingX.sm}px ${spacing[1]}px ${componentPaddingX.sm}px`,\n fontSize: fontSizes.xs,\n fontWeight: 600,\n color: 'var(--brycks-foreground-muted)',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n ...style,\n }\n\n return (\n <div\n ref={ref}\n className={cx('brycks-dropdown-label', className)}\n style={labelStyle}\n {...props}\n >\n {children}\n </div>\n )\n})\n\nDropdownLabel.displayName = 'DropdownLabel'\n"],"names":["DropdownContext","createContext","useDropdownContext","context","useContext","Dropdown","forwardRef","trigger","controlledIsOpen","defaultOpen","onOpenChange","placement","offset","closeOnSelect","className","style","children","testId","props","ref","internalIsOpen","setInternalIsOpen","useState","position","setPosition","activeIndex","setActiveIndex","mounted","setMounted","triggerRef","useRef","menuRef","isOpen","useEffect","setIsOpen","useCallback","open","close","updatePosition","rect","scrollX","scrollY","top","left","handleClick","e","handleKeyDown","items","prev","triggerProps","triggerElement","cloneElement","menuStyle","spacing","durations","easings","jsx","jsxs","cx","createPortal","Fragment","DropdownItem","disabled","icon","shortcut","destructive","onClick","itemStyle","componentGap","componentPaddingY","componentPaddingX","fontSizes","iconStyle","iconSizes","shortcutStyle","DropdownDivider","dividerStyle","DropdownLabel","labelStyle"],"mappings":"uaAoCMA,EAAkBC,EAAAA,cAA2C,IAAI,EAEvE,SAASC,GAAqB,CAC5B,MAAMC,EAAUC,EAAAA,WAAWJ,CAAe,EAC1C,GAAI,CAACG,EACH,MAAM,IAAI,MAAM,oDAAoD,EAEtE,OAAOA,CACT,CAuBO,MAAME,EAAWC,EAAAA,WAA0C,SAChE,CACE,QAAAC,EACA,OAAQC,EACR,YAAAC,EAAc,GACd,aAAAC,EACA,UAAAC,EAAY,eACZ,OAAAC,EAAS,EACT,cAAAC,EAAgB,GAChB,UAAAC,EACA,MAAAC,EACA,SAAAC,EACA,OAAAC,EACA,GAAGC,CACL,EACAC,EACA,CACA,KAAM,CAACC,EAAgBC,CAAiB,EAAIC,EAAAA,SAASb,CAAW,EAC1D,CAACc,EAAUC,CAAW,EAAIF,EAAAA,SAAS,CAAE,IAAK,EAAG,KAAM,EAAG,EACtD,CAACG,EAAaC,CAAc,EAAIJ,EAAAA,SAAS,EAAE,EAC3C,CAACK,EAASC,CAAU,EAAIN,EAAAA,SAAS,EAAK,EACtCO,EAAaC,EAAAA,OAAoB,IAAI,EACrCC,EAAUD,EAAAA,OAAuB,IAAI,EAErCE,EAASxB,GAAoBY,EAEnCa,EAAAA,UAAU,KACRL,EAAW,EAAI,EACR,IAAMA,EAAW,EAAK,GAC5B,CAAA,CAAE,EAEL,MAAMM,EAAYC,EAAAA,YACfC,GAAkB,CACb5B,IAAqB,QACvBa,EAAkBe,CAAI,EAExB1B,IAAe0B,CAAI,EACdA,GACHV,EAAe,EAAE,CAErB,EACA,CAAClB,EAAkBE,CAAY,CAAA,EAG3B2B,EAAQF,EAAAA,YAAY,IAAMD,EAAU,EAAK,EAAG,CAACA,CAAS,CAAC,EAG7DD,EAAAA,UAAU,IAAM,CACd,GAAI,CAACD,GAAU,CAACH,EAAW,SAAW,CAACF,EAAS,OAEhD,MAAMW,EAAiB,IAAM,CAC3B,MAAM/B,EAAUsB,EAAW,QAC3B,GAAI,CAACtB,EAAS,OAEd,MAAMgC,EAAOhC,EAAQ,sBAAA,EACfiC,EAAU,OAAO,QACjBC,EAAU,OAAO,QAEvB,IAAIC,EAAM,EACNC,EAAO,EAEPhC,EAAU,WAAW,QAAQ,EAC/B+B,EAAMH,EAAK,OAASE,EAAU7B,EAE9B8B,EAAMH,EAAK,IAAME,EAAU7B,EAGzBD,EAAU,SAAS,OAAO,EAC5BgC,EAAOJ,EAAK,KAAOC,EAEnBG,EAAOJ,EAAK,MAAQC,EAGtBhB,EAAY,CAAE,IAAAkB,EAAK,KAAAC,EAAM,CAC3B,EAEA,OAAAL,EAAA,EACA,OAAO,iBAAiB,SAAUA,CAAc,EAChD,OAAO,iBAAiB,SAAUA,EAAgB,EAAI,EAE/C,IAAM,CACX,OAAO,oBAAoB,SAAUA,CAAc,EACnD,OAAO,oBAAoB,SAAUA,EAAgB,EAAI,CAC3D,CACF,EAAG,CAACN,EAAQrB,EAAWC,EAAQe,CAAO,CAAC,EAGvCM,EAAAA,UAAU,IAAM,CACd,GAAI,CAACD,EAAQ,OAEb,MAAMY,EAAeC,GAAkB,CAEnChB,EAAW,SAAS,SAASgB,EAAE,MAAc,GAC7Cd,EAAQ,SAAS,SAASc,EAAE,MAAc,GAI5CX,EAAU,EAAK,CACjB,EAEA,gBAAS,iBAAiB,YAAaU,CAAW,EAC3C,IAAM,SAAS,oBAAoB,YAAaA,CAAW,CACpE,EAAG,CAACZ,EAAQE,CAAS,CAAC,EAGtB,MAAMY,EAAgBX,EAAAA,YACnB,GAAqB,CACpB,GAAI,CAACH,EAAQ,EACP,EAAE,MAAQ,aAAe,EAAE,MAAQ,SAAW,EAAE,MAAQ,OAC1D,EAAE,eAAA,EACFE,EAAU,EAAI,GAEhB,MACF,CAEA,MAAMa,EAAQhB,EAAQ,SAAS,iBAA8B,+CAA+C,EAC5G,GAAKgB,GAAO,OAEZ,OAAQ,EAAE,IAAA,CACR,IAAK,YACH,EAAE,eAAA,EACFrB,EAAgBsB,IAAUA,EAAO,GAAKD,EAAM,MAAM,EAClD,MACF,IAAK,UACH,EAAE,eAAA,EACFrB,EAAgBsB,IAAUA,EAAO,EAAID,EAAM,QAAUA,EAAM,MAAM,EACjE,MACF,IAAK,OACH,EAAE,eAAA,EACFrB,EAAe,CAAC,EAChB,MACF,IAAK,MACH,EAAE,eAAA,EACFA,EAAeqB,EAAM,OAAS,CAAC,EAC/B,MACF,IAAK,SACH,EAAE,eAAA,EACFb,EAAU,EAAK,EACfL,EAAW,SAAS,MAAA,EACpB,MACF,IAAK,QACL,IAAK,IACH,EAAE,eAAA,EACEJ,GAAe,GACjBsB,EAAMtB,CAAW,GAAG,MAAA,EAEtB,KAAA,CAEN,EACA,CAACO,EAAQP,EAAaS,CAAS,CAAA,EAIjCD,EAAAA,UAAU,IAAM,CACd,GAAI,CAACD,GAAUP,EAAc,EAAG,OAClBM,EAAQ,SAAS,iBAA8B,+CAA+C,IACpGN,CAAW,GAAG,MAAA,CACxB,EAAG,CAACO,EAAQP,CAAW,CAAC,EAExB,MAAMwB,EAAe1C,EAAQ,MACvB2C,EAAiBC,EAAAA,aAAa5C,EAAkD,CACpF,IAAKsB,EACL,gBAAiB,OACjB,gBAAiBG,EACjB,QAAU,GAAwB,CAC/BiB,EAAa,UAA0D,CAAC,EACzEf,EAAU,CAACF,CAAM,CACnB,EACA,UAAY,GAAqB,CAC9BiB,EAAa,YAAyD,CAAC,EACxEH,EAAc,CAAC,CACjB,CAAA,CACD,EAEKM,EAA2B,CAC/B,SAAU,WACV,IAAK7B,EAAS,IACd,KAAMZ,EAAU,SAAS,KAAK,EAAI,OAASY,EAAS,KACpD,MAAOZ,EAAU,SAAS,KAAK,EAAI,OAAO,WAAaY,EAAS,KAAO,OACvE,OAAQ,2BACR,SAAUM,EAAW,SAAS,aAAe,IAC7C,gBAAiB,oCACjB,OAAQ,yCACR,aAAc,0BACd,UAAW,0BACX,QAASwB,EAAAA,QAAQ,CAAC,EAClB,UAAW,sBAAsBC,EAAAA,UAAU,KAAK,MAAMC,EAAAA,QAAQ,OAAO,GACrE,QAAS,OACT,GAAGxC,CAAA,EAGL,OACEyC,EAAAA,IAACxD,EAAgB,SAAhB,CAAyB,MAAO,CAAE,OAAAgC,EAAQ,MAAOnB,EAAgBwB,EAAQ,IAAM,CAAC,EAAG,YAAAZ,EAAa,eAAAC,CAAA,EAC/F,SAAA+B,EAAAA,KAAC,MAAA,CACC,IAAAtC,EACA,UAAWuC,EAAAA,GAAG,kBAAmB5C,CAAS,EAC1C,MAAO,CAAE,QAAS,cAAA,EAClB,cAAaG,EACZ,GAAGC,EAEH,SAAA,CAAAgC,EACAlB,GAAUL,GAAWgC,EAAAA,aACpBF,OAAAG,EAAAA,SAAA,CACE,SAAA,CAAAJ,MAAC,QAAA,CACE,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gBAMH,EACAA,EAAAA,IAAC,MAAA,CACC,IAAKzB,EACL,KAAK,OACL,MAAOqB,EACP,UAAWN,EACX,SAAU,GAET,SAAA9B,CAAA,CAAA,CACH,EACF,EACA,SAAS,IAAA,CACX,CAAA,CAAA,EAEJ,CAEJ,CAAC,EAEDX,EAAS,YAAc,WAgBhB,MAAMwD,EAAevD,EAAAA,WAA8C,SACxE,CAAE,SAAAwD,EAAW,GAAO,KAAAC,EAAM,SAAAC,EAAU,YAAAC,EAAc,GAAO,UAAAnD,EAAW,MAAAC,EAAO,SAAAC,EAAU,QAAAkD,EAAS,GAAGhD,CAAA,EACjGC,EACA,CACA,KAAM,CAAE,MAAAkB,CAAA,EAAUnC,EAAA,EAEZ0C,EAAeC,GAAwC,CACvDiB,IACJI,IAAUrB,CAAC,EACXR,EAAA,EACF,EAEM8B,EAA2B,CAC/B,QAAS,OACT,WAAY,SACZ,IAAKC,EAAAA,aAAa,GAClB,QAAS,GAAGC,oBAAkB,EAAE,MAAMC,EAAAA,kBAAkB,EAAE,KAC1D,SAAUC,EAAAA,UAAU,KACpB,MAAOT,EACH,oCACAG,EACA,8BACA,mCACJ,gBAAiB,cACjB,aAAc,0BACd,OAAQH,EAAW,cAAgB,UACnC,WAAY,oBAAoBR,EAAAA,UAAU,IAAI,MAAMC,EAAAA,QAAQ,OAAO,GACnE,QAAS,OACT,GAAGxC,CAAA,EAGCyD,EAA2B,CAC/B,MAAOC,EAAAA,UAAU,GACjB,OAAQA,EAAAA,UAAU,GAClB,MAAOX,EACH,oCACAG,EACA,8BACA,iCACJ,WAAY,CAAA,EAGRS,EAA+B,CACnC,WAAY,OACZ,SAAUH,EAAAA,UAAU,GACpB,MAAO,iCACP,WAAY,yBAAA,EAGd,OACEd,EAAAA,KAAC,MAAA,CACC,IAAAtC,EACA,KAAK,WACL,gBAAe2C,EACf,SAAUA,EAAW,GAAK,EAC1B,UAAWJ,EAAAA,GAAG,uBAAwBI,GAAY,iCAAkChD,CAAS,EAC7F,MAAOqD,EACP,QAASvB,EACT,aAAeC,GAAM,CACdiB,IACHjB,EAAE,cAAc,MAAM,gBAAkB,iCAE5C,EACA,aAAeA,GAAM,CACnBA,EAAE,cAAc,MAAM,gBAAkB,aAC1C,EACA,QAAUA,GAAM,CACdA,EAAE,cAAc,MAAM,gBAAkB,gCAC1C,EACA,OAASA,GAAM,CACbA,EAAE,cAAc,MAAM,gBAAkB,aAC1C,EACC,GAAG3B,EAEH,SAAA,CAAA6C,GAAQP,EAAAA,IAAC,OAAA,CAAK,MAAOgB,EAAY,SAAAT,EAAK,QACtC,OAAA,CAAK,MAAO,CAAE,KAAM,CAAA,EAAM,SAAA/C,EAAS,EACnCgD,GAAYR,EAAAA,IAAC,OAAA,CAAK,MAAOkB,EAAgB,SAAAV,CAAA,CAAS,CAAA,CAAA,CAAA,CAGzD,CAAC,EAEDH,EAAa,YAAc,eAQpB,MAAMc,EAAkBrE,EAAAA,WAAiD,SAC9E,CAAE,UAAAQ,EAAW,MAAAC,EAAO,GAAGG,CAAA,EACvBC,EACA,CACA,MAAMyD,EAA8B,CAClC,OAAQ,EACR,gBAAiB,6BACjB,OAAQ,GAAGvB,EAAAA,QAAQ,CAAC,CAAC,OACrB,GAAGtC,CAAA,EAGL,OACEyC,EAAAA,IAAC,MAAA,CACC,IAAArC,EACA,KAAK,YACL,UAAWuC,EAAAA,GAAG,0BAA2B5C,CAAS,EAClD,MAAO8D,EACN,GAAG1D,CAAA,CAAA,CAGV,CAAC,EAEDyD,EAAgB,YAAc,kBAQvB,MAAME,EAAgBvE,EAAAA,WAA+C,SAC1E,CAAE,UAAAQ,EAAW,MAAAC,EAAO,SAAAC,EAAU,GAAGE,CAAA,EACjCC,EACA,CACA,MAAM2D,EAA4B,CAChC,QAAS,GAAGT,EAAAA,kBAAkB,EAAE,MAAMC,EAAAA,kBAAkB,EAAE,MAAMjB,EAAAA,QAAQ,CAAC,CAAC,MAAMiB,EAAAA,kBAAkB,EAAE,KACpG,SAAUC,EAAAA,UAAU,GACpB,WAAY,IACZ,MAAO,iCACP,cAAe,YACf,cAAe,SACf,GAAGxD,CAAA,EAGL,OACEyC,EAAAA,IAAC,MAAA,CACC,IAAArC,EACA,UAAWuC,EAAAA,GAAG,wBAAyB5C,CAAS,EAChD,MAAOgE,EACN,GAAG5D,EAEH,SAAAF,CAAA,CAAA,CAGP,CAAC,EAED6D,EAAc,YAAc"}
@@ -1,69 +1,73 @@
1
- import { jsx as u, jsxs as T, Fragment as G } from "react/jsx-runtime";
2
- import { forwardRef as v, useState as k, useRef as q, useEffect as g, useCallback as P, cloneElement as J, createContext as Q, useContext as V } from "react";
3
- import { createPortal as Z } from "react-dom";
1
+ import { jsx as u, jsxs as A, Fragment as ee } from "react/jsx-runtime";
2
+ import { forwardRef as v, useState as k, useRef as j, useEffect as g, useCallback as R, cloneElement as re, createContext as te, useContext as oe } from "react";
3
+ import { createPortal as ne } from "react-dom";
4
4
  import { cx as h } from "../../../utils/styles.js";
5
- const B = Q(null);
6
- function _() {
7
- const m = V(B);
8
- if (!m)
5
+ import { componentPaddingY as G, componentPaddingX as T, componentGap as se, iconSizes as B } from "../../../design-system/primitives/sizing.js";
6
+ import { durations as H, easings as U } from "../../../design-system/tokens/motion.js";
7
+ import { spacing as K } from "../../../design-system/tokens/spacing.js";
8
+ import { fontSizes as Y } from "../../../design-system/tokens/typography.js";
9
+ const J = te(null);
10
+ function ae() {
11
+ const f = oe(J);
12
+ if (!f)
9
13
  throw new Error("Dropdown components must be used within a Dropdown");
10
- return m;
14
+ return f;
11
15
  }
12
- const $ = v(function({
16
+ const ie = v(function({
13
17
  trigger: r,
14
18
  isOpen: a,
15
- defaultOpen: l = !1,
19
+ defaultOpen: c = !1,
16
20
  onOpenChange: i,
17
21
  placement: s = "bottom-start",
18
22
  offset: p = 4,
19
23
  closeOnSelect: D = !0,
20
24
  className: x,
21
25
  style: C,
22
- children: E,
23
- testId: S,
26
+ children: S,
27
+ testId: E,
24
28
  ...I
25
29
  }, L) {
26
- const [N, W] = k(l), [t, O] = k({ top: 0, left: 0 }), [f, y] = k(-1), [z, K] = k(!1), b = q(null), w = q(null), o = a ?? N;
27
- g(() => (K(!0), () => K(!1)), []);
28
- const c = P(
30
+ const [$, z] = k(c), [t, Q] = k({ top: 0, left: 0 }), [m, y] = k(-1), [N, O] = k(!1), b = j(null), w = j(null), o = a ?? $;
31
+ g(() => (O(!0), () => O(!1)), []);
32
+ const d = R(
29
33
  (e) => {
30
- a === void 0 && W(e), i?.(e), e || y(-1);
34
+ a === void 0 && z(e), i?.(e), e || y(-1);
31
35
  },
32
36
  [a, i]
33
- ), X = P(() => c(!1), [c]);
37
+ ), V = R(() => d(!1), [d]);
34
38
  g(() => {
35
- if (!o || !b.current || !z) return;
39
+ if (!o || !b.current || !N) return;
36
40
  const e = () => {
37
41
  const n = b.current;
38
42
  if (!n) return;
39
- const d = n.getBoundingClientRect(), M = window.scrollX, j = window.scrollY;
40
- let R = 0, A = 0;
41
- s.startsWith("bottom") ? R = d.bottom + j + p : R = d.top + j - p, s.endsWith("start") ? A = d.left + M : A = d.right + M, O({ top: R, left: A });
43
+ const l = n.getBoundingClientRect(), M = window.scrollX, X = window.scrollY;
44
+ let W = 0, P = 0;
45
+ s.startsWith("bottom") ? W = l.bottom + X + p : W = l.top + X - p, s.endsWith("start") ? P = l.left + M : P = l.right + M, Q({ top: W, left: P });
42
46
  };
43
47
  return e(), window.addEventListener("resize", e), window.addEventListener("scroll", e, !0), () => {
44
48
  window.removeEventListener("resize", e), window.removeEventListener("scroll", e, !0);
45
49
  };
46
- }, [o, s, p, z]), g(() => {
50
+ }, [o, s, p, N]), g(() => {
47
51
  if (!o) return;
48
52
  const e = (n) => {
49
- b.current?.contains(n.target) || w.current?.contains(n.target) || c(!1);
53
+ b.current?.contains(n.target) || w.current?.contains(n.target) || d(!1);
50
54
  };
51
55
  return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
52
- }, [o, c]);
53
- const Y = P(
56
+ }, [o, d]);
57
+ const q = R(
54
58
  (e) => {
55
59
  if (!o) {
56
- (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") && (e.preventDefault(), c(!0));
60
+ (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") && (e.preventDefault(), d(!0));
57
61
  return;
58
62
  }
59
63
  const n = w.current?.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])');
60
64
  if (n?.length)
61
65
  switch (e.key) {
62
66
  case "ArrowDown":
63
- e.preventDefault(), y((d) => (d + 1) % n.length);
67
+ e.preventDefault(), y((l) => (l + 1) % n.length);
64
68
  break;
65
69
  case "ArrowUp":
66
- e.preventDefault(), y((d) => (d - 1 + n.length) % n.length);
70
+ e.preventDefault(), y((l) => (l - 1 + n.length) % n.length);
67
71
  break;
68
72
  case "Home":
69
73
  e.preventDefault(), y(0);
@@ -72,31 +76,31 @@ const $ = v(function({
72
76
  e.preventDefault(), y(n.length - 1);
73
77
  break;
74
78
  case "Escape":
75
- e.preventDefault(), c(!1), b.current?.focus();
79
+ e.preventDefault(), d(!1), b.current?.focus();
76
80
  break;
77
81
  case "Enter":
78
82
  case " ":
79
- e.preventDefault(), f >= 0 && n[f]?.click();
83
+ e.preventDefault(), m >= 0 && n[m]?.click();
80
84
  break;
81
85
  }
82
86
  },
83
- [o, f, c]
87
+ [o, m, d]
84
88
  );
85
89
  g(() => {
86
- if (!o || f < 0) return;
87
- w.current?.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')?.[f]?.focus();
88
- }, [o, f]);
89
- const F = r.props, H = J(r, {
90
+ if (!o || m < 0) return;
91
+ w.current?.querySelectorAll('[role="menuitem"]:not([aria-disabled="true"])')?.[m]?.focus();
92
+ }, [o, m]);
93
+ const F = r.props, Z = re(r, {
90
94
  ref: b,
91
95
  "aria-haspopup": "menu",
92
96
  "aria-expanded": o,
93
97
  onClick: (e) => {
94
- F.onClick?.(e), c(!o);
98
+ F.onClick?.(e), d(!o);
95
99
  },
96
100
  onKeyDown: (e) => {
97
- F.onKeyDown?.(e), Y(e);
101
+ F.onKeyDown?.(e), q(e);
98
102
  }
99
- }), U = {
103
+ }), _ = {
100
104
  position: "absolute",
101
105
  top: t.top,
102
106
  left: s.endsWith("end") ? "auto" : t.left,
@@ -107,24 +111,24 @@ const $ = v(function({
107
111
  border: "1px solid var(--brycks-border-default)",
108
112
  borderRadius: "var(--brycks-radius-lg)",
109
113
  boxShadow: "var(--brycks-shadow-lg)",
110
- padding: 4,
111
- animation: "brycks-dropdown-in 150ms ease-out",
114
+ padding: K[1],
115
+ animation: `brycks-dropdown-in ${H.quick}ms ${U.easeOut}`,
112
116
  outline: "none",
113
117
  ...C
114
118
  };
115
- return /* @__PURE__ */ u(B.Provider, { value: { isOpen: o, close: D ? X : () => {
116
- }, activeIndex: f, setActiveIndex: y }, children: /* @__PURE__ */ T(
119
+ return /* @__PURE__ */ u(J.Provider, { value: { isOpen: o, close: D ? V : () => {
120
+ }, activeIndex: m, setActiveIndex: y }, children: /* @__PURE__ */ A(
117
121
  "div",
118
122
  {
119
123
  ref: L,
120
124
  className: h("brycks-dropdown", x),
121
125
  style: { display: "inline-block" },
122
- "data-testid": S,
126
+ "data-testid": E,
123
127
  ...I,
124
128
  children: [
125
- H,
126
- o && z && Z(
127
- /* @__PURE__ */ T(G, { children: [
129
+ Z,
130
+ o && N && ne(
131
+ /* @__PURE__ */ A(ee, { children: [
128
132
  /* @__PURE__ */ u("style", { children: `
129
133
  @keyframes brycks-dropdown-in {
130
134
  from { opacity: 0; transform: translateY(-4px); }
@@ -136,10 +140,10 @@ const $ = v(function({
136
140
  {
137
141
  ref: w,
138
142
  role: "menu",
139
- style: U,
140
- onKeyDown: Y,
143
+ style: _,
144
+ onKeyDown: q,
141
145
  tabIndex: -1,
142
- children: E
146
+ children: S
143
147
  }
144
148
  )
145
149
  ] }),
@@ -149,38 +153,38 @@ const $ = v(function({
149
153
  }
150
154
  ) });
151
155
  });
152
- $.displayName = "Dropdown";
153
- const ee = v(function({ disabled: r = !1, icon: a, shortcut: l, destructive: i = !1, className: s, style: p, children: D, onClick: x, ...C }, E) {
154
- const { close: S } = _(), I = (t) => {
155
- r || (x?.(t), S());
156
+ ie.displayName = "Dropdown";
157
+ const ce = v(function({ disabled: r = !1, icon: a, shortcut: c, destructive: i = !1, className: s, style: p, children: D, onClick: x, ...C }, S) {
158
+ const { close: E } = ae(), I = (t) => {
159
+ r || (x?.(t), E());
156
160
  }, L = {
157
161
  display: "flex",
158
162
  alignItems: "center",
159
- gap: 10,
160
- padding: "8px 12px",
161
- fontSize: 14,
163
+ gap: se.lg,
164
+ padding: `${G.md}px ${T.sm}px`,
165
+ fontSize: Y.base,
162
166
  color: r ? "var(--brycks-foreground-disabled)" : i ? "var(--brycks-error-default)" : "var(--brycks-foreground-default)",
163
167
  backgroundColor: "transparent",
164
168
  borderRadius: "var(--brycks-radius-md)",
165
169
  cursor: r ? "not-allowed" : "pointer",
166
- transition: "background-color 100ms ease-out",
170
+ transition: `background-color ${H.fast}ms ${U.easeOut}`,
167
171
  outline: "none",
168
172
  ...p
169
- }, N = {
170
- width: 16,
171
- height: 16,
173
+ }, $ = {
174
+ width: B.sm,
175
+ height: B.sm,
172
176
  color: r ? "var(--brycks-foreground-disabled)" : i ? "var(--brycks-error-default)" : "var(--brycks-foreground-muted)",
173
177
  flexShrink: 0
174
- }, W = {
178
+ }, z = {
175
179
  marginLeft: "auto",
176
- fontSize: 12,
180
+ fontSize: Y.sm,
177
181
  color: "var(--brycks-foreground-muted)",
178
182
  fontFamily: "var(--brycks-font-mono)"
179
183
  };
180
- return /* @__PURE__ */ T(
184
+ return /* @__PURE__ */ A(
181
185
  "div",
182
186
  {
183
- ref: E,
187
+ ref: S,
184
188
  role: "menuitem",
185
189
  "aria-disabled": r,
186
190
  tabIndex: r ? -1 : 0,
@@ -201,19 +205,19 @@ const ee = v(function({ disabled: r = !1, icon: a, shortcut: l, destructive: i =
201
205
  },
202
206
  ...C,
203
207
  children: [
204
- a && /* @__PURE__ */ u("span", { style: N, children: a }),
208
+ a && /* @__PURE__ */ u("span", { style: $, children: a }),
205
209
  /* @__PURE__ */ u("span", { style: { flex: 1 }, children: D }),
206
- l && /* @__PURE__ */ u("span", { style: W, children: l })
210
+ c && /* @__PURE__ */ u("span", { style: z, children: c })
207
211
  ]
208
212
  }
209
213
  );
210
214
  });
211
- ee.displayName = "DropdownItem";
212
- const re = v(function({ className: r, style: a, ...l }, i) {
215
+ ce.displayName = "DropdownItem";
216
+ const de = v(function({ className: r, style: a, ...c }, i) {
213
217
  const s = {
214
218
  height: 1,
215
219
  backgroundColor: "var(--brycks-border-muted)",
216
- margin: "4px 0",
220
+ margin: `${K[1]}px 0`,
217
221
  ...a
218
222
  };
219
223
  return /* @__PURE__ */ u(
@@ -223,15 +227,15 @@ const re = v(function({ className: r, style: a, ...l }, i) {
223
227
  role: "separator",
224
228
  className: h("brycks-dropdown-divider", r),
225
229
  style: s,
226
- ...l
230
+ ...c
227
231
  }
228
232
  );
229
233
  });
230
- re.displayName = "DropdownDivider";
231
- const te = v(function({ className: r, style: a, children: l, ...i }, s) {
234
+ de.displayName = "DropdownDivider";
235
+ const le = v(function({ className: r, style: a, children: c, ...i }, s) {
232
236
  const p = {
233
- padding: "8px 12px 4px 12px",
234
- fontSize: 11,
237
+ padding: `${G.md}px ${T.sm}px ${K[1]}px ${T.sm}px`,
238
+ fontSize: Y.xs,
235
239
  fontWeight: 600,
236
240
  color: "var(--brycks-foreground-muted)",
237
241
  textTransform: "uppercase",
@@ -245,15 +249,15 @@ const te = v(function({ className: r, style: a, children: l, ...i }, s) {
245
249
  className: h("brycks-dropdown-label", r),
246
250
  style: p,
247
251
  ...i,
248
- children: l
252
+ children: c
249
253
  }
250
254
  );
251
255
  });
252
- te.displayName = "DropdownLabel";
256
+ le.displayName = "DropdownLabel";
253
257
  export {
254
- $ as Dropdown,
255
- re as DropdownDivider,
256
- ee as DropdownItem,
257
- te as DropdownLabel
258
+ ie as Dropdown,
259
+ de as DropdownDivider,
260
+ ce as DropdownItem,
261
+ le as DropdownLabel
258
262
  };
259
263
  //# sourceMappingURL=Dropdown.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Dropdown.js","sources":["../../../../src/components/navigation/Dropdown/Dropdown.tsx"],"sourcesContent":["/**\n * Dropdown Component\n *\n * A dropdown menu with keyboard navigation support.\n * Can be used for actions, navigation, or selection.\n */\n\nimport {\n forwardRef,\n useState,\n useCallback,\n useRef,\n useEffect,\n cloneElement,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type ReactElement,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\n\nexport type DropdownPlacement = 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end'\n\ninterface DropdownContextValue {\n isOpen: boolean\n close: () => void\n activeIndex: number\n setActiveIndex: (index: number) => void\n}\n\nconst DropdownContext = createContext<DropdownContextValue | null>(null)\n\nfunction useDropdownContext() {\n const context = useContext(DropdownContext)\n if (!context) {\n throw new Error('Dropdown components must be used within a Dropdown')\n }\n return context\n}\n\nexport interface DropdownProps extends HTMLAttributes<HTMLDivElement> {\n /** Trigger element */\n trigger: ReactElement\n /** Whether the dropdown is open (controlled) */\n isOpen?: boolean\n /** Default open state (uncontrolled) */\n defaultOpen?: boolean\n /** Callback when open state changes */\n onOpenChange?: (isOpen: boolean) => void\n /** Dropdown placement */\n placement?: DropdownPlacement\n /** Offset from trigger */\n offset?: number\n /** Whether to close on item select */\n closeOnSelect?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(function Dropdown(\n {\n trigger,\n isOpen: controlledIsOpen,\n defaultOpen = false,\n onOpenChange,\n placement = 'bottom-start',\n offset = 4,\n closeOnSelect = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalIsOpen, setInternalIsOpen] = useState(defaultOpen)\n const [position, setPosition] = useState({ top: 0, left: 0 })\n const [activeIndex, setActiveIndex] = useState(-1)\n const [mounted, setMounted] = useState(false)\n const triggerRef = useRef<HTMLElement>(null)\n const menuRef = useRef<HTMLDivElement>(null)\n\n const isOpen = controlledIsOpen ?? internalIsOpen\n\n useEffect(() => {\n setMounted(true)\n return () => setMounted(false)\n }, [])\n\n const setIsOpen = useCallback(\n (open: boolean) => {\n if (controlledIsOpen === undefined) {\n setInternalIsOpen(open)\n }\n onOpenChange?.(open)\n if (!open) {\n setActiveIndex(-1)\n }\n },\n [controlledIsOpen, onOpenChange]\n )\n\n const close = useCallback(() => setIsOpen(false), [setIsOpen])\n\n // Calculate position\n useEffect(() => {\n if (!isOpen || !triggerRef.current || !mounted) return\n\n const updatePosition = () => {\n const trigger = triggerRef.current\n if (!trigger) return\n\n const rect = trigger.getBoundingClientRect()\n const scrollX = window.scrollX\n const scrollY = window.scrollY\n\n let top = 0\n let left = 0\n\n if (placement.startsWith('bottom')) {\n top = rect.bottom + scrollY + offset\n } else {\n top = rect.top + scrollY - offset\n }\n\n if (placement.endsWith('start')) {\n left = rect.left + scrollX\n } else {\n left = rect.right + scrollX\n }\n\n setPosition({ top, left })\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, placement, offset, mounted])\n\n // Close on outside click\n useEffect(() => {\n if (!isOpen) return\n\n const handleClick = (e: MouseEvent) => {\n if (\n triggerRef.current?.contains(e.target as Node) ||\n menuRef.current?.contains(e.target as Node)\n ) {\n return\n }\n setIsOpen(false)\n }\n\n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [isOpen, setIsOpen])\n\n // Keyboard navigation\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (!isOpen) {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n setIsOpen(true)\n }\n return\n }\n\n const items = menuRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])')\n if (!items?.length) return\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n setActiveIndex((prev) => (prev + 1) % items.length)\n break\n case 'ArrowUp':\n e.preventDefault()\n setActiveIndex((prev) => (prev - 1 + items.length) % items.length)\n break\n case 'Home':\n e.preventDefault()\n setActiveIndex(0)\n break\n case 'End':\n e.preventDefault()\n setActiveIndex(items.length - 1)\n break\n case 'Escape':\n e.preventDefault()\n setIsOpen(false)\n triggerRef.current?.focus()\n break\n case 'Enter':\n case ' ':\n e.preventDefault()\n if (activeIndex >= 0) {\n items[activeIndex]?.click()\n }\n break\n }\n },\n [isOpen, activeIndex, setIsOpen]\n )\n\n // Focus active item\n useEffect(() => {\n if (!isOpen || activeIndex < 0) return\n const items = menuRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])')\n items?.[activeIndex]?.focus()\n }, [isOpen, activeIndex])\n\n const triggerProps = trigger.props as Record<string, unknown>\n const triggerElement = cloneElement(trigger as ReactElement<Record<string, unknown>>, {\n ref: triggerRef,\n 'aria-haspopup': 'menu',\n 'aria-expanded': isOpen,\n onClick: (e: React.MouseEvent) => {\n (triggerProps.onClick as ((e: React.MouseEvent) => void) | undefined)?.(e)\n setIsOpen(!isOpen)\n },\n onKeyDown: (e: KeyboardEvent) => {\n (triggerProps.onKeyDown as ((e: KeyboardEvent) => void) | undefined)?.(e)\n handleKeyDown(e)\n },\n })\n\n const menuStyle: CSSProperties = {\n position: 'absolute',\n top: position.top,\n left: placement.endsWith('end') ? 'auto' : position.left,\n right: placement.endsWith('end') ? window.innerWidth - position.left : 'auto',\n zIndex: 'var(--brycks-z-dropdown)' as unknown as number,\n minWidth: triggerRef.current?.offsetWidth ?? 160,\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 padding: 4,\n animation: 'brycks-dropdown-in 150ms ease-out',\n outline: 'none',\n ...style,\n }\n\n return (\n <DropdownContext.Provider value={{ isOpen, close: closeOnSelect ? close : () => {}, activeIndex, setActiveIndex }}>\n <div\n ref={ref}\n className={cx('brycks-dropdown', className)}\n style={{ display: 'inline-block' }}\n data-testid={testId}\n {...props}\n >\n {triggerElement}\n {isOpen && mounted && 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 <div\n ref={menuRef}\n role=\"menu\"\n style={menuStyle}\n onKeyDown={handleKeyDown}\n tabIndex={-1}\n >\n {children}\n </div>\n </>,\n document.body\n )}\n </div>\n </DropdownContext.Provider>\n )\n})\n\nDropdown.displayName = 'Dropdown'\n\n// DropdownItem\nexport interface DropdownItemProps extends HTMLAttributes<HTMLDivElement> {\n /** Whether the item is disabled */\n disabled?: boolean\n /** Icon before the label */\n icon?: ReactNode\n /** Shortcut text */\n shortcut?: string\n /** Whether the item is destructive */\n destructive?: boolean\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownItem = forwardRef<HTMLDivElement, DropdownItemProps>(function DropdownItem(\n { disabled = false, icon, shortcut, destructive = false, className, style, children, onClick, ...props },\n ref\n) {\n const { close } = useDropdownContext()\n\n const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return\n onClick?.(e)\n close()\n }\n\n const itemStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: 10,\n padding: '8px 12px',\n fontSize: 14,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : destructive\n ? 'var(--brycks-error-default)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: 'transparent',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: 'background-color 100ms ease-out',\n outline: 'none',\n ...style,\n }\n\n const iconStyle: CSSProperties = {\n width: 16,\n height: 16,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : destructive\n ? 'var(--brycks-error-default)'\n : 'var(--brycks-foreground-muted)',\n flexShrink: 0,\n }\n\n const shortcutStyle: CSSProperties = {\n marginLeft: 'auto',\n fontSize: 12,\n color: 'var(--brycks-foreground-muted)',\n fontFamily: 'var(--brycks-font-mono)',\n }\n\n return (\n <div\n ref={ref}\n role=\"menuitem\"\n aria-disabled={disabled}\n tabIndex={disabled ? -1 : 0}\n className={cx('brycks-dropdown-item', disabled && 'brycks-dropdown-item--disabled', className)}\n style={itemStyle}\n onClick={handleClick}\n onMouseEnter={(e) => {\n if (!disabled) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n onFocus={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }}\n onBlur={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n {...props}\n >\n {icon && <span style={iconStyle}>{icon}</span>}\n <span style={{ flex: 1 }}>{children}</span>\n {shortcut && <span style={shortcutStyle}>{shortcut}</span>}\n </div>\n )\n})\n\nDropdownItem.displayName = 'DropdownItem'\n\n// DropdownDivider\nexport interface DropdownDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownDivider = forwardRef<HTMLDivElement, DropdownDividerProps>(function DropdownDivider(\n { className, style, ...props },\n ref\n) {\n const dividerStyle: CSSProperties = {\n height: 1,\n backgroundColor: 'var(--brycks-border-muted)',\n margin: '4px 0',\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-dropdown-divider', className)}\n style={dividerStyle}\n {...props}\n />\n )\n})\n\nDropdownDivider.displayName = 'DropdownDivider'\n\n// DropdownLabel\nexport interface DropdownLabelProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownLabel = forwardRef<HTMLDivElement, DropdownLabelProps>(function DropdownLabel(\n { className, style, children, ...props },\n ref\n) {\n const labelStyle: CSSProperties = {\n padding: '8px 12px 4px 12px',\n fontSize: 11,\n fontWeight: 600,\n color: 'var(--brycks-foreground-muted)',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n ...style,\n }\n\n return (\n <div\n ref={ref}\n className={cx('brycks-dropdown-label', className)}\n style={labelStyle}\n {...props}\n >\n {children}\n </div>\n )\n})\n\nDropdownLabel.displayName = 'DropdownLabel'\n"],"names":["DropdownContext","createContext","useDropdownContext","context","useContext","Dropdown","forwardRef","trigger","controlledIsOpen","defaultOpen","onOpenChange","placement","offset","closeOnSelect","className","style","children","testId","props","ref","internalIsOpen","setInternalIsOpen","useState","position","setPosition","activeIndex","setActiveIndex","mounted","setMounted","triggerRef","useRef","menuRef","isOpen","useEffect","setIsOpen","useCallback","open","close","updatePosition","rect","scrollX","scrollY","top","left","handleClick","e","handleKeyDown","items","prev","triggerProps","triggerElement","cloneElement","menuStyle","jsx","jsxs","cx","createPortal","Fragment","DropdownItem","disabled","icon","shortcut","destructive","onClick","itemStyle","iconStyle","shortcutStyle","DropdownDivider","dividerStyle","DropdownLabel","labelStyle"],"mappings":";;;;AAkCA,MAAMA,IAAkBC,EAA2C,IAAI;AAEvE,SAASC,IAAqB;AAC5B,QAAMC,IAAUC,EAAWJ,CAAe;AAC1C,MAAI,CAACG;AACH,UAAM,IAAI,MAAM,oDAAoD;AAEtE,SAAOA;AACT;AAuBO,MAAME,IAAWC,EAA0C,SAChE;AAAA,EACE,SAAAC;AAAA,EACA,QAAQC;AAAA,EACR,aAAAC,IAAc;AAAA,EACd,cAAAC;AAAA,EACA,WAAAC,IAAY;AAAA,EACZ,QAAAC,IAAS;AAAA,EACT,eAAAC,IAAgB;AAAA,EAChB,WAAAC;AAAA,EACA,OAAAC;AAAA,EACA,UAAAC;AAAA,EACA,QAAAC;AAAA,EACA,GAAGC;AACL,GACAC,GACA;AACA,QAAM,CAACC,GAAgBC,CAAiB,IAAIC,EAASb,CAAW,GAC1D,CAACc,GAAUC,CAAW,IAAIF,EAAS,EAAE,KAAK,GAAG,MAAM,GAAG,GACtD,CAACG,GAAaC,CAAc,IAAIJ,EAAS,EAAE,GAC3C,CAACK,GAASC,CAAU,IAAIN,EAAS,EAAK,GACtCO,IAAaC,EAAoB,IAAI,GACrCC,IAAUD,EAAuB,IAAI,GAErCE,IAASxB,KAAoBY;AAEnC,EAAAa,EAAU,OACRL,EAAW,EAAI,GACR,MAAMA,EAAW,EAAK,IAC5B,CAAA,CAAE;AAEL,QAAMM,IAAYC;AAAA,IAChB,CAACC,MAAkB;AACjB,MAAI5B,MAAqB,UACvBa,EAAkBe,CAAI,GAExB1B,IAAe0B,CAAI,GACdA,KACHV,EAAe,EAAE;AAAA,IAErB;AAAA,IACA,CAAClB,GAAkBE,CAAY;AAAA,EAAA,GAG3B2B,IAAQF,EAAY,MAAMD,EAAU,EAAK,GAAG,CAACA,CAAS,CAAC;AAG7D,EAAAD,EAAU,MAAM;AACd,QAAI,CAACD,KAAU,CAACH,EAAW,WAAW,CAACF,EAAS;AAEhD,UAAMW,IAAiB,MAAM;AAC3B,YAAM/B,IAAUsB,EAAW;AAC3B,UAAI,CAACtB,EAAS;AAEd,YAAMgC,IAAOhC,EAAQ,sBAAA,GACfiC,IAAU,OAAO,SACjBC,IAAU,OAAO;AAEvB,UAAIC,IAAM,GACNC,IAAO;AAEX,MAAIhC,EAAU,WAAW,QAAQ,IAC/B+B,IAAMH,EAAK,SAASE,IAAU7B,IAE9B8B,IAAMH,EAAK,MAAME,IAAU7B,GAGzBD,EAAU,SAAS,OAAO,IAC5BgC,IAAOJ,EAAK,OAAOC,IAEnBG,IAAOJ,EAAK,QAAQC,GAGtBhB,EAAY,EAAE,KAAAkB,GAAK,MAAAC,GAAM;AAAA,IAC3B;AAEA,WAAAL,EAAA,GACA,OAAO,iBAAiB,UAAUA,CAAc,GAChD,OAAO,iBAAiB,UAAUA,GAAgB,EAAI,GAE/C,MAAM;AACX,aAAO,oBAAoB,UAAUA,CAAc,GACnD,OAAO,oBAAoB,UAAUA,GAAgB,EAAI;AAAA,IAC3D;AAAA,EACF,GAAG,CAACN,GAAQrB,GAAWC,GAAQe,CAAO,CAAC,GAGvCM,EAAU,MAAM;AACd,QAAI,CAACD,EAAQ;AAEb,UAAMY,IAAc,CAACC,MAAkB;AACrC,MACEhB,EAAW,SAAS,SAASgB,EAAE,MAAc,KAC7Cd,EAAQ,SAAS,SAASc,EAAE,MAAc,KAI5CX,EAAU,EAAK;AAAA,IACjB;AAEA,oBAAS,iBAAiB,aAAaU,CAAW,GAC3C,MAAM,SAAS,oBAAoB,aAAaA,CAAW;AAAA,EACpE,GAAG,CAACZ,GAAQE,CAAS,CAAC;AAGtB,QAAMY,IAAgBX;AAAA,IACpB,CAAC,MAAqB;AACpB,UAAI,CAACH,GAAQ;AACX,SAAI,EAAE,QAAQ,eAAe,EAAE,QAAQ,WAAW,EAAE,QAAQ,SAC1D,EAAE,eAAA,GACFE,EAAU,EAAI;AAEhB;AAAA,MACF;AAEA,YAAMa,IAAQhB,EAAQ,SAAS,iBAA8B,+CAA+C;AAC5G,UAAKgB,GAAO;AAEZ,gBAAQ,EAAE,KAAA;AAAA,UACR,KAAK;AACH,cAAE,eAAA,GACFrB,EAAe,CAACsB,OAAUA,IAAO,KAAKD,EAAM,MAAM;AAClD;AAAA,UACF,KAAK;AACH,cAAE,eAAA,GACFrB,EAAe,CAACsB,OAAUA,IAAO,IAAID,EAAM,UAAUA,EAAM,MAAM;AACjE;AAAA,UACF,KAAK;AACH,cAAE,eAAA,GACFrB,EAAe,CAAC;AAChB;AAAA,UACF,KAAK;AACH,cAAE,eAAA,GACFA,EAAeqB,EAAM,SAAS,CAAC;AAC/B;AAAA,UACF,KAAK;AACH,cAAE,eAAA,GACFb,EAAU,EAAK,GACfL,EAAW,SAAS,MAAA;AACpB;AAAA,UACF,KAAK;AAAA,UACL,KAAK;AACH,cAAE,eAAA,GACEJ,KAAe,KACjBsB,EAAMtB,CAAW,GAAG,MAAA;AAEtB;AAAA,QAAA;AAAA,IAEN;AAAA,IACA,CAACO,GAAQP,GAAaS,CAAS;AAAA,EAAA;AAIjC,EAAAD,EAAU,MAAM;AACd,QAAI,CAACD,KAAUP,IAAc,EAAG;AAEhC,IADcM,EAAQ,SAAS,iBAA8B,+CAA+C,IACpGN,CAAW,GAAG,MAAA;AAAA,EACxB,GAAG,CAACO,GAAQP,CAAW,CAAC;AAExB,QAAMwB,IAAe1C,EAAQ,OACvB2C,IAAiBC,EAAa5C,GAAkD;AAAA,IACpF,KAAKsB;AAAA,IACL,iBAAiB;AAAA,IACjB,iBAAiBG;AAAA,IACjB,SAAS,CAAC,MAAwB;AAC/B,MAAAiB,EAAa,UAA0D,CAAC,GACzEf,EAAU,CAACF,CAAM;AAAA,IACnB;AAAA,IACA,WAAW,CAAC,MAAqB;AAC9B,MAAAiB,EAAa,YAAyD,CAAC,GACxEH,EAAc,CAAC;AAAA,IACjB;AAAA,EAAA,CACD,GAEKM,IAA2B;AAAA,IAC/B,UAAU;AAAA,IACV,KAAK7B,EAAS;AAAA,IACd,MAAMZ,EAAU,SAAS,KAAK,IAAI,SAASY,EAAS;AAAA,IACpD,OAAOZ,EAAU,SAAS,KAAK,IAAI,OAAO,aAAaY,EAAS,OAAO;AAAA,IACvE,QAAQ;AAAA,IACR,UAAUM,EAAW,SAAS,eAAe;AAAA,IAC7C,iBAAiB;AAAA,IACjB,QAAQ;AAAA,IACR,cAAc;AAAA,IACd,WAAW;AAAA,IACX,SAAS;AAAA,IACT,WAAW;AAAA,IACX,SAAS;AAAA,IACT,GAAGd;AAAA,EAAA;AAGL,SACE,gBAAAsC,EAACrD,EAAgB,UAAhB,EAAyB,OAAO,EAAE,QAAAgC,GAAQ,OAAOnB,IAAgBwB,IAAQ,MAAM;AAAA,EAAC,GAAG,aAAAZ,GAAa,gBAAAC,EAAA,GAC/F,UAAA,gBAAA4B;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAnC;AAAA,MACA,WAAWoC,EAAG,mBAAmBzC,CAAS;AAAA,MAC1C,OAAO,EAAE,SAAS,eAAA;AAAA,MAClB,eAAaG;AAAA,MACZ,GAAGC;AAAA,MAEH,UAAA;AAAA,QAAAgC;AAAA,QACAlB,KAAUL,KAAW6B;AAAA,UACpB,gBAAAF,EAAAG,GAAA,EACE,UAAA;AAAA,YAAA,gBAAAJ,EAAC,SAAA,EACE,UAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAMH;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA,cAAA;AAAA,gBACC,KAAKtB;AAAA,gBACL,MAAK;AAAA,gBACL,OAAOqB;AAAA,gBACP,WAAWN;AAAA,gBACX,UAAU;AAAA,gBAET,UAAA9B;AAAA,cAAA;AAAA,YAAA;AAAA,UACH,GACF;AAAA,UACA,SAAS;AAAA,QAAA;AAAA,MACX;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ,CAAC;AAEDX,EAAS,cAAc;AAgBhB,MAAMqD,KAAepD,EAA8C,SACxE,EAAE,UAAAqD,IAAW,IAAO,MAAAC,GAAM,UAAAC,GAAU,aAAAC,IAAc,IAAO,WAAAhD,GAAW,OAAAC,GAAO,UAAAC,GAAU,SAAA+C,GAAS,GAAG7C,EAAA,GACjGC,GACA;AACA,QAAM,EAAE,OAAAkB,EAAA,IAAUnC,EAAA,GAEZ0C,IAAc,CAACC,MAAwC;AAC3D,IAAIc,MACJI,IAAUlB,CAAC,GACXR,EAAA;AAAA,EACF,GAEM2B,IAA2B;AAAA,IAC/B,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,SAAS;AAAA,IACT,UAAU;AAAA,IACV,OAAOL,IACH,sCACAG,IACA,gCACA;AAAA,IACJ,iBAAiB;AAAA,IACjB,cAAc;AAAA,IACd,QAAQH,IAAW,gBAAgB;AAAA,IACnC,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,GAAG5C;AAAA,EAAA,GAGCkD,IAA2B;AAAA,IAC/B,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,OAAON,IACH,sCACAG,IACA,gCACA;AAAA,IACJ,YAAY;AAAA,EAAA,GAGRI,IAA+B;AAAA,IACnC,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,OAAO;AAAA,IACP,YAAY;AAAA,EAAA;AAGd,SACE,gBAAAZ;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAnC;AAAA,MACA,MAAK;AAAA,MACL,iBAAewC;AAAA,MACf,UAAUA,IAAW,KAAK;AAAA,MAC1B,WAAWJ,EAAG,wBAAwBI,KAAY,kCAAkC7C,CAAS;AAAA,MAC7F,OAAOkD;AAAA,MACP,SAASpB;AAAA,MACT,cAAc,CAACC,MAAM;AACnB,QAAKc,MACHd,EAAE,cAAc,MAAM,kBAAkB;AAAA,MAE5C;AAAA,MACA,cAAc,CAACA,MAAM;AACnB,QAAAA,EAAE,cAAc,MAAM,kBAAkB;AAAA,MAC1C;AAAA,MACA,SAAS,CAACA,MAAM;AACd,QAAAA,EAAE,cAAc,MAAM,kBAAkB;AAAA,MAC1C;AAAA,MACA,QAAQ,CAACA,MAAM;AACb,QAAAA,EAAE,cAAc,MAAM,kBAAkB;AAAA,MAC1C;AAAA,MACC,GAAG3B;AAAA,MAEH,UAAA;AAAA,QAAA0C,KAAQ,gBAAAP,EAAC,QAAA,EAAK,OAAOY,GAAY,UAAAL,GAAK;AAAA,0BACtC,QAAA,EAAK,OAAO,EAAE,MAAM,EAAA,GAAM,UAAA5C,GAAS;AAAA,QACnC6C,KAAY,gBAAAR,EAAC,QAAA,EAAK,OAAOa,GAAgB,UAAAL,EAAA,CAAS;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGzD,CAAC;AAEDH,GAAa,cAAc;AAQpB,MAAMS,KAAkB7D,EAAiD,SAC9E,EAAE,WAAAQ,GAAW,OAAAC,GAAO,GAAGG,EAAA,GACvBC,GACA;AACA,QAAMiD,IAA8B;AAAA,IAClC,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,QAAQ;AAAA,IACR,GAAGrD;AAAA,EAAA;AAGL,SACE,gBAAAsC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAlC;AAAA,MACA,MAAK;AAAA,MACL,WAAWoC,EAAG,2BAA2BzC,CAAS;AAAA,MAClD,OAAOsD;AAAA,MACN,GAAGlD;AAAA,IAAA;AAAA,EAAA;AAGV,CAAC;AAEDiD,GAAgB,cAAc;AAQvB,MAAME,KAAgB/D,EAA+C,SAC1E,EAAE,WAAAQ,GAAW,OAAAC,GAAO,UAAAC,GAAU,GAAGE,EAAA,GACjCC,GACA;AACA,QAAMmD,IAA4B;AAAA,IAChC,SAAS;AAAA,IACT,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,eAAe;AAAA,IACf,eAAe;AAAA,IACf,GAAGvD;AAAA,EAAA;AAGL,SACE,gBAAAsC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAAlC;AAAA,MACA,WAAWoC,EAAG,yBAAyBzC,CAAS;AAAA,MAChD,OAAOwD;AAAA,MACN,GAAGpD;AAAA,MAEH,UAAAF;AAAA,IAAA;AAAA,EAAA;AAGP,CAAC;AAEDqD,GAAc,cAAc;"}
1
+ {"version":3,"file":"Dropdown.js","sources":["../../../../src/components/navigation/Dropdown/Dropdown.tsx"],"sourcesContent":["/**\n * Dropdown Component\n *\n * A dropdown menu with keyboard navigation support.\n * Can be used for actions, navigation, or selection.\n */\n\nimport {\n forwardRef,\n useState,\n useCallback,\n useRef,\n useEffect,\n cloneElement,\n createContext,\n useContext,\n type CSSProperties,\n type ReactNode,\n type ReactElement,\n type HTMLAttributes,\n type KeyboardEvent,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\nimport { spacing, fontSizes, durations, easings } from '../../../design-system'\nimport { componentGap, componentPaddingX, componentPaddingY, iconSizes } from '../../../design-system/primitives'\n\nexport type DropdownPlacement = 'bottom-start' | 'bottom-end' | 'top-start' | 'top-end'\n\ninterface DropdownContextValue {\n isOpen: boolean\n close: () => void\n activeIndex: number\n setActiveIndex: (index: number) => void\n}\n\nconst DropdownContext = createContext<DropdownContextValue | null>(null)\n\nfunction useDropdownContext() {\n const context = useContext(DropdownContext)\n if (!context) {\n throw new Error('Dropdown components must be used within a Dropdown')\n }\n return context\n}\n\nexport interface DropdownProps extends HTMLAttributes<HTMLDivElement> {\n /** Trigger element */\n trigger: ReactElement\n /** Whether the dropdown is open (controlled) */\n isOpen?: boolean\n /** Default open state (uncontrolled) */\n defaultOpen?: boolean\n /** Callback when open state changes */\n onOpenChange?: (isOpen: boolean) => void\n /** Dropdown placement */\n placement?: DropdownPlacement\n /** Offset from trigger */\n offset?: number\n /** Whether to close on item select */\n closeOnSelect?: boolean\n /** Custom class name */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(function Dropdown(\n {\n trigger,\n isOpen: controlledIsOpen,\n defaultOpen = false,\n onOpenChange,\n placement = 'bottom-start',\n offset = 4,\n closeOnSelect = true,\n className,\n style,\n children,\n testId,\n ...props\n },\n ref\n) {\n const [internalIsOpen, setInternalIsOpen] = useState(defaultOpen)\n const [position, setPosition] = useState({ top: 0, left: 0 })\n const [activeIndex, setActiveIndex] = useState(-1)\n const [mounted, setMounted] = useState(false)\n const triggerRef = useRef<HTMLElement>(null)\n const menuRef = useRef<HTMLDivElement>(null)\n\n const isOpen = controlledIsOpen ?? internalIsOpen\n\n useEffect(() => {\n setMounted(true)\n return () => setMounted(false)\n }, [])\n\n const setIsOpen = useCallback(\n (open: boolean) => {\n if (controlledIsOpen === undefined) {\n setInternalIsOpen(open)\n }\n onOpenChange?.(open)\n if (!open) {\n setActiveIndex(-1)\n }\n },\n [controlledIsOpen, onOpenChange]\n )\n\n const close = useCallback(() => setIsOpen(false), [setIsOpen])\n\n // Calculate position\n useEffect(() => {\n if (!isOpen || !triggerRef.current || !mounted) return\n\n const updatePosition = () => {\n const trigger = triggerRef.current\n if (!trigger) return\n\n const rect = trigger.getBoundingClientRect()\n const scrollX = window.scrollX\n const scrollY = window.scrollY\n\n let top = 0\n let left = 0\n\n if (placement.startsWith('bottom')) {\n top = rect.bottom + scrollY + offset\n } else {\n top = rect.top + scrollY - offset\n }\n\n if (placement.endsWith('start')) {\n left = rect.left + scrollX\n } else {\n left = rect.right + scrollX\n }\n\n setPosition({ top, left })\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, placement, offset, mounted])\n\n // Close on outside click\n useEffect(() => {\n if (!isOpen) return\n\n const handleClick = (e: MouseEvent) => {\n if (\n triggerRef.current?.contains(e.target as Node) ||\n menuRef.current?.contains(e.target as Node)\n ) {\n return\n }\n setIsOpen(false)\n }\n\n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [isOpen, setIsOpen])\n\n // Keyboard navigation\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n if (!isOpen) {\n if (e.key === 'ArrowDown' || e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n setIsOpen(true)\n }\n return\n }\n\n const items = menuRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])')\n if (!items?.length) return\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault()\n setActiveIndex((prev) => (prev + 1) % items.length)\n break\n case 'ArrowUp':\n e.preventDefault()\n setActiveIndex((prev) => (prev - 1 + items.length) % items.length)\n break\n case 'Home':\n e.preventDefault()\n setActiveIndex(0)\n break\n case 'End':\n e.preventDefault()\n setActiveIndex(items.length - 1)\n break\n case 'Escape':\n e.preventDefault()\n setIsOpen(false)\n triggerRef.current?.focus()\n break\n case 'Enter':\n case ' ':\n e.preventDefault()\n if (activeIndex >= 0) {\n items[activeIndex]?.click()\n }\n break\n }\n },\n [isOpen, activeIndex, setIsOpen]\n )\n\n // Focus active item\n useEffect(() => {\n if (!isOpen || activeIndex < 0) return\n const items = menuRef.current?.querySelectorAll<HTMLElement>('[role=\"menuitem\"]:not([aria-disabled=\"true\"])')\n items?.[activeIndex]?.focus()\n }, [isOpen, activeIndex])\n\n const triggerProps = trigger.props as Record<string, unknown>\n const triggerElement = cloneElement(trigger as ReactElement<Record<string, unknown>>, {\n ref: triggerRef,\n 'aria-haspopup': 'menu',\n 'aria-expanded': isOpen,\n onClick: (e: React.MouseEvent) => {\n (triggerProps.onClick as ((e: React.MouseEvent) => void) | undefined)?.(e)\n setIsOpen(!isOpen)\n },\n onKeyDown: (e: KeyboardEvent) => {\n (triggerProps.onKeyDown as ((e: KeyboardEvent) => void) | undefined)?.(e)\n handleKeyDown(e)\n },\n })\n\n const menuStyle: CSSProperties = {\n position: 'absolute',\n top: position.top,\n left: placement.endsWith('end') ? 'auto' : position.left,\n right: placement.endsWith('end') ? window.innerWidth - position.left : 'auto',\n zIndex: 'var(--brycks-z-dropdown)' as unknown as number,\n minWidth: triggerRef.current?.offsetWidth ?? 160,\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 padding: spacing[1],\n animation: `brycks-dropdown-in ${durations.quick}ms ${easings.easeOut}`,\n outline: 'none',\n ...style,\n }\n\n return (\n <DropdownContext.Provider value={{ isOpen, close: closeOnSelect ? close : () => {}, activeIndex, setActiveIndex }}>\n <div\n ref={ref}\n className={cx('brycks-dropdown', className)}\n style={{ display: 'inline-block' }}\n data-testid={testId}\n {...props}\n >\n {triggerElement}\n {isOpen && mounted && 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 <div\n ref={menuRef}\n role=\"menu\"\n style={menuStyle}\n onKeyDown={handleKeyDown}\n tabIndex={-1}\n >\n {children}\n </div>\n </>,\n document.body\n )}\n </div>\n </DropdownContext.Provider>\n )\n})\n\nDropdown.displayName = 'Dropdown'\n\n// DropdownItem\nexport interface DropdownItemProps extends HTMLAttributes<HTMLDivElement> {\n /** Whether the item is disabled */\n disabled?: boolean\n /** Icon before the label */\n icon?: ReactNode\n /** Shortcut text */\n shortcut?: string\n /** Whether the item is destructive */\n destructive?: boolean\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownItem = forwardRef<HTMLDivElement, DropdownItemProps>(function DropdownItem(\n { disabled = false, icon, shortcut, destructive = false, className, style, children, onClick, ...props },\n ref\n) {\n const { close } = useDropdownContext()\n\n const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return\n onClick?.(e)\n close()\n }\n\n const itemStyle: CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n gap: componentGap.lg,\n padding: `${componentPaddingY.md}px ${componentPaddingX.sm}px`,\n fontSize: fontSizes.base,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : destructive\n ? 'var(--brycks-error-default)'\n : 'var(--brycks-foreground-default)',\n backgroundColor: 'transparent',\n borderRadius: 'var(--brycks-radius-md)',\n cursor: disabled ? 'not-allowed' : 'pointer',\n transition: `background-color ${durations.fast}ms ${easings.easeOut}`,\n outline: 'none',\n ...style,\n }\n\n const iconStyle: CSSProperties = {\n width: iconSizes.sm,\n height: iconSizes.sm,\n color: disabled\n ? 'var(--brycks-foreground-disabled)'\n : destructive\n ? 'var(--brycks-error-default)'\n : 'var(--brycks-foreground-muted)',\n flexShrink: 0,\n }\n\n const shortcutStyle: CSSProperties = {\n marginLeft: 'auto',\n fontSize: fontSizes.sm,\n color: 'var(--brycks-foreground-muted)',\n fontFamily: 'var(--brycks-font-mono)',\n }\n\n return (\n <div\n ref={ref}\n role=\"menuitem\"\n aria-disabled={disabled}\n tabIndex={disabled ? -1 : 0}\n className={cx('brycks-dropdown-item', disabled && 'brycks-dropdown-item--disabled', className)}\n style={itemStyle}\n onClick={handleClick}\n onMouseEnter={(e) => {\n if (!disabled) {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n onFocus={(e) => {\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\n }}\n onBlur={(e) => {\n e.currentTarget.style.backgroundColor = 'transparent'\n }}\n {...props}\n >\n {icon && <span style={iconStyle}>{icon}</span>}\n <span style={{ flex: 1 }}>{children}</span>\n {shortcut && <span style={shortcutStyle}>{shortcut}</span>}\n </div>\n )\n})\n\nDropdownItem.displayName = 'DropdownItem'\n\n// DropdownDivider\nexport interface DropdownDividerProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownDivider = forwardRef<HTMLDivElement, DropdownDividerProps>(function DropdownDivider(\n { className, style, ...props },\n ref\n) {\n const dividerStyle: CSSProperties = {\n height: 1,\n backgroundColor: 'var(--brycks-border-muted)',\n margin: `${spacing[1]}px 0`,\n ...style,\n }\n\n return (\n <div\n ref={ref}\n role=\"separator\"\n className={cx('brycks-dropdown-divider', className)}\n style={dividerStyle}\n {...props}\n />\n )\n})\n\nDropdownDivider.displayName = 'DropdownDivider'\n\n// DropdownLabel\nexport interface DropdownLabelProps extends HTMLAttributes<HTMLDivElement> {\n /** Custom class name */\n className?: string\n}\n\nexport const DropdownLabel = forwardRef<HTMLDivElement, DropdownLabelProps>(function DropdownLabel(\n { className, style, children, ...props },\n ref\n) {\n const labelStyle: CSSProperties = {\n padding: `${componentPaddingY.md}px ${componentPaddingX.sm}px ${spacing[1]}px ${componentPaddingX.sm}px`,\n fontSize: fontSizes.xs,\n fontWeight: 600,\n color: 'var(--brycks-foreground-muted)',\n textTransform: 'uppercase',\n letterSpacing: '0.05em',\n ...style,\n }\n\n return (\n <div\n ref={ref}\n className={cx('brycks-dropdown-label', className)}\n style={labelStyle}\n {...props}\n >\n {children}\n </div>\n )\n})\n\nDropdownLabel.displayName = 'DropdownLabel'\n"],"names":["DropdownContext","createContext","useDropdownContext","context","useContext","Dropdown","forwardRef","trigger","controlledIsOpen","defaultOpen","onOpenChange","placement","offset","closeOnSelect","className","style","children","testId","props","ref","internalIsOpen","setInternalIsOpen","useState","position","setPosition","activeIndex","setActiveIndex","mounted","setMounted","triggerRef","useRef","menuRef","isOpen","useEffect","setIsOpen","useCallback","open","close","updatePosition","rect","scrollX","scrollY","top","left","handleClick","e","handleKeyDown","items","prev","triggerProps","triggerElement","cloneElement","menuStyle","spacing","durations","easings","jsx","jsxs","cx","createPortal","Fragment","DropdownItem","disabled","icon","shortcut","destructive","onClick","itemStyle","componentGap","componentPaddingY","componentPaddingX","fontSizes","iconStyle","iconSizes","shortcutStyle","DropdownDivider","dividerStyle","DropdownLabel","labelStyle"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("react/jsx-runtime"),u=require("react"),y=require("../../../utils/styles.cjs"),S=u.createContext(null);function k(){const l=u.useContext(S);if(!l)throw new Error("Menu components must be used within a Menu");return l}const M=u.forwardRef(function({activeItem:e,onChange:o,size:n="md",collapsed:t=!1,className:c,style:m,children:f,testId:d,...p},g){const[x,v]=u.useState(null),h=e!==void 0?e:x,a=r=>{e===void 0&&v(r),o?.(r)},s={display:"flex",flexDirection:"column",gap:2,padding:8,...m};return i.jsx(S.Provider,{value:{size:n,activeItem:h,setActiveItem:a,collapsed:t},children:i.jsx("nav",{ref:g,className:y.cx("brycks-menu",`brycks-menu--${n}`,t&&"brycks-menu--collapsed",c),style:s,"data-testid":d,...p,children:f})})});M.displayName="Menu";const D={sm:{height:32,fontSize:13,iconSize:16,padding:10},md:{height:40,fontSize:14,iconSize:18,padding:12},lg:{height:48,fontSize:16,iconSize:20,padding:14}},j=u.forwardRef(function({value:e,icon:o,badge:n,disabled:t=!1,onClick:c,className:m,style:f,children:d,...p},g){const{size:x,activeItem:v,setActiveItem:h,collapsed:a}=k(),s=v===e,r=D[x],I=()=>{t||(h(e),c?.(e))},w={display:"flex",alignItems:"center",gap:10,width:"100%",minHeight:r.height,padding:a?r.padding:`0 ${r.padding}px`,fontSize:r.fontSize,fontWeight:s?600:500,color:t?"var(--brycks-foreground-disabled)":s?"var(--brycks-primary-default)":"var(--brycks-foreground-default)",backgroundColor:s?"var(--brycks-primary-50)":"transparent",border:"none",borderRadius:"var(--brycks-radius-md)",cursor:t?"not-allowed":"pointer",transition:"all 150ms ease-out",textAlign:"left",outline:"none",justifyContent:a?"center":"flex-start",...f},N={width:r.iconSize,height:r.iconSize,flexShrink:0,display:"flex",alignItems:"center",justifyContent:"center"},R={marginLeft:"auto"};return i.jsxs("button",{ref:g,type:"button",disabled:t,className:y.cx("brycks-menu-item",s&&"brycks-menu-item--active",m),style:w,onClick:I,onMouseEnter:b=>{!t&&!s&&(b.currentTarget.style.backgroundColor="var(--brycks-background-muted)")},onMouseLeave:b=>{s||(b.currentTarget.style.backgroundColor="transparent")},title:a?String(d):void 0,...p,children:[o&&i.jsx("span",{style:N,children:o}),!a&&i.jsx("span",{style:{flex:1},children:d}),!a&&n&&i.jsx("span",{style:R,children:n})]})});j.displayName="MenuItem";const C=u.forwardRef(function({label:e,className:o,style:n,children:t,...c},m){const{collapsed:f}=k(),d={display:"flex",flexDirection:"column",gap:2,...n},p={padding:"12px 12px 6px 12px",fontSize:11,fontWeight:600,color:"var(--brycks-foreground-muted)",textTransform:"uppercase",letterSpacing:"0.05em"};return i.jsxs("div",{ref:m,className:y.cx("brycks-menu-group",o),style:d,...c,children:[e&&!f&&i.jsx("div",{style:p,children:e}),t]})});C.displayName="MenuGroup";const z=u.forwardRef(function({className:e,style:o,...n},t){const c={height:1,backgroundColor:"var(--brycks-border-muted)",margin:"8px 0",...o};return i.jsx("div",{ref:t,role:"separator",className:y.cx("brycks-menu-divider",e),style:c,...n})});z.displayName="MenuDivider";exports.Menu=M;exports.MenuDivider=z;exports.MenuGroup=C;exports.MenuItem=j;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const a=require("react/jsx-runtime"),r=require("react"),j=require("../../../utils/styles.cjs"),m=require("../../../design-system/tokens/spacing.cjs"),N=require("../../../design-system/tokens/motion.cjs"),i=require("../../../design-system/primitives/sizing.cjs"),C=require("../../../design-system/tokens/typography.cjs"),A=r.createContext(null);function G(){const f=r.useContext(A);if(!f)throw new Error("Menu components must be used within a Menu");return f}const H=r.forwardRef(function({activeItem:t,onChange:u,size:c="md",collapsed:s=!1,className:p,style:b,children:S,testId:g,...v},y){const[D,R]=r.useState(null),[x,o]=r.useState(-1),k=r.useRef([]),I=r.useRef(null),q=t!==void 0?t:D,l=e=>{t===void 0&&R(e),u?.(e)},d=r.useCallback(e=>{const n=k.current.indexOf(e);return n===-1?(k.current.push(e),k.current.length-1):n},[]),M=r.useCallback(e=>{const n=I.current?.querySelectorAll('[role="menuitem"]:not(:disabled)');if(!n?.length)return;const z=x>=0?x:0;switch(e.key){case"ArrowDown":e.preventDefault();const w=(z+1)%n.length;o(w),n[w]?.focus();break;case"ArrowUp":e.preventDefault();const h=(z-1+n.length)%n.length;o(h),n[h]?.focus();break;case"Home":e.preventDefault(),o(0),n[0]?.focus();break;case"End":e.preventDefault(),o(n.length-1),n[n.length-1]?.focus();break}},[x]),$={display:"flex",flexDirection:"column",gap:m.spacing[.5],padding:m.spacing[2],...b};return a.jsx(A.Provider,{value:{size:c,activeItem:q,setActiveItem:l,collapsed:s,focusedIndex:x,setFocusedIndex:o,registerItem:d},children:a.jsx("nav",{ref:e=>{I.current=e,typeof y=="function"?y(e):y&&(y.current=e)},role:"menu",className:j.cx("brycks-menu",`brycks-menu--${c}`,s&&"brycks-menu--collapsed",p),style:$,onKeyDown:M,"data-testid":g,...v,children:S})})});H.displayName="Menu";const E={sm:{height:i.componentHeights.sm,fontSize:C.fontSizes.base-1,iconSize:i.iconSizes.sm,padding:i.componentGap.lg},md:{height:i.componentHeights.md,fontSize:C.fontSizes.base,iconSize:i.iconSizes.md-2,padding:i.componentPaddingX.sm},lg:{height:i.componentHeights.lg,fontSize:C.fontSizes.md,iconSize:i.iconSizes.md,padding:m.spacing[3.5]}},F=r.forwardRef(function({value:t,icon:u,badge:c,disabled:s=!1,onClick:p,className:b,style:S,children:g,...v},y){const{size:D,activeItem:R,setActiveItem:x,collapsed:o,focusedIndex:k,setFocusedIndex:I,registerItem:q}=G(),l=R===t,d=E[D],M=q(t),$=()=>{s||(x(t),p?.(t))},e=()=>{I(M)},n={display:"flex",alignItems:"center",gap:i.componentGap.lg,width:"100%",minHeight:d.height,padding:o?d.padding:`0 ${d.padding}px`,fontSize:d.fontSize,fontWeight:l?600:500,color:s?"var(--brycks-foreground-disabled)":l?"var(--brycks-primary-default)":"var(--brycks-foreground-default)",backgroundColor:l?"var(--brycks-primary-50)":"transparent",border:"none",borderRadius:"var(--brycks-radius-md)",cursor:s?"not-allowed":"pointer",transition:`all ${N.durations.quick}ms ${N.easings.easeOut}`,textAlign:"left",outline:"none",justifyContent:o?"center":"flex-start",...S},z={width:d.iconSize,height:d.iconSize,flexShrink:0,display:"flex",alignItems:"center",justifyContent:"center"},w={marginLeft:"auto"};return a.jsxs("button",{ref:y,type:"button",role:"menuitem",disabled:s,tabIndex:k===M?0:-1,className:j.cx("brycks-menu-item",l&&"brycks-menu-item--active",b),style:n,onClick:$,onFocus:e,onMouseEnter:h=>{!s&&!l&&(h.currentTarget.style.backgroundColor="var(--brycks-background-muted)")},onMouseLeave:h=>{l||(h.currentTarget.style.backgroundColor="transparent")},title:o?String(g):void 0,...v,children:[u&&a.jsx("span",{style:z,children:u}),!o&&a.jsx("span",{style:{flex:1},children:g}),!o&&c&&a.jsx("span",{style:w,children:c})]})});F.displayName="MenuItem";const P=r.forwardRef(function({label:t,className:u,style:c,children:s,...p},b){const{collapsed:S}=G(),g={display:"flex",flexDirection:"column",gap:m.spacing[.5],...c},v={padding:`${m.spacing[3]}px ${m.spacing[3]}px ${i.componentPaddingY.sm}px ${m.spacing[3]}px`,fontSize:C.fontSizes.xs,fontWeight:600,color:"var(--brycks-foreground-muted)",textTransform:"uppercase",letterSpacing:"0.05em"};return a.jsxs("div",{ref:b,className:j.cx("brycks-menu-group",u),style:g,...p,children:[t&&!S&&a.jsx("div",{style:v,children:t}),s]})});P.displayName="MenuGroup";const T=r.forwardRef(function({className:t,style:u,...c},s){const p={height:1,backgroundColor:"var(--brycks-border-muted)",margin:`${m.spacing[2]}px 0`,...u};return a.jsx("div",{ref:s,role:"separator",className:j.cx("brycks-menu-divider",t),style:p,...c})});T.displayName="MenuDivider";exports.Menu=H;exports.MenuDivider=T;exports.MenuGroup=P;exports.MenuItem=F;
2
2
  //# sourceMappingURL=Menu.cjs.map