{"version":3,"file":"Menu.cjs","names":[],"sources":["../../../src/components/Menu/Menu.tsx"],"sourcesContent":["/*\nCopyright 2023 New Vector Ltd.\n\nSPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\nPlease see LICENSE files in the repository root for full details.\n*/\n\nimport React, {\n  type FC,\n  type ReactNode,\n  useMemo,\n  useEffect,\n  useState,\n} from \"react\";\nimport {\n  Root,\n  Trigger,\n  Portal,\n  Content,\n  DropdownMenuItem,\n  DropdownMenuSub,\n  DropdownMenuSubTrigger,\n  DropdownMenuSubContent,\n  DropdownMenuPortal,\n} from \"@radix-ui/react-dropdown-menu\";\nimport { FloatingMenu } from \"./FloatingMenu\";\nimport { Drawer } from \"vaul\";\nimport classnames from \"classnames\";\nimport drawerMenu from \"./DrawerMenu.module.css\";\nimport {\n  MenuContext,\n  type MenuData,\n  type MenuItemWrapperProps,\n  type SubMenuWrapperProps,\n} from \"./MenuContext\";\nimport { DrawerMenu } from \"./DrawerMenu\";\nimport { getPlatform } from \"../../utils/platform\";\n\ninterface Props {\n  /**\n   * CSS classes for the menu.\n   */\n  className?: string;\n\n  /**\n   * The menu title. This can be hidden with `showTitle={false}` in which case it will only\n   * be a label for screen readers.\n   */\n  title: string;\n  /**\n   * Controls whether the title is displayed (see `title` prop). Titles are only displayed on\n   * web: on mobile, this parameter is ignored.\n   */\n  showTitle?: boolean;\n  /**\n   * Whether the menu is open.\n   */\n  open: boolean;\n  /**\n   * Event handler called when the open state of the menu changes. This includes\n   * anything like clicking the trigger, selecting a menu item, or dismissing\n   * the menu with the mouse or keyboard.\n   */\n  onOpenChange: (open: boolean) => void;\n  /**\n   * The button that opens the menu. This must be a component that accepts a ref\n   * and spreads props.\n   * https://www.radix-ui.com/primitives/docs/guides/composition\n   */\n  trigger: ReactNode;\n  /**\n   * The menu contents.\n   */\n  children: ReactNode;\n  /**\n   * The side of the trigger on which to place the menu. Note that the menu may\n   * still end up on a different side than the one you request if there isn't\n   * enough space.\n   * @default bottom\n   */\n  side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n  /**\n   * The edge along which the menu and trigger will be aligned.\n   * @default center\n   */\n  align?: \"start\" | \"center\" | \"end\";\n}\n\nconst DropdownMenuItemWrapper: FC<MenuItemWrapperProps> = ({\n  onSelect,\n  children,\n}) => (\n  <DropdownMenuItem onSelect={onSelect ?? undefined} asChild>\n    {children}\n  </DropdownMenuItem>\n);\n\n/** Duration of the parent menu's slide-in animation (ms). */\nconst MENU_ANIMATION_DURATION = 180;\n\nconst DropdownSubMenuWrapper: FC<SubMenuWrapperProps> = ({\n  trigger,\n  children,\n  open: openProp,\n  onOpenChange,\n}) => {\n  // When the submenu is programmatically opened at the same time as the parent\n  // menu (e.g. open={true} on mount), the parent is still mid-animation and\n  // the trigger position hasn't settled. Defer the open so the submenu\n  // positions correctly after the parent animation completes.\n  const [deferredOpen, setDeferredOpen] = useState(false);\n\n  useEffect(() => {\n    if (openProp) {\n      const timer = setTimeout(\n        () => setDeferredOpen(true),\n        MENU_ANIMATION_DURATION,\n      );\n      return () => clearTimeout(timer);\n    } else {\n      setDeferredOpen(false);\n    }\n  }, [openProp]);\n\n  const open = openProp ? deferredOpen : openProp;\n\n  return (\n    <DropdownMenuSub open={open} onOpenChange={onOpenChange}>\n      <DropdownMenuSubTrigger asChild>{trigger}</DropdownMenuSubTrigger>\n      <DropdownMenuPortal>\n        <DropdownMenuSubContent asChild alignOffset={-20}>\n          <FloatingMenu title=\"\" showTitle={false}>\n            {children}\n          </FloatingMenu>\n        </DropdownMenuSubContent>\n      </DropdownMenuPortal>\n    </DropdownMenuSub>\n  );\n};\n\n/**\n * A menu opened by pressing a button.\n */\nexport const Menu: FC<Props> = ({\n  className,\n  title,\n  showTitle = true,\n  open,\n  onOpenChange,\n  trigger,\n  children: childrenProp,\n  side = \"bottom\",\n  align = \"center\",\n}) => {\n  // Normally, the menu takes the form of a floating box. But on Android and\n  // iOS, the menu should morph into a drawer\n  const platform = getPlatform();\n  const drawer = platform === \"android\" || platform === \"ios\";\n  const context: MenuData = useMemo(\n    () => ({\n      MenuItemWrapper: drawer ? null : DropdownMenuItemWrapper,\n      SubMenuWrapper: drawer ? null : DropdownSubMenuWrapper,\n      onOpenChange,\n    }),\n    [onOpenChange],\n  );\n  const children = (\n    <MenuContext.Provider value={context}>{childrenProp}</MenuContext.Provider>\n  );\n\n  return drawer ? (\n    <Drawer.Root open={open} onOpenChange={onOpenChange}>\n      <Drawer.Trigger asChild>{trigger}</Drawer.Trigger>\n      <Drawer.Portal>\n        <Drawer.Overlay className={classnames(drawerMenu.bg)} />\n        <Drawer.Content asChild>\n          <DrawerMenu title={title}>{children}</DrawerMenu>\n        </Drawer.Content>\n      </Drawer.Portal>\n    </Drawer.Root>\n  ) : (\n    <Root open={open} onOpenChange={onOpenChange}>\n      <Trigger asChild>{trigger}</Trigger>\n      <Portal>\n        <Content asChild side={side} align={align} sideOffset={8}>\n          <FloatingMenu\n            className={className}\n            title={title}\n            showTitle={showTitle}\n          >\n            {children}\n          </FloatingMenu>\n        </Content>\n      </Portal>\n    </Root>\n  );\n};\n"],"mappings":";;;;;;;;;;;;;;AAwFA,IAAM,2BAAqD,EACzD,UACA,eAEA,iBAAA,GAAA,kBAAA,KAAC,8BAAA,kBAAD;CAAkB,UAAU,YAAY,KAAA;CAAW,SAAA;CAChD;CACgB,CAAA;;AAIrB,IAAM,0BAA0B;AAEhC,IAAM,0BAAmD,EACvD,SACA,UACA,MAAM,UACN,mBACI;CAKJ,MAAM,CAAC,cAAc,oBAAA,GAAA,MAAA,UAA4B,MAAM;AAEvD,EAAA,GAAA,MAAA,iBAAgB;AACd,MAAI,UAAU;GACZ,MAAM,QAAQ,iBACN,gBAAgB,KAAK,EAC3B,wBACD;AACD,gBAAa,aAAa,MAAM;QAEhC,iBAAgB,MAAM;IAEvB,CAAC,SAAS,CAAC;AAId,QACE,iBAAA,GAAA,kBAAA,MAAC,8BAAA,iBAAD;EAAuB,MAHZ,WAAW,eAAe;EAGM;YAA3C,CACE,iBAAA,GAAA,kBAAA,KAAC,8BAAA,wBAAD;GAAwB,SAAA;aAAS;GAAiC,CAAA,EAClE,iBAAA,GAAA,kBAAA,KAAC,8BAAA,oBAAD,EAAA,UACE,iBAAA,GAAA,kBAAA,KAAC,8BAAA,wBAAD;GAAwB,SAAA;GAAQ,aAAa;aAC3C,iBAAA,GAAA,kBAAA,KAAC,qBAAA,cAAD;IAAc,OAAM;IAAG,WAAW;IAC/B;IACY,CAAA;GACQ,CAAA,EACN,CAAA,CACL;;;;;;AAOtB,IAAa,QAAmB,EAC9B,WACA,OACA,YAAY,MACZ,MACA,cACA,SACA,UAAU,cACV,OAAO,UACP,QAAQ,eACJ;CAGJ,MAAM,WAAW,iBAAA,aAAa;CAC9B,MAAM,SAAS,aAAa,aAAa,aAAa;CACtD,MAAM,WAAA,GAAA,MAAA,gBACG;EACL,iBAAiB,SAAS,OAAO;EACjC,gBAAgB,SAAS,OAAO;EAChC;EACD,GACD,CAAC,aAAa,CACf;CACD,MAAM,WACJ,iBAAA,GAAA,kBAAA,KAAC,oBAAA,YAAY,UAAb;EAAsB,OAAO;YAAU;EAAoC,CAAA;AAG7E,QAAO,SACL,iBAAA,GAAA,kBAAA,MAAC,KAAA,OAAO,MAAR;EAAmB;EAAoB;YAAvC,CACE,iBAAA,GAAA,kBAAA,KAAC,KAAA,OAAO,SAAR;GAAgB,SAAA;aAAS;GAAyB,CAAA,EAClD,iBAAA,GAAA,kBAAA,MAAC,KAAA,OAAO,QAAR,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAC,KAAA,OAAO,SAAR,EAAgB,YAAA,GAAA,WAAA,SAAsB,0BAAA,QAAW,GAAG,EAAI,CAAA,EACxD,iBAAA,GAAA,kBAAA,KAAC,KAAA,OAAO,SAAR;GAAgB,SAAA;aACd,iBAAA,GAAA,kBAAA,KAAC,mBAAA,YAAD;IAAmB;IAAQ;IAAsB,CAAA;GAClC,CAAA,CACH,EAAA,CAAA,CACJ;MAEd,iBAAA,GAAA,kBAAA,MAAC,8BAAA,MAAD;EAAY;EAAoB;YAAhC,CACE,iBAAA,GAAA,kBAAA,KAAC,8BAAA,SAAD;GAAS,SAAA;aAAS;GAAkB,CAAA,EACpC,iBAAA,GAAA,kBAAA,KAAC,8BAAA,QAAD,EAAA,UACE,iBAAA,GAAA,kBAAA,KAAC,8BAAA,SAAD;GAAS,SAAA;GAAc;GAAa;GAAO,YAAY;aACrD,iBAAA,GAAA,kBAAA,KAAC,qBAAA,cAAD;IACa;IACJ;IACI;IAEV;IACY,CAAA;GACP,CAAA,EACH,CAAA,CACJ"}