diff --git a/src/utils/parseHTML.ts b/src/utils/parseHTML.ts
index 0d6c48e..df93fb4 100644
--- a/src/utils/parseHTML.ts
+++ b/src/utils/parseHTML.ts
@@ -29,7 +29,7 @@ const parseHTML = (html: string): ReactElement | ReactElement[] | string => {
const domChildren = children || [];
if (name && attribs) {
if (name === 'a') {
- return rewriteLink(attribs, domChildren, parseOptions);
+ return rewriteLink(attribs, domChildren, parseOptions, domNode);
}
if (name === 'table') {
return rewriteTable(domChildren, parseOptions);
diff --git a/src/utils/rewriteHeader.ts b/src/utils/rewriteHeader.ts
index a96653d..f3e67f0 100644
--- a/src/utils/rewriteHeader.ts
+++ b/src/utils/rewriteHeader.ts
@@ -1,6 +1,9 @@
import { createElement, ReactElement } from "react";
-import { DomElement } from 'html-react-parser';
+import { DomElement, domToReact, htmlToDOM } from 'html-react-parser';
+/**
+ * Generate the ID for a heading
+ */
const generateID = (title: string): string => {
return title
.replace('&', '-and-')
@@ -11,45 +14,58 @@ const generateID = (title: string): string => {
.replace(/-$/g, '')
.toLowerCase();
}
+
+/**
+ * Get the full plain text of the heading for checking and generating the ID
+ */
+const getFullHeading = (element: DomElement): string => {
+ let text = '';
+ if (element.type === 'text') {
+ text += element.data;
+ }
+
+ if (element.children) {
+ for (const child of element.children) {
+ text += getFullHeading(child);
+ }
+ }
+
+ return text;
+}
+
/**
* If a header has a {#explicit-id}, add it as an attribute
- * @param domNode
*/
const rewriteHeaders = (domNode: DomElement): ReactElement | false => {
if (!domNode.name) {
return false;
}
- const firstChild = domNode.children ? domNode.children[0] : null;
- if (firstChild && firstChild.type === 'text') {
- const { data } = firstChild;
- const matches = data.match(/^(.*?){#([A-Za-z0-9_-]+)\}$/);
+
+ const plainText = getFullHeading(domNode);
+
+ if (plainText) {
+ // const plainText = getFullHeading(firstChild);
+ const matches = plainText.match(/^(.*?)\{#([A-Za-z0-9_-]+)\}$/);
let header;
let id;
if (matches) {
header = matches[1];
id = matches[2];
} else {
- header = data;
- id = generateID(data);
+ header = plainText;
+ id = generateID(plainText);
}
- const anchor = createElement(
- 'a',
- {
- "aria-hidden": true,
- className: 'anchor',
- href: `#${id}`,
- id,
- key: id,
- },
- '#'
- );
+ const anchor = htmlToDOM(`#`)[0];
- return createElement(
- domNode.name,
- {},
- [ header, anchor ]
- );
+ const lastChild = domNode.children ? domNode.children[domNode.children.length - 1] : null;
+ if (lastChild && lastChild.type === 'text') {
+ lastChild.data = lastChild.data.replace(/\s*{#([A-Za-z0-9_-]+)\}$/, '');
+ }
+
+ domNode.children?.push(anchor);
+
+ return domToReact([domNode]);
}
diff --git a/src/utils/rewriteLink.ts b/src/utils/rewriteLink.ts
index 4203aaf..1acb7af 100644
--- a/src/utils/rewriteLink.ts
+++ b/src/utils/rewriteLink.ts
@@ -8,6 +8,7 @@ import { SilverstripeDocument } from '../types';
interface LinkAttributes {
href?: string;
+ class?: string;
};
@@ -31,7 +32,8 @@ const relativeLink = (currentNode: SilverstripeDocument, href: string): string =
const rewriteLink = (
attribs: LinkAttributes,
children: DomElement[],
- parseOptions: HTMLReactParserOptions
+ parseOptions: HTMLReactParserOptions,
+ domNode: DomElement
): ReactElement|false => {
const { href } = attribs;
if (!href) {
@@ -43,6 +45,11 @@ const rewriteLink = (
// hash links
if (href.startsWith('#')) {
+ // Just let normal parsing occur for heading links
+ if (attribs.class === 'anchor') {
+ return domToReact(domNode);
+ }
+ // rewrite all other hashlinks
return createElement(
Link,
{