diff --git a/plugins/qeta-react/src/components/Buttons/LinkButton.tsx b/plugins/qeta-react/src/components/Buttons/LinkButton.tsx
index 24d2954d..4a834a40 100644
--- a/plugins/qeta-react/src/components/Buttons/LinkButton.tsx
+++ b/plugins/qeta-react/src/components/Buttons/LinkButton.tsx
@@ -6,6 +6,7 @@ import {
import { useTranslation } from '../../hooks';
import { IconButton, Tooltip } from '@material-ui/core';
import Link from '@material-ui/icons/Link';
+import { alertApiRef, useApi } from '@backstage/core-plugin-api';
export const LinkButton = (props: {
entity: PostResponse | AnswerResponse;
@@ -13,12 +14,18 @@ export const LinkButton = (props: {
}) => {
const isQuestion = 'title' in props.entity;
const { t } = useTranslation();
+ const alertApi = useApi(alertApiRef);
const copyToClipboard = () => {
const url = new URL(window.location.href);
if (!isQuestion) {
url.hash = `#answer_${props.entity.id}`;
}
window.navigator.clipboard.writeText(url.toString());
+ alertApi.post({
+ message: t('link.copied'),
+ severity: 'info',
+ display: 'transient',
+ });
};
return (
diff --git a/plugins/qeta-react/src/components/MarkdownRenderer/MarkdownRenderer.tsx b/plugins/qeta-react/src/components/MarkdownRenderer/MarkdownRenderer.tsx
index ff7dddfc..e76c6395 100644
--- a/plugins/qeta-react/src/components/MarkdownRenderer/MarkdownRenderer.tsx
+++ b/plugins/qeta-react/src/components/MarkdownRenderer/MarkdownRenderer.tsx
@@ -1,16 +1,19 @@
-import React, { PropsWithChildren } from 'react';
+import React, { PropsWithChildren, useEffect } from 'react';
import SyntaxHighlighter from 'react-syntax-highlighter';
import ReactMarkdown from 'react-markdown';
import {
a11yDark,
a11yLight,
} from 'react-syntax-highlighter/dist/esm/styles/hljs';
-import { makeStyles } from '@material-ui/core';
+import { IconButton, makeStyles, Tooltip } from '@material-ui/core';
import { findUserMentions } from '@drodil/backstage-plugin-qeta-common';
import gfm from 'remark-gfm';
import { EntityRefLink } from '@backstage/plugin-catalog-react';
import { useIsDarkTheme } from '../../hooks/useIsDarkTheme';
import { BackstageOverrides } from '@backstage/core-components';
+import LinkIcon from '@material-ui/icons/Link';
+import { alertApiRef, useApi } from '@backstage/core-plugin-api';
+import { useTranslation } from '../../hooks';
export type QetaMarkdownContentClassKey = 'markdown';
@@ -114,6 +117,15 @@ const useStyles = makeStyles(
? overrides.BackstageMarkdownContent
: {}),
},
+ header: {
+ '& .anchor-link': {
+ display: 'none',
+ marginLeft: '0.5em',
+ },
+ '&:hover .anchor-link': {
+ display: 'inline-block',
+ },
+ },
};
},
{ name: 'QetaMarkdownContent' },
@@ -127,23 +139,69 @@ const flatten = (text: string, child: any): string => {
: React.Children.toArray(child.props.children).reduce(flatten, text);
};
-const headingRenderer = (
- props: PropsWithChildren<{ node: { tagName: string } }>,
-) => {
- const { node, children } = props;
- const childrenArray = React.Children.toArray(children);
- const text = childrenArray.reduce(flatten, '');
- const slug = text.toLocaleLowerCase('en-US').replace(/\W/g, '-');
- return React.createElement(`${node.tagName}`, { id: slug }, children);
-};
-
export const MarkdownRenderer = (props: {
content: string;
className?: string;
}) => {
const { content, className: mainClassName } = props;
const darkTheme = useIsDarkTheme();
+ const { t } = useTranslation();
const classes = useStyles();
+ const alertApi = useApi(alertApiRef);
+
+ const copyToClipboard = (slug: string) => {
+ const url = new URL(window.location.href);
+ url.hash = `#${slug}`;
+ window.navigator.clipboard.writeText(url.toString());
+ alertApi.post({
+ message: t('link.copied'),
+ severity: 'info',
+ display: 'transient',
+ });
+ };
+
+ const headingRenderer = (
+ hProps: PropsWithChildren<{ node: { tagName: string } }>,
+ ) => {
+ const { node, children } = hProps;
+ const childrenArray = React.Children.toArray(children);
+ const text = childrenArray.reduce(flatten, '');
+ const slug = text.toLocaleLowerCase('en-US').replace(/\W/g, '-');
+ const link = (
+
+ copyToClipboard(slug)}
+ size="small"
+ className="anchor-link"
+ >
+
+
+
+ );
+ return (
+ <>
+ {React.createElement(
+ `${node.tagName}`,
+ { id: slug, className: classes.header },
+ [children, link],
+ )}
+ >
+ );
+ };
+
+ useEffect(() => {
+ if (!window.location.hash) {
+ return;
+ }
+
+ const id = window.location.hash.slice(1);
+ const element = document.getElementById(id);
+ if (element) {
+ element.scrollIntoView({ behavior: 'auto', block: 'start' });
+ }
+ }, []);
+
return (