2024-04-21 00:16:30 +08:00

99 lines
2.1 KiB
TypeScript

/**
* :[mdi:11]:
* :[mdi:11 24px]:
* :[mid:11 /#ccc]:
* :[fluent-mdl2:toggle-filled 128px/#fff]:
*/
import type { PluginWithOptions } from 'markdown-it'
import type { RuleInline } from 'markdown-it/lib/parser_inline.mjs'
import { parseRect } from '../../utils/parseRect.js'
type AddIcon = (iconName: string) => string | undefined
const [openTag, endTag] = [':[', ']:']
function createTokenizer(addIcon: AddIcon): RuleInline {
return (state, silent) => {
let found = false
const max = state.posMax
const start = state.pos
if (state.src.slice(start, start + 2) !== openTag)
return false
if (silent)
return false
// :[]:
if (max - start < 5)
return false
state.pos = start + 2
while (state.pos < max) {
if (state.src.slice(state.pos, state.pos + 2) === endTag) {
found = true
break
}
state.md.inline.skipToken(state)
}
if (!found || start + 2 === state.pos) {
state.pos = start
return false
}
const content = state.src.slice(start + 2, state.pos)
// 不允许前后带有空格
if (/^\s|\s$/.test(content)) {
state.pos = start
return false
}
// found!
state.posMax = state.pos
state.pos = start + 2
const [iconName, options = ''] = content.split(/\s+/)
const [size, color] = options.split('/')
const open = state.push('iconify_open', 'span', 1)
open.markup = openTag
const className = addIcon(iconName)
if (className)
open.attrSet('class', className)
let style = ''
if (size)
style += `width:${parseRect(size)};height:${parseRect(size)};`
if (color)
style += `color:${color};`
if (style)
open.attrSet('style', style)
const text = state.push('text', '', 0)
text.content = className ? '' : iconName
const close = state.push('iconify_close', 'span', -1)
close.markup = endTag
state.pos = state.posMax + 2
state.posMax = max
return true
}
}
export const iconsPlugin: PluginWithOptions<AddIcon> = (
md,
addIcon = () => '',
) => {
md.inline.ruler.before('emphasis', 'iconify', createTokenizer(addIcon))
}