feat(components): add md-border custom element
This commit is contained in:
parent
110451ae04
commit
e861b80f57
|
|
@ -1 +1,12 @@
|
|||
:md-font[Pacifico]{source=google} hello?
|
||||
:md-dice[d6]{.bg-amber-100}
|
||||
|
||||
:md-border[crimson]{.b}
|
||||
|
||||
:::div{.flex.justify-stretch.gap-1}
|
||||
|
||||
foo
|
||||
|
||||
bar
|
||||
|
||||
:::
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import "./md-table";
|
|||
import "./md-link";
|
||||
import "./md-pins";
|
||||
import "./md-bg";
|
||||
import "./md-border";
|
||||
import "./md-font";
|
||||
import "./md-embed";
|
||||
import "./md-deck";
|
||||
|
|
@ -26,6 +27,7 @@ export type { DiceProps } from "./md-dice";
|
|||
export type { TableProps } from "./md-table";
|
||||
export type { BgProps } from "./md-bg";
|
||||
export type { FontProps } from "./md-font";
|
||||
export type { BorderProps } from "./md-border";
|
||||
export type { EmbedProps } from "./md-embed";
|
||||
export type { YarnSpinnerProps } from "./md-yarn-spinner";
|
||||
export type { TokenProps } from "./md-token";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
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<string, string> = {
|
||||
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<CSSStyleDeclaration> = {};
|
||||
|
||||
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;
|
||||
},
|
||||
);
|
||||
Loading…
Reference in New Issue