mirror of
https://github.com/silverstripe/doc.silverstripe.org
synced 2024-10-22 15:05:50 +00:00
Merge pull request #241 from creative-commoners/pulls/master/fix-headings
FIX Don't break headings when they include sub-elements.
This commit is contained in:
commit
415351f12e
@ -29,7 +29,7 @@ const parseHTML = (html: string): ReactElement | ReactElement[] | string => {
|
|||||||
const domChildren = children || [];
|
const domChildren = children || [];
|
||||||
if (name && attribs) {
|
if (name && attribs) {
|
||||||
if (name === 'a') {
|
if (name === 'a') {
|
||||||
return rewriteLink(attribs, domChildren, parseOptions);
|
return rewriteLink(attribs, domChildren, parseOptions, domNode);
|
||||||
}
|
}
|
||||||
if (name === 'table') {
|
if (name === 'table') {
|
||||||
return rewriteTable(domChildren, parseOptions);
|
return rewriteTable(domChildren, parseOptions);
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import { createElement, ReactElement } from "react";
|
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 => {
|
const generateID = (title: string): string => {
|
||||||
return title
|
return title
|
||||||
.replace('&', '-and-')
|
.replace('&', '-and-')
|
||||||
@ -11,45 +14,58 @@ const generateID = (title: string): string => {
|
|||||||
.replace(/-$/g, '')
|
.replace(/-$/g, '')
|
||||||
.toLowerCase();
|
.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
|
* If a header has a {#explicit-id}, add it as an attribute
|
||||||
* @param domNode
|
|
||||||
*/
|
*/
|
||||||
const rewriteHeaders = (domNode: DomElement): ReactElement | false => {
|
const rewriteHeaders = (domNode: DomElement): ReactElement | false => {
|
||||||
if (!domNode.name) {
|
if (!domNode.name) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const firstChild = domNode.children ? domNode.children[0] : null;
|
|
||||||
if (firstChild && firstChild.type === 'text') {
|
const plainText = getFullHeading(domNode);
|
||||||
const { data } = firstChild;
|
|
||||||
const matches = data.match(/^(.*?){#([A-Za-z0-9_-]+)\}$/);
|
if (plainText) {
|
||||||
|
// const plainText = getFullHeading(firstChild);
|
||||||
|
const matches = plainText.match(/^(.*?)\{#([A-Za-z0-9_-]+)\}$/);
|
||||||
let header;
|
let header;
|
||||||
let id;
|
let id;
|
||||||
if (matches) {
|
if (matches) {
|
||||||
header = matches[1];
|
header = matches[1];
|
||||||
id = matches[2];
|
id = matches[2];
|
||||||
} else {
|
} else {
|
||||||
header = data;
|
header = plainText;
|
||||||
id = generateID(data);
|
id = generateID(plainText);
|
||||||
}
|
}
|
||||||
|
|
||||||
const anchor = createElement(
|
const anchor = htmlToDOM(`<a id="${id}" class="anchor" aria-hidden="true" href="#${id}">#</a>`)[0];
|
||||||
'a',
|
|
||||||
{
|
|
||||||
"aria-hidden": true,
|
|
||||||
className: 'anchor',
|
|
||||||
href: `#${id}`,
|
|
||||||
id,
|
|
||||||
key: id,
|
|
||||||
},
|
|
||||||
'#'
|
|
||||||
);
|
|
||||||
|
|
||||||
return createElement(
|
const lastChild = domNode.children ? domNode.children[domNode.children.length - 1] : null;
|
||||||
domNode.name,
|
if (lastChild && lastChild.type === 'text') {
|
||||||
{},
|
lastChild.data = lastChild.data.replace(/\s*{#([A-Za-z0-9_-]+)\}$/, '');
|
||||||
[ header, anchor ]
|
}
|
||||||
);
|
|
||||||
|
domNode.children?.push(anchor);
|
||||||
|
|
||||||
|
return domToReact([domNode]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import { SilverstripeDocument } from '../types';
|
|||||||
|
|
||||||
interface LinkAttributes {
|
interface LinkAttributes {
|
||||||
href?: string;
|
href?: string;
|
||||||
|
class?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -31,7 +32,8 @@ const relativeLink = (currentNode: SilverstripeDocument, href: string): string =
|
|||||||
const rewriteLink = (
|
const rewriteLink = (
|
||||||
attribs: LinkAttributes,
|
attribs: LinkAttributes,
|
||||||
children: DomElement[],
|
children: DomElement[],
|
||||||
parseOptions: HTMLReactParserOptions
|
parseOptions: HTMLReactParserOptions,
|
||||||
|
domNode: DomElement
|
||||||
): ReactElement|false => {
|
): ReactElement|false => {
|
||||||
const { href } = attribs;
|
const { href } = attribs;
|
||||||
if (!href) {
|
if (!href) {
|
||||||
@ -52,6 +54,11 @@ const rewriteLink = (
|
|||||||
|
|
||||||
// hash links
|
// hash links
|
||||||
if (href.startsWith('#')) {
|
if (href.startsWith('#')) {
|
||||||
|
// Just let normal parsing occur for heading links
|
||||||
|
if (attribs.class === 'anchor') {
|
||||||
|
return domToReact(domNode);
|
||||||
|
}
|
||||||
|
// rewrite all other hashlinks
|
||||||
return createElement(
|
return createElement(
|
||||||
Link,
|
Link,
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user