stoat-for-desktop/components/markdown/plugins/unicodeEmoji.tsx

103 lines
2.2 KiB
TypeScript

import { Handler } from "mdast-util-to-hast";
import { Plugin } from "unified";
import { visit } from "unist-util-visit";
import { UnicodeEmoji } from "../emoji";
import {
RE_UNICODE_EMOJI,
UNICODE_EMOJI_MAX_PACK,
UNICODE_EMOJI_MIN_PACK,
UNICODE_EMOJI_PUA_PACK,
UnicodeEmojiPacks,
} from "../emoji/UnicodeEmoji";
/**
* Render Unicode emoji
*/
export function RenderUnicodeEmoji(props: {
str: string;
pack: UnicodeEmojiPacks;
}) {
return <UnicodeEmoji emoji={props.str} pack={props.pack} />;
}
export function parseUnicodeEmoji(str: string): {
str: string;
pack?: UnicodeEmojiPacks;
} {
const selectorChar = str[0];
const selector = selectorChar.codePointAt(0);
if (
selector &&
selector >= UNICODE_EMOJI_MIN_PACK &&
selector <= UNICODE_EMOJI_MAX_PACK
) {
return {
str: str.substring(1),
pack: UNICODE_EMOJI_PUA_PACK[selectorChar],
};
} else {
return {
str,
};
}
}
export const remarkUnicodeEmoji: Plugin = () => (tree) => {
visit(
tree,
"text",
(
node: { type: "text"; value: string },
idx,
parent: { children: unknown[] },
) => {
const elements = node.value.split(RE_UNICODE_EMOJI);
if (elements.length === 1) return; // no matches
// Generate initial node
const newNodes: (
| { type: "text"; value: string }
| {
type: "unicodeEmoji";
str: string;
pack?: UnicodeEmojiPacks;
}
)[] = [
{
type: "text",
value: elements.shift()!,
},
];
// Process all timestamps
for (let i = 0; i < elements.length / 2; i++) {
newNodes.push({
type: "unicodeEmoji",
...parseUnicodeEmoji(elements[i * 2]),
});
newNodes.push({
type: "text",
value: elements[i * 2 + 1],
});
}
parent.children.splice(idx, 1, ...newNodes);
return idx + newNodes.length;
},
);
};
export const unicodeEmojiHandler: Handler = (h, node) => {
return {
type: "element" as const,
tagName: "unicodeEmoji",
children: [],
properties: {
str: node.str,
pack: node.pack,
},
};
};