import { customElement, noShadowDOM } from "solid-element"; import { onCleanup } from "solid-js"; import { resolvePath } from "./utils/path"; import { registerStyle } from "./utils/article-style-manager"; const SIDE_CLASSES = new Set(["t", "b", "l", "r", "all"]); const STYLE_CLASSES = new Set([ "solid", "dashed", "dotted", "double", "groove", "ridge", "inset", "outset", "none", ]); const REPEAT_CLASSES = new Set(["stretch", "repeat", "round", "space"]); const SIDE_CSS: Record = { t: "Top", b: "Bottom", l: "Left", r: "Right", }; function parseSides(classes: string[]): string[] { const sides = classes.filter((c) => SIDE_CLASSES.has(c)); if (sides.length === 0) return ["Top", "Bottom", "Left", "Right"]; if (sides.includes("all")) return ["Top", "Bottom", "Left", "Right"]; return sides.map((s) => SIDE_CSS[s]).filter(Boolean); } function parseStyle(classes: string[]): string { const s = classes.find((c) => STYLE_CLASSES.has(c)); return s || "solid"; } function parseRepeat(classes: string[]): string { const r = classes.find((c) => REPEAT_CLASSES.has(c)); return r || "stretch"; } function isImagePath(value: string): boolean { return /^(\.{0,2}\/|[a-zA-Z]:\\|https?:\/\/)|\.(png|jpg|jpeg|gif|svg|webp|bmp)(\?|$)/i.test( value, ); } export interface BorderProps { width?: string; slice?: string; } customElement( "md-border", { width: undefined, slice: "10" }, (props, { element }) => { noShadowDOM(); const rawValue = element?.textContent?.trim() || ""; if (element) element.textContent = ""; const articleEl = element?.closest("article") as HTMLElement; const articlePath = element?.closest("article[data-src]")?.getAttribute("data-src") || ""; // Parse classes from the element's className (marked-directive passes // attrs.class as the element's className) const classList = element?.className ? element.className.split(/\s+/) : []; const sides = parseSides(classList); const image = isImagePath(rawValue); const resolvedSrc = image ? resolvePath(articlePath, rawValue) : ""; const styles: Partial = {}; if (image) { const repeat = parseRepeat(classList); const borderWidth = props.width || "2mm"; styles.borderImageSource = `url(${resolvedSrc})`; styles.borderImageSlice = props.slice || "10"; styles.borderImageWidth = borderWidth; styles.borderImageRepeat = repeat; styles.borderImageOutset = "0"; (styles as any).borderStyle = "solid"; const allSides = ["Top", "Bottom", "Left", "Right"]; for (const side of allSides) { if (sides.includes(side)) { (styles as any)[`border${side}Width`] = borderWidth; } else { (styles as any)[`border${side}Width`] = "0"; } } } else { const color = rawValue || "currentColor"; const style = parseStyle(classList); const borderWidth = props.width || ".2mm"; for (const side of sides) { (styles as any)[`border${side}Width`] = borderWidth; (styles as any)[`border${side}Style`] = style; (styles as any)[`border${side}Color`] = color; } } const handle = registerStyle({ key: "border", article: articleEl, styles, }); onCleanup(() => handle.dispose()); return null; }, );