|
|
|
@ -6,11 +6,13 @@ import { getMediaDimensions } from '@vizality/util/file';
|
|
|
|
|
import { toKebabCase } from '@vizality/util/string';
|
|
|
|
|
import { open as openModal } from '@vizality/modal';
|
|
|
|
|
import { joinClassNames } from '@vizality/util/dom';
|
|
|
|
|
import { getModule } from '@vizality/webpack';
|
|
|
|
|
import { getModule, contextMenu } from '@vizality/webpack';
|
|
|
|
|
import { Regexes } from '@vizality/constants';
|
|
|
|
|
|
|
|
|
|
import { CodeBlock, Icon, DeferredRender, Spinner, LazyImageZoomable, ImageModal, Anchor } from '.';
|
|
|
|
|
import { CodeBlock, Icon, DeferredRender, Spinner, LazyImageZoomable, ImageModal, Anchor, ContextMenu } from '.';
|
|
|
|
|
|
|
|
|
|
const { readFile } = promises;
|
|
|
|
|
const { openContextMenu, closeContextMenu } = contextMenu;
|
|
|
|
|
|
|
|
|
|
export default memo(({ source }) => {
|
|
|
|
|
const [ markdown, setMarkdown ] = useState();
|
|
|
|
@ -33,7 +35,7 @@ export default memo(({ source }) => {
|
|
|
|
|
setMarkdown(source.trim());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}, [ markdown ]);
|
|
|
|
|
}, [ source, markdown ]);
|
|
|
|
|
|
|
|
|
|
const flatten = (text, child) => {
|
|
|
|
|
return typeof child === 'string'
|
|
|
|
@ -49,18 +51,49 @@ export default memo(({ source }) => {
|
|
|
|
|
};
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
const renderers = {
|
|
|
|
|
root: ({ children }) => {
|
|
|
|
|
return children;
|
|
|
|
|
},
|
|
|
|
|
const getHeader = (level, children) => {
|
|
|
|
|
const sizes = [ null, size32, size24, size20, size16, size14, size12 ];
|
|
|
|
|
const text = toKebabCase(children.reduce(flatten, ''));
|
|
|
|
|
const slug = `vz-markdown-header--${generateId(text)}`;
|
|
|
|
|
const Header = `h${level}`;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Header
|
|
|
|
|
id={slug}
|
|
|
|
|
className={joinClassNames('vz-markdown-header', `vz-markdown-header-h${level}`, sizes[level], base)}
|
|
|
|
|
>
|
|
|
|
|
<Anchor className='vz-markdown-anchor' href={`#${slug}`}>
|
|
|
|
|
<Icon
|
|
|
|
|
name='Link'
|
|
|
|
|
className='vz-markdown-anchor-icon'
|
|
|
|
|
iconClassName='vz-markdown-anchor-icon'
|
|
|
|
|
size='16px'
|
|
|
|
|
onContextMenu={evt => openContextMenu(evt, () => (
|
|
|
|
|
<ContextMenu.Menu navId='vz-markdown-link-reference-context-menu' onClose={closeContextMenu}>
|
|
|
|
|
<ContextMenu.Item
|
|
|
|
|
id='copy-link'
|
|
|
|
|
label='Copy Link'
|
|
|
|
|
action={() => {
|
|
|
|
|
try {
|
|
|
|
|
const href = vizality.api.routes.getLocation()?.href;
|
|
|
|
|
const protocolUrl = href?.replace(href?.match(new RegExp(`${Regexes.DISCORD}/`))[0], '');
|
|
|
|
|
DiscordNative?.clipboard?.copy(`<${protocolUrl.replace('vizality', 'vizality:/')}#${slug}>`);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* text: ({ children }) => {
|
|
|
|
|
* return <span className='vz-markdown__span'>{children}</span>;
|
|
|
|
|
* },
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</ContextMenu.Menu>
|
|
|
|
|
))}
|
|
|
|
|
/>
|
|
|
|
|
</Anchor>
|
|
|
|
|
{children}
|
|
|
|
|
</Header>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
paragraph: ({ children }) => {
|
|
|
|
|
const renderers = {
|
|
|
|
|
p: ({ children }) => {
|
|
|
|
|
return <p className='vz-markdown-p'>
|
|
|
|
|
{children}
|
|
|
|
|
</p>;
|
|
|
|
@ -83,13 +116,13 @@ export default memo(({ source }) => {
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
listItem: ({ children }) => {
|
|
|
|
|
li: ({ children }) => {
|
|
|
|
|
return <li className='vz-markdown-list-item'>
|
|
|
|
|
{children}
|
|
|
|
|
</li>;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
link: ({ href, children }) => {
|
|
|
|
|
a: ({ href, children }) => {
|
|
|
|
|
return <>
|
|
|
|
|
<Anchor
|
|
|
|
|
href={href}
|
|
|
|
@ -111,7 +144,7 @@ export default memo(({ source }) => {
|
|
|
|
|
</>;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
emphasis: ({ children }) => {
|
|
|
|
|
em: ({ children }) => {
|
|
|
|
|
return <em className='vz-markdown-em'>{children}</em>;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
@ -123,20 +156,31 @@ export default memo(({ source }) => {
|
|
|
|
|
return <blockquote className='vz-markdown-blockquote'>{children}</blockquote>;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
thematicBreak: () => {
|
|
|
|
|
hr: () => {
|
|
|
|
|
return <hr className='vz-markdown-hr' />;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
code: ({ language, value }) => {
|
|
|
|
|
return <CodeBlock language={language} content={value} className='vz-markdown-code' />;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
inlineCode: ({ children }) => {
|
|
|
|
|
return <code className='vz-markdown-code-inline'>{children}</code>;
|
|
|
|
|
code: ({ inline, className, children }) => {
|
|
|
|
|
const language = className?.match(/(language-)/)?.input?.replace('language-', '');
|
|
|
|
|
return (
|
|
|
|
|
inline
|
|
|
|
|
? (
|
|
|
|
|
<code className='vz-markdown-code-inline'>
|
|
|
|
|
{children}
|
|
|
|
|
</code>
|
|
|
|
|
)
|
|
|
|
|
: (
|
|
|
|
|
<CodeBlock
|
|
|
|
|
language={language}
|
|
|
|
|
content={children?.join()}
|
|
|
|
|
className='vz-markdown-code'
|
|
|
|
|
/>
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// @todo Fix this to work with variable dimensions.
|
|
|
|
|
image: ({ alt, src }) => {
|
|
|
|
|
img: ({ alt, src }) => {
|
|
|
|
|
return <LazyImageZoomable
|
|
|
|
|
className={joinClassNames('vz-markdown-image', imageWrapper)}
|
|
|
|
|
src={src}
|
|
|
|
@ -156,64 +200,44 @@ export default memo(({ source }) => {
|
|
|
|
|
/>;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// @todo Fix this to work with variable dimensions.
|
|
|
|
|
imageReference: ({ alt, src }) => {
|
|
|
|
|
return <LazyImageZoomable
|
|
|
|
|
className={joinClassNames('vz-markdown-image', imageWrapper)}
|
|
|
|
|
src={src}
|
|
|
|
|
alt={alt}
|
|
|
|
|
width='500'
|
|
|
|
|
height='500'
|
|
|
|
|
onClick={e => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
openModal(() =>
|
|
|
|
|
<ImageModal
|
|
|
|
|
src={src}
|
|
|
|
|
width='500'
|
|
|
|
|
height='500'
|
|
|
|
|
/>);
|
|
|
|
|
}}
|
|
|
|
|
/>;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
table: ({ children }) => {
|
|
|
|
|
return <table className='vz-markdown-table'>{children}</table>;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
tableHead: ({ children }) => {
|
|
|
|
|
thead: ({ children }) => {
|
|
|
|
|
return <thead className='vz-markdown-thead'>{children}</thead>;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
tableBody: ({ children }) => {
|
|
|
|
|
tbody: ({ children }) => {
|
|
|
|
|
return <tbody className='vz-markdown-tbody'>{children}</tbody>;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
tableRow: ({ children }) => {
|
|
|
|
|
tr: ({ children }) => {
|
|
|
|
|
return <tr className='vz-markdown-tr'>{children}</tr>;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
tableCell: ({ isHeader, align, children }) => {
|
|
|
|
|
td: ({ isHeader, style, children }) => {
|
|
|
|
|
return isHeader
|
|
|
|
|
? <th className={joinClassNames('vz-markdown-th', { 'vz-markdown-th vz-align-right': align })}>{children}</th>
|
|
|
|
|
: <td className={joinClassNames('vz-markdown-td', { 'vz-markdown-td vz-align-right': align })}>{children}</td>;
|
|
|
|
|
? (
|
|
|
|
|
<th className='vz-markdown-th' style={style}>
|
|
|
|
|
{children}
|
|
|
|
|
</th>
|
|
|
|
|
)
|
|
|
|
|
: (
|
|
|
|
|
<td className='vz-markdown-td' style={style}>
|
|
|
|
|
{children}
|
|
|
|
|
</td>
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
heading: ({ level, children }) => {
|
|
|
|
|
const sizes = [ null, size32, size24, size20, size16, size14, size12 ];
|
|
|
|
|
const text = toKebabCase(children.reduce(flatten, ''));
|
|
|
|
|
const slug = `vz-markdown-header--${generateId(text)}`;
|
|
|
|
|
const Header = `h${level}`;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Header id={slug} className={joinClassNames('vz-markdown-header', `vz-markdown-header-h${level}`, sizes[level], base)}>
|
|
|
|
|
<Anchor className='vz-markdown-anchor' href={`#${slug}`}>
|
|
|
|
|
<Icon name='Link' className='vz-markdown-anchor-icon' iconClassName='vz-markdown-anchor-icon' size='16px' />
|
|
|
|
|
</Anchor>
|
|
|
|
|
{children}
|
|
|
|
|
</Header>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
h1: ({ level, children }) => getHeader(level, children),
|
|
|
|
|
h2: ({ level, children }) => getHeader(level, children),
|
|
|
|
|
h3: ({ level, children }) => getHeader(level, children),
|
|
|
|
|
h4: ({ level, children }) => getHeader(level, children),
|
|
|
|
|
h5: ({ level, children }) => getHeader(level, children),
|
|
|
|
|
h6: ({ level, children }) => getHeader(level, children)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<>
|
|
|
|
|
{existsSync(source)
|
|
|
|
@ -223,10 +247,10 @@ export default memo(({ source }) => {
|
|
|
|
|
<Spinner />
|
|
|
|
|
</div>
|
|
|
|
|
}>
|
|
|
|
|
<Markdown source={markdown} renderers={renderers} />
|
|
|
|
|
<Markdown children={markdown} components={renderers} />
|
|
|
|
|
</DeferredRender>
|
|
|
|
|
)
|
|
|
|
|
: <Markdown source={markdown} renderers={renderers} />
|
|
|
|
|
: <Markdown children={markdown} components={renderers} />
|
|
|
|
|
}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|