{"version":3,"file":"DataLinksContextMenu.mjs","sources":["../../../../src/components/DataLinks/DataLinksContextMenu.tsx"],"sourcesContent":["import { css } from '@emotion/css';\nimport { type CSSProperties, type JSX } from 'react';\nimport * as React from 'react';\n\nimport { type ActionModel, type GrafanaTheme2, type LinkModel } from '@grafana/data';\nimport { selectors } from '@grafana/e2e-selectors';\n\nimport { useStyles2 } from '../../themes/ThemeContext';\nimport { linkModelToContextMenuItems } from '../../utils/dataLinks';\nimport { type OpenMenuTrigger, WithContextMenu } from '../ContextMenu/WithContextMenu';\nimport { MenuGroup, type MenuItemsGroup } from '../Menu/MenuGroup';\nimport { MenuItem } from '../Menu/MenuItem';\n\nexport interface DataLinksContextMenuProps {\n  children: (props: DataLinksContextMenuApi) => JSX.Element;\n  links: () => LinkModel[];\n  style?: CSSProperties;\n  /**\n   * @deprecated Will be removed in a future version\n   */\n  actions?: ActionModel[];\n}\n\n/**\n * Props consumers spread on the focusable element that opens the data-links menu.\n * Intentionally `Partial<...>` of the underlying handler types so the props can\n * be applied to either an HTML element (`<button>`) or an SVG element (`<g>`,\n * `<a>`) without TS gymnastics on the consumer side.\n *\n * Use these for the multi-link case to get keyboard accessibility (Enter/Space\n * to open the menu) and the correct ARIA semantics for free.\n */\nexport interface DataLinksMenuTriggerProps {\n  role: 'button';\n  tabIndex: 0;\n  'aria-haspopup': 'menu';\n  onClick: (event: React.MouseEvent<Element>) => void;\n  onKeyDown: (event: React.KeyboardEvent<Element>) => void;\n}\n\nexport interface DataLinksContextMenuApi {\n  /**\n   * Opens the menu, anchored to either a pointer event, an `Element`, or an\n   * explicit `{x, y}`. Most consumers should prefer spreading `triggerProps`\n   * onto a focusable element instead of wiring `openMenu` manually.\n   */\n  openMenu?: (trigger: OpenMenuTrigger) => void;\n  targetClassName?: string;\n  /**\n   * Spread onto the focusable trigger element to make the menu keyboard- and\n   * screen-reader-accessible (Enter/Space to open, `role=\"button\"`,\n   * `aria-haspopup=\"menu\"`). Only present when the menu has more than one\n   * link — for a single link the component renders a real `<a>` itself.\n   */\n  triggerProps?: DataLinksMenuTriggerProps;\n}\n\nexport const DataLinksContextMenu = ({ children, links, style }: DataLinksContextMenuProps) => {\n  const styles = useStyles2(getStyles);\n\n  const itemsGroup: MenuItemsGroup[] = [\n    { items: linkModelToContextMenuItems(links), label: Boolean(links().length) ? 'Data links' : '' },\n  ];\n\n  const linksCounter = itemsGroup[0].items.length;\n  const renderMenuGroupItems = () => {\n    return itemsGroup.map((group, groupIdx) => (\n      <MenuGroup key={`${group.label}${groupIdx}`} label={group.label}>\n        {(group.items || []).map((item, itemIdx) => (\n          <MenuItem\n            key={`${group.label}-${groupIdx}-${itemIdx}}`}\n            url={item.url}\n            label={item.label}\n            target={item.target}\n            icon={item.icon}\n            active={item.active}\n            onClick={item.onClick}\n            className={styles.itemWrapper}\n          />\n        ))}\n      </MenuGroup>\n    ));\n  };\n\n  // Class consumers can apply to their click target purely as a visual \"this\n  // opens a context menu\" affordance (cursor only). Behaviour is exposed\n  // separately via `openMenu` / `triggerProps`.\n  const targetClassName = styles.menuTarget;\n\n  if (linksCounter > 1) {\n    return (\n      <WithContextMenu renderMenuItems={renderMenuGroupItems}>\n        {({ openMenu }) => {\n          // Keyboard activation — anchor the menu to the trigger element\n          // itself instead of fabricating a synthetic MouseEvent. This works\n          // identically for HTML and SVG focusable elements.\n          const triggerProps: DataLinksMenuTriggerProps = {\n            role: 'button',\n            tabIndex: 0,\n            'aria-haspopup': 'menu',\n            onClick: (e) => openMenu(e),\n            onKeyDown: (e) => {\n              if (e.key === 'Enter' || e.key === ' ') {\n                e.preventDefault();\n                openMenu(e.currentTarget);\n              }\n            },\n          };\n          return children({ openMenu, targetClassName, triggerProps });\n        }}\n      </WithContextMenu>\n    );\n  } else if (linksCounter === 1) {\n    const linkModel = links()[0];\n    return (\n      <a\n        href={linkModel.href}\n        onClick={linkModel.onClick}\n        target={linkModel.target}\n        title={linkModel.title}\n        // Layout shorthands stay inline for back-compat — BarGauge/Gauge pass\n        // `style={{ flexGrow: 1 }}`/`{ height: '100%' }` and rely on the\n        // wrapper being a flex container. The `:focus-visible` ring lives in\n        // the themed class so every consumer gains a focus indicator without\n        // opting in (this is what fixes the keyboard-accessibility gap).\n        style={{ ...style, overflow: 'hidden', display: 'flex' }}\n        className={styles.singleLink}\n        data-testid={selectors.components.DataLinksContextMenu.singleLink}\n      >\n        {children({})}\n      </a>\n    );\n  }\n\n  // No links — render children plainly. This guards a long-standing latent\n  // crash: `hasLinks` and a `getLinks` supplier come from independent code\n  // paths (`hasLinks(field)` checks `field.config.links.length` while the\n  // supplier is created by `applyFieldOverrides`), so consumers can land here\n  // with `getLinks() === []`. Returning `children({})` mirrors what the panel\n  // would have rendered without the wrapper.\n  return children({});\n};\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n  itemWrapper: css({\n    fontSize: 12,\n  }),\n  // The single-link path used to ship only inline styles; the `:focus-visible`\n  // ring is the one piece of polish that was missing and the reason panels\n  // were re-implementing this branch themselves. Layout shorthands stay\n  // inline (see component) so this class is purely visual.\n  singleLink: css({\n    color: 'inherit',\n    textDecoration: 'none',\n    borderRadius: theme.shape.radius.default,\n    '&:focus-visible': {\n      outline: `2px solid ${theme.colors.primary.main}`,\n      outlineOffset: '2px',\n    },\n  }),\n  menuTarget: css({\n    cursor: 'context-menu',\n    borderRadius: theme.shape.radius.default,\n    '&:focus-visible': {\n      outline: `2px solid ${theme.colors.primary.main}`,\n      outlineOffset: '2px',\n    },\n  }),\n});\n"],"names":[],"mappings":"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"}