diff --git a/public/app/core/components/AppChrome/Breadcrumbs.tsx b/public/app/core/components/AppChrome/Breadcrumbs.tsx deleted file mode 100644 index 7f64568c98f..00000000000 --- a/public/app/core/components/AppChrome/Breadcrumbs.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { css } from '@emotion/css'; -import React from 'react'; - -import { GrafanaTheme2, NavModelItem } from '@grafana/data'; -import { useStyles2, Icon, IconName, LinkButton } from '@grafana/ui'; - -export interface Props { - breadcrumbs: Breadcrumb[]; -} - -export interface Breadcrumb { - text?: string; - icon?: IconName; - href?: string; -} - -export function Breadcrumbs({ breadcrumbs }: Props) { - const styles = useStyles2(getStyles); - - return ( - - ); -} - -export function buildBreadcrumbs(sectionNav: NavModelItem, pageNav?: NavModelItem) { - const crumbs: Breadcrumb[] = [{ icon: 'home-alt', href: '/' }]; - - function addCrumbs(node: NavModelItem) { - if (node.parentItem) { - addCrumbs(node.parentItem); - } - - crumbs.push({ text: node.text, href: node.url }); - } - - addCrumbs(sectionNav); - - if (pageNav) { - addCrumbs(pageNav); - } - - return crumbs; -} - -const getStyles = (theme: GrafanaTheme2) => { - return { - breadcrumbs: css({ - display: 'flex', - alignItems: 'center', - flexWrap: 'nowrap', - fontWeight: theme.typography.fontWeightMedium, - }), - breadcrumb: css({ - display: 'flex', - alignItems: 'center', - }), - separator: css({ - color: theme.colors.text.secondary, - }), - breadcrumbLink: css({ - alignItems: 'center', - color: theme.colors.text.primary, - display: 'flex', - padding: theme.spacing(0, 0.5), - whiteSpace: 'nowrap', - '&:hover': { - textDecoration: 'underline', - }, - }), - }; -}; diff --git a/public/app/core/components/AppChrome/NavToolbar.tsx b/public/app/core/components/AppChrome/NavToolbar.tsx index 424a62fe92d..260e4229e07 100644 --- a/public/app/core/components/AppChrome/NavToolbar.tsx +++ b/public/app/core/components/AppChrome/NavToolbar.tsx @@ -4,7 +4,9 @@ import React from 'react'; import { GrafanaTheme2, NavModelItem } from '@grafana/data'; import { Icon, IconButton, ToolbarButton, useStyles2 } from '@grafana/ui'; -import { Breadcrumbs, buildBreadcrumbs } from './Breadcrumbs'; +import { Breadcrumbs } from '../Breadcrumbs/Breadcrumbs'; +import { buildBreadcrumbs } from '../Breadcrumbs/utils'; + import { NavToolbarSeparator } from './NavToolbarSeparator'; import { TOP_BAR_LEVEL_HEIGHT } from './types'; diff --git a/public/app/core/components/Breadcrumbs/BreadcrumbItem.tsx b/public/app/core/components/Breadcrumbs/BreadcrumbItem.tsx new file mode 100644 index 00000000000..7b41f9cccbe --- /dev/null +++ b/public/app/core/components/Breadcrumbs/BreadcrumbItem.tsx @@ -0,0 +1,69 @@ +import { css, cx } from '@emotion/css'; +import React from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { Icon, LinkButton, useStyles2 } from '@grafana/ui'; + +import { Breadcrumb } from './types'; + +type Props = Breadcrumb & { + isCurrent: boolean; +}; + +export function BreadcrumbItem(props: Props) { + const styles = useStyles2(getStyles); + return ( +
  • + {props.isCurrent ? ( + + {props.text} + + ) : ( + <> + {'icon' in props ? ( + + ) : ( + + {props.text} + + )} +
    + +
    + + )} +
  • + ); +} + +const getStyles = (theme: GrafanaTheme2) => { + return { + breadcrumb: css({ + alignItems: 'center', + display: 'flex', + padding: theme.spacing(0, 0.5), + whiteSpace: 'nowrap', + }), + breadcrumbLink: css({ + '&:hover': { + textDecoration: 'underline', + }, + }), + breadcrumbWrapper: css({ + alignItems: 'center', + color: theme.colors.text.primary, + display: 'flex', + fontWeight: theme.typography.fontWeightMedium, + }), + separator: css({ + color: theme.colors.text.secondary, + }), + }; +}; diff --git a/public/app/core/components/Breadcrumbs/Breadcrumbs.tsx b/public/app/core/components/Breadcrumbs/Breadcrumbs.tsx new file mode 100644 index 00000000000..c08a1d67d27 --- /dev/null +++ b/public/app/core/components/Breadcrumbs/Breadcrumbs.tsx @@ -0,0 +1,36 @@ +import { css } from '@emotion/css'; +import React from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { useStyles2 } from '@grafana/ui'; + +import { BreadcrumbItem } from './BreadcrumbItem'; +import { Breadcrumb } from './types'; + +export interface Props { + breadcrumbs: Breadcrumb[]; +} + +export function Breadcrumbs({ breadcrumbs }: Props) { + const styles = useStyles2(getStyles); + + return ( + + ); +} + +const getStyles = (theme: GrafanaTheme2) => { + return { + breadcrumbs: css({ + display: 'flex', + alignItems: 'center', + flexWrap: 'nowrap', + }), + }; +}; diff --git a/public/app/core/components/Breadcrumbs/types.ts b/public/app/core/components/Breadcrumbs/types.ts new file mode 100644 index 00000000000..271d16b4dc4 --- /dev/null +++ b/public/app/core/components/Breadcrumbs/types.ts @@ -0,0 +1,12 @@ +import { IconName } from '@grafana/ui'; + +interface TextBreadcrumb { + text: string; + href: string; +} + +interface IconBreadcrumb extends TextBreadcrumb { + icon: IconName; +} + +export type Breadcrumb = TextBreadcrumb | IconBreadcrumb; diff --git a/public/app/core/components/Breadcrumbs/utils.ts b/public/app/core/components/Breadcrumbs/utils.ts new file mode 100644 index 00000000000..6bea1e7bf06 --- /dev/null +++ b/public/app/core/components/Breadcrumbs/utils.ts @@ -0,0 +1,23 @@ +import { NavModelItem } from '@grafana/data'; + +import { Breadcrumb } from './types'; + +export function buildBreadcrumbs(sectionNav: NavModelItem, pageNav?: NavModelItem) { + const crumbs: Breadcrumb[] = [{ icon: 'home-alt', href: '/', text: 'Home' }]; + + function addCrumbs(node: NavModelItem) { + if (node.parentItem) { + addCrumbs(node.parentItem); + } + + crumbs.push({ text: node.text, href: node.url ?? '' }); + } + + addCrumbs(sectionNav); + + if (pageNav) { + addCrumbs(pageNav); + } + + return crumbs; +}