aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/Markdown/CodeBlock.tsx2
-rw-r--r--src/lib/Markdown/Content.tsx2
-rw-r--r--src/lib/Markdown/InlineSyntax.tsx81
-rw-r--r--src/lib/Markdown/Paragraph.tsx100
-rw-r--r--src/lib/Markdown/types.ts4
5 files changed, 91 insertions, 98 deletions
diff --git a/src/lib/Markdown/CodeBlock.tsx b/src/lib/Markdown/CodeBlock.tsx
index ee2114e..2718647 100644
--- a/src/lib/Markdown/CodeBlock.tsx
+++ b/src/lib/Markdown/CodeBlock.tsx
@@ -3,7 +3,7 @@ import { ParserPropTypes } from './types';
const CodeBlock: React.FC<ParserPropTypes> = ({ rawLines }) => {
return (
- <p style={{background: '#444444', padding: '8px' }}>
+ <p style={{background: 'rgba(255, 255, 255, .1)', padding: '8px' }}>
{rawLines.map(line => <> {line} <br/> </>)}
</p>
);
diff --git a/src/lib/Markdown/Content.tsx b/src/lib/Markdown/Content.tsx
index 2a9e2ee..d0a2193 100644
--- a/src/lib/Markdown/Content.tsx
+++ b/src/lib/Markdown/Content.tsx
@@ -20,7 +20,7 @@ const Content: React.FC<ParserPropTypes> = ({ rawLines }) => {
const codeBlockLines = rawLines.splice(0, closeIndex + 1).slice(0, closeIndex);
result = <CodeBlock rawLines={codeBlockLines} />
} else {
- result = <Paragraph data={line} />
+ result = <Paragraph line={line} />
}
return (
diff --git a/src/lib/Markdown/InlineSyntax.tsx b/src/lib/Markdown/InlineSyntax.tsx
new file mode 100644
index 0000000..bf5669d
--- /dev/null
+++ b/src/lib/Markdown/InlineSyntax.tsx
@@ -0,0 +1,81 @@
+import React from 'react';
+import { Link } from '@material-ui/core';
+import axios from 'axios';
+
+import { InlineParserPropTypes } from './types';
+
+interface RegexPair {
+ global: RegExp;
+ local: RegExp;
+}
+
+interface Emoji {
+ name: string;
+ char: string;
+}
+
+const enclosureRegex = (e: string): RegexPair => ({
+ local: new RegExp(`${e}([^${e}]+)${e}`),
+ global: new RegExp(`(${e}[^${e}]+${e})`)
+});
+
+const regex: Record<string, RegexPair> = {
+ conceal: {
+ global: /(!?\[.+?\]\(.+?\))/g,
+ local: /!?\[(.+?)\]\((.+?)\)/
+ },
+ rawLink: {
+ global: /((?:(?:[A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)(?:(?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\\w]*))?)/,
+ local: /&^/
+ },
+ emoji: enclosureRegex(':'),
+ bold: enclosureRegex('\\*\\*'),
+ italic: enclosureRegex('\\*'),
+ code: enclosureRegex('`'),
+ strikeThrough: enclosureRegex('~~'),
+}
+
+const splitter = new RegExp(Object.values(regex).map(pair => pair.global.source).join('|'));
+
+const emojiList: Emoji[] = [];
+axios.get('https://unpkg.com/emojilib@2.4.0/emojis.json').then(response => {
+ Object.keys(response.data).forEach(name => emojiList.push({ name, char: response.data[name].char }));
+});
+
+
+const InlineSyntax: React.FC<InlineParserPropTypes> = ({ line }) => {
+ if (!line) return null;
+
+ const matchConceal = regex.conceal.local.exec(line);
+ if (matchConceal) {
+ if (line[0] === '!') return <img src={matchConceal[2]} alt={matchConceal[1]} style={{ maxWidth: '100%', maxHeight: '100%' }} />;
+ return <Link href={matchConceal[2]}>{matchConceal[1]}</Link>;
+ }
+
+ const matchEmoji = line.match(regex.emoji.local);
+ if (matchEmoji) {
+ const emoji = emojiList.find(emoji => emoji.name === matchEmoji[1]);
+ return <span>{emoji ? emoji.char : line}</span>;
+ }
+
+ const matchCode = line.match(regex.code.local);
+ if (matchCode) return <span style={{ background: 'rgba(255, 255, 255, .1)' }}>{matchCode[1]}</span>;
+
+ const matchBold = line.match(regex.bold.local);
+ if (matchBold) return <b>{matchBold[1]}</b>;
+
+ const matchItalic = line.match(regex.italic.local);
+ if (matchItalic) return <i>{matchItalic[1]}</i>;
+
+ const matchStrikeThrough = line.match(regex.strikeThrough.local);
+ if (matchStrikeThrough) return <span style={{textDecoration: 'line-through' }}>{matchStrikeThrough[1]}</span>;
+
+ if (line.match(regex.rawLink.global)) return <Link href={line}>{line}</Link>;
+
+ return <>{line}</>;
+}
+
+
+export { splitter };
+export default InlineSyntax;
+
diff --git a/src/lib/Markdown/Paragraph.tsx b/src/lib/Markdown/Paragraph.tsx
index 19f8382..f46199e 100644
--- a/src/lib/Markdown/Paragraph.tsx
+++ b/src/lib/Markdown/Paragraph.tsx
@@ -1,101 +1,9 @@
import React from 'react';
-import { Link } from '@material-ui/core';
-import axios from 'axios';
+import { InlineParserPropTypes } from './types';
+import InlineSyntax, { splitter } from './InlineSyntax';
-interface PropTypes {
- data: string;
-}
-
-interface Emoji {
- name: string;
- char: string;
-}
-
-type InlineRuleName = 'bold' | 'italic' | 'code' | 'strikeThrough' | 'emoji';
-
-interface InlineRuleProperties {
- enclosure: string;
- style: React.CSSProperties;
- pattern?: RegExp;
-}
-
-const inlineRules: Record<InlineRuleName, InlineRuleProperties>= {
- // Order matters - lowest property has highest priority
- strikeThrough: {
- enclosure: '~~',
- style: { textDecoration: 'line-through' },
- },
- code: {
- enclosure: '`',
- style: { background: '#444444', padding: '4px' },
- },
- italic: {
- enclosure: '\\*',
- style: { fontStyle: 'italic' },
- },
- bold: {
- enclosure: '\\*\\*',
- style: { fontWeight: 'bold' },
- },
- emoji: {
- enclosure: ':',
- style: {},
- }
-};
-
-const inlineRuleNames = Object.keys(inlineRules) as InlineRuleName[];
-
-const captureInline = (enclosure: string): RegExp => {
- return new RegExp(enclosure + '([^' + enclosure + ']+)' + enclosure);
-}
-const captureInlineSplit = (enclosure: string): string => {
- return '(' + enclosure + '[^' + enclosure + ']+' + enclosure + ')';
-}
-const concealRegex = /!?\[(.+?)\]\((.+?)\)/;
-const concealRegexSplit = /(!?\[.+?\]\(.+?\))/g;
-const rawLinkRegex = /((?:(?:[A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)(?:(?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\\w]*))?)/;
-
-const ruleSplitPatterns: string[] = [concealRegexSplit.source, rawLinkRegex.source];
-inlineRuleNames.forEach(name => {
- const enclosure = inlineRules[name].enclosure;
- inlineRules[name].pattern = captureInline(enclosure);
- ruleSplitPatterns.push(captureInlineSplit(enclosure));
-});
-
-const splitter = new RegExp(ruleSplitPatterns.join('|'));
-
-const emojiList: Emoji[] = [];
-axios.get('https://unpkg.com/emojilib@2.4.0/emojis.json').then(response => {
- Object.keys(response.data).forEach(name => emojiList.push({ name, char: response.data[name].char }));
-});
-
-const SyntaxSpan: React.FC<PropTypes> = ({ data }) => {
- if (!data) return null;
-
- const conceal = concealRegex.exec(data);
- if (conceal) {
- if (data[0] === '!') return <img src={conceal[2]} alt={conceal[1]} style={{ maxWidth: '100%', maxHeight: '100%' }} />;
- return <Link href={conceal[2]}>{conceal[1]}</Link>;
- }
-
- if (data.match(rawLinkRegex)) return <Link href={data}>{data}</Link>;
-
- let span = <>{data}</>;
- inlineRuleNames.forEach(name => {
- const rule = inlineRules[name];
- const match = data.match(rule.pattern || '');
- if (match) {
- if (name === 'emoji') {
- const emoji = emojiList.find(emoji => emoji.name === match[1]);
- span = <span>{emoji ? emoji.char : ''}</span>;
- } else span = <span style={rule.style}>{match[1]}</span>;
- }
- });
- return span;
-}
-
-const Paragraph: React.FC<PropTypes> = ({ data }) => {
- const result = data.split(splitter).map(span => <SyntaxSpan data={span} />);
+const Paragraph: React.FC<InlineParserPropTypes> = ({ line }) => {
+ const result = line.split(splitter).map(span => <InlineSyntax line={span} />);
return <p> {result} </p>;
}
diff --git a/src/lib/Markdown/types.ts b/src/lib/Markdown/types.ts
index 0b6f4b6..8fb28ed 100644
--- a/src/lib/Markdown/types.ts
+++ b/src/lib/Markdown/types.ts
@@ -1,3 +1,7 @@
+export interface InlineParserPropTypes {
+ line: string;
+}
+
export interface ParserPropTypes {
rawLines: string[];
}