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-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-link";
|
||||||
import "./md-pins";
|
import "./md-pins";
|
||||||
import "./md-bg";
|
import "./md-bg";
|
||||||
|
import "./md-border";
|
||||||
import "./md-font";
|
import "./md-font";
|
||||||
import "./md-embed";
|
import "./md-embed";
|
||||||
import "./md-deck";
|
import "./md-deck";
|
||||||
|
|
@ -26,6 +27,7 @@ export type { DiceProps } from "./md-dice";
|
||||||
export type { TableProps } from "./md-table";
|
export type { TableProps } from "./md-table";
|
||||||
export type { BgProps } from "./md-bg";
|
export type { BgProps } from "./md-bg";
|
||||||
export type { FontProps } from "./md-font";
|
export type { FontProps } from "./md-font";
|
||||||
|
export type { BorderProps } from "./md-border";
|
||||||
export type { EmbedProps } from "./md-embed";
|
export type { EmbedProps } from "./md-embed";
|
||||||
export type { YarnSpinnerProps } from "./md-yarn-spinner";
|
export type { YarnSpinnerProps } from "./md-yarn-spinner";
|
||||||
export type { TokenProps } from "./md-token";
|
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