Repository: h2oai/wave
File: ui/src/menu.tsx (TypeScript)

Code before:
      },
      '&:hover button': {
        color: cssVar('$themePrimary')
      },
    }
  },
  icon: {
    fontSize: 18
  }
})

/**
  Create a contextual menu component. Useful when you have a lot of links and want to conserve the space.
*/
export interface Menu {
  /** Commands to render. */
  items: Command[]
  /** The card's icon. Mutually exclusive with the image. */
  icon?: S
  /** The card’s image, preferably user avatar. Mutually exclusive with the icon. */
  image?: S
  /** An identifying name for this component. */
  name?: Id
}

export const XMenu = ({ model }: { model: Menu & {hideCaret?: B } }) => {
  const
    { name, items, icon, image, hideCaret } = model,
    ref = React.useRef<HTMLDivElement>(null),
    [isMenuHidden, setIsMenuHidden] = React.useState(true),
    toggleMenu = () => setIsMenuHidden(isHidden => !isHidden)

  return (
    // HACK: Marker css class.
    <div data-test={name} className={clas(css.card, 'w-menu')} ref={ref} onClick={toggleMenu}>
      { image && <Fluent.Persona imageUrl={image} size={Fluent.PersonaSize.size48} styles={{ details: { padding: 0 } }} />}
      { icon && <Fluent.FontIcon className={css.icon} iconName={icon} />}
      <Fluent.ContextualMenu
        items={toCommands(items)}
        target={ref}
        hidden={isMenuHidden}
        onDismiss={toggleMenu}
        isBeakVisible
        directionalHint={Fluent.DirectionalHint.bottomRightEdge}
        calloutProps={{ styles: { beak: { border: border(1, cssVar('$neutralQuaternaryAlt')) } } }}
        styles={{ list: { border: border(1, cssVar('$neutralQuaternaryAlt')) } }}
      />
      {!hideCaret && <Fluent.ActionButton iconProps={{ iconName: 'CaretSolidDown', styles: { root: { fontSize: 12 } } }} styles={{ root: { padding: 0 } }} />}
    </div>
  )
}

Review comment (suggestion):
I'm not in favor of reusing this component for the table cell menu - the `& {hideCaret?: B }` breaks pattern, and the styling customizations done here may not be relevant to the table cell menu. It should be fairly simply to code up a menu in the cell_type tsx file.

Code after:
      },
      '&:hover button': {
        color: cssVar('$themePrimary')
      },
    }
  },
  icon: {
    fontSize: 18
  }
})

/**
  Create a contextual menu component. Useful when you have a lot of links and want to conserve the space.
*/
export interface Menu {
  /** Commands to render. */
  items: Command[]
  /** The card's icon. Mutually exclusive with the image. */
  icon?: S
  /** The card’s image, preferably user avatar. Mutually exclusive with the icon. */
  image?: S
  /** An identifying name for this component. */
  name?: Id
}

export const XMenu = ({ model }: { model: Menu }) => {
  const
    { name, items, icon, image } = model,
    ref = React.useRef<HTMLDivElement>(null),
    [isMenuHidden, setIsMenuHidden] = React.useState(true),
    toggleMenu = () => setIsMenuHidden(isHidden => !isHidden)

  return (
    // HACK: Marker css class.
    <div data-test={name} className={clas(css.card, 'w-menu')} ref={ref} onClick={toggleMenu}>
      { image && <Fluent.Persona imageUrl={image} size={Fluent.PersonaSize.size48} styles={{ details: { padding: 0 } }} />}
      { icon && <Fluent.FontIcon className={css.icon} iconName={icon} />}
      <Fluent.ContextualMenu
        items={toCommands(items)}
        target={ref}
        hidden={isMenuHidden}
        onDismiss={toggleMenu}
        isBeakVisible
        directionalHint={Fluent.DirectionalHint.bottomRightEdge}
        calloutProps={{ styles: { beak: { border: border(1, cssVar('$neutralQuaternaryAlt')) } } }}
        styles={{ list: { border: border(1, cssVar('$neutralQuaternaryAlt')) } }}
      />
      <Fluent.ActionButton iconProps={{ iconName: 'CaretSolidDown', styles: { root: { fontSize: 12 } } }} styles={{ root: { padding: 0 } }} />
    </div>
  )
}