-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Is this working with unified v10? #49
Comments
You're probably done with this by now, but for future googlers: Remark recommends you use remark-directive instead. Recommendation: https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins |
@eestein any tips on how to reproduce the callouts in this plugin? |
@fmonper1 you have to create your own plugin and the CSS classes. I'm gonna share the one I created: const acceptableCalloutTypes = {
'note': {cssClass: '', iconClass: 'comment-alt-lines'},
'tip': {cssClass: 'is-success', iconClass: 'lightbulb'},
'info': {cssClass: 'is-info', iconClass: 'info-circle'},
'warning': {cssClass: 'is-warning', iconClass: 'exclamation-triangle'},
'danger': {cssClass: 'is-danger', iconClass: 'siren-on'}
};
/**
* Plugin to generate callout blocks.
*/
function calloutsPlugin() {
return (tree) => {
visit(tree, (node) => {
if (node.type === 'textDirective' || node.type === 'leafDirective' || node.type === 'containerDirective') {
if (!Object.keys(acceptableCalloutTypes).includes(node.name)) {
return;
}
const boxInfo = acceptableCalloutTypes[node.name];
// Adding CSS classes according to the type.
const data = node.data || (node.data = {});
const tagName = node.type === 'textDirective' ? 'span' : 'div';
data.hName = tagName;
data.hProperties = h(tagName, {class: `message ${boxInfo.cssClass}`}).properties;
// Creating the icon.
const icon = h('i');
const iconData = icon.data || (icon.data = {});
iconData.hName = 'i';
iconData.hProperties = h('i', {class: `far fa-${boxInfo.iconClass} md-callout-icon`}).properties;
// Creating the icon's column.
const iconWrapper = h('div');
const iconWrapperData = iconWrapper.data || (iconWrapper.data = {});
iconWrapperData.hName = 'div';
iconWrapperData.hProperties = h('div', {class: 'column is-narrow'}).properties;
iconWrapper.children = [icon];
// Creating the content's column.
const contentColWrapper = h('div');
const contentColWrapperData = contentColWrapper.data || (contentColWrapper.data = {});
contentColWrapperData.hName = 'div';
contentColWrapperData.hProperties = h('div', {class: 'column'}).properties;
contentColWrapper.children = [...node.children]; // Adding markdown's content block.
// Creating the column's wrapper.
const columnsWrapper = h('div');
const columnsWrapperData = columnsWrapper.data || (columnsWrapper.data = {});
columnsWrapperData.hName = 'div';
columnsWrapperData.hProperties = h('div', {class: 'columns'}).properties;
columnsWrapper.children = [iconWrapper, contentColWrapper];
// Creating the wrapper for the callout's content.
const contentWrapper = h('div');
const wrapperData = contentWrapper.data || (contentWrapper.data = {});
wrapperData.hName = 'div';
wrapperData.hProperties = h('div', {class: 'message-body'}).properties;
contentWrapper.children = [columnsWrapper];
node.children = [contentWrapper];
}
});
};
} And the usage in my markdown files remains the same: :::info
Message
::: Remember that for the styling to work you must add your CSS and follow my code's structure, if you're copying/pasting. This is the CSS FW I used: |
@eestein, thank you so much for sharing your plugin! It's working wonderfully for me and you saved me a ton of time. In the spirit of continuing to help any others that come across this, I'll also share a few tweaks I made.
/** @typedef {import('remark-directive')} */
/** @typedef {import('unified').Plugin<[Settings], import('mdast').Root>} Plugin */
import { h, s } from 'hastscript';
import { visit } from 'unist-util-visit';
/**
* @typedef {{ title?: string, size?: number }} Attributes
* @typedef {Object} Settings
*/
const callouts = {
note: { color: 'brandTan', icon: 'h-clipboard-list', title: 'Note' },
tip: { color: 'success', icon: 'h-clipboard-check', title: 'Tip' },
info: { color: 'primary', icon: 'i-info', title: 'Info' },
warning: { color: 'warning', icon: 'i-alert-triangle', title: 'Warning' },
danger: { color: 'danger', icon: 'i-alert-octagon', title: 'Danger' },
};
const iconSizeMap = /** @type {Record<number, string>} */ ({
4: 'large',
5: 'medium',
6: 'small',
});
const spacingMap = /** @type {Record<number, string>} */ ({
4: '300',
5: '200',
6: '100',
});
/**
* Recursively walk a `hast` tree and decorate each node with the metadata
* required for `remark-directive`
*
* @param {JSX.Element} node
*/
const decorateHast = node => {
Object.assign(node.data ?? (node.data = {}), {
hName: node.tagName,
hProperties: node.properties,
});
if (node.children && Array.isArray(node.children)) {
node.children.forEach(decorateHast);
}
};
/**
* Check if directive `name` is a supported callout
*
* @param {string} name
* @returns {name is keyof typeof callouts}
*/
const isSupportedCallout = name => Object.keys(callouts).includes(name);
/**
* Remark plugin to support block-quote style callouts with the same syntax
* introduced in `remark-admonition` which is apparently no longer supported in
* the latest version of Remark.
*
* @see {@link https://github.com/elviswolcott/remark-admonitions/issues/49#issuecomment-1162400177}
* @see {@link https://github.com/remarkjs/remark-directive#examples}
*
* @type {Plugin}
*/
const plugin = () => tree => {
visit(tree, node => {
if (
!(
node.type === 'textDirective' ||
node.type === 'leafDirective' ||
node.type === 'containerDirective'
)
) {
return;
}
if (!isSupportedCallout(node.name)) return;
// Grab attributes from the directive and apply defaults
const { color, icon, title: defaultTitle } = callouts[node.name];
const { title = defaultTitle, size = '6' } = node.attributes ?? {};
// Next, build up all of the elements that are going to make up the
// callout DOM structure in `hast`. These are separated out as nesting all
// of the `hastscript` `h` and `s` calls would get a little unweildy
// compared to something like JSX.
// Icon -----------------
const iconSize = iconSizeMap[size] ?? 'small';
const svg = s(
'svg',
{
xmlns: 'http://www.w3.org/2000/svg',
class: `icon icon-${iconSize} text-${color}-700 m-0`,
},
[s('use', { 'xlink:href': `/icons/all.svg#${icon}` })],
);
// Heading --------------
const heading = h(
`h${size}`,
{ class: `m-0 fw-bodySemiBold text-${color}-700` },
[{ type: 'text', value: title }],
);
// Title --------------
const spacing = spacingMap[size] ?? '100';
const titleContainer =
// Wrapping just the title container in `.hover-bootstrap`
// to apply the Bootstrap theme for the icon and heading
h('span', { class: 'hover-bootstrap' }, [
h(
'span',
{
class: `d-inline-flex align-items-center gap-${spacing} mb-${spacing}`,
},
[svg, heading],
),
]);
// Body --------------
const body = h('div', { class: 'callout-body' }, [
titleContainer,
// Actual Markdown content for the callout
h('div', { class: 'column' }, [...node.children]),
]);
// Mutate the actual node we're visiting to attach the `hast`
// tree we've built up with all of the elements we're inserting
node.tagName = node.type === 'textDirective' ? 'span' : 'blockquote';
node.properties = { class: `message`, 'data-color-scheme': color };
node.children = [body];
// Finally we need to walk this whole `hast` tree we've built and augment
// each node with the metadata that `remark-directive` requires
decorateHast(node);
});
};
export default plugin; Usage:::note
The icons must always come _after_ the `<input>` element
::: :::note{title="Anchor must accept a ref"}
As with [`trigger`](#basic-usage), if you pass a custom component inside
`anchor` ensure that it uses `forwardRef` so the popover is triggered
successfully.
::: :::info{title="Core Concepts" size="5"}
By the end of this walkthrough, you'll have a solid understanding of:
- Contributing a bug fix
- Adding changelog information associated with your fix
- Getting your fix released using continuous integration
::: |
I am getting
Cannot read properties of undefined (reading 'blockTokenizers')
In the DOM when I use this plugin with react-markdown.
Given that this project seems more or less fully abandoned, is there a replacement that does something similar? Are we waiting for someone to fork this. I tried to figure out how to fix this plugin but given the very sparse documentation on how to write unified plugins, I got very confused.
The text was updated successfully, but these errors were encountered: