feat: header and full-page transcludes (closes #557)
This commit is contained in:
parent
8223465bda
commit
74777118a7
@ -5,7 +5,7 @@ import BodyConstructor from "./Body"
|
|||||||
import { JSResourceToScriptElement, StaticResources } from "../util/resources"
|
import { JSResourceToScriptElement, StaticResources } from "../util/resources"
|
||||||
import { FullSlug, RelativeURL, joinSegments } from "../util/path"
|
import { FullSlug, RelativeURL, joinSegments } from "../util/path"
|
||||||
import { visit } from "unist-util-visit"
|
import { visit } from "unist-util-visit"
|
||||||
import { Root, Element } from "hast"
|
import { Root, Element, ElementContent } from "hast"
|
||||||
|
|
||||||
interface RenderComponents {
|
interface RenderComponents {
|
||||||
head: QuartzComponent
|
head: QuartzComponent
|
||||||
@ -61,22 +61,81 @@ export function renderPage(
|
|||||||
const classNames = (node.properties?.className ?? []) as string[]
|
const classNames = (node.properties?.className ?? []) as string[]
|
||||||
if (classNames.includes("transclude")) {
|
if (classNames.includes("transclude")) {
|
||||||
const inner = node.children[0] as Element
|
const inner = node.children[0] as Element
|
||||||
const blockSlug = inner.properties?.["data-slug"] as FullSlug
|
const transcludeTarget = inner.properties?.["data-slug"] as FullSlug
|
||||||
const blockRef = node.properties!.dataBlock as string
|
|
||||||
|
|
||||||
// TODO: avoid this expensive find operation and construct an index ahead of time
|
// TODO: avoid this expensive find operation and construct an index ahead of time
|
||||||
let blockNode = componentData.allFiles.find((f) => f.slug === blockSlug)?.blocks?.[blockRef]
|
const page = componentData.allFiles.find((f) => f.slug === transcludeTarget)
|
||||||
if (blockNode) {
|
if (!page) {
|
||||||
if (blockNode.tagName === "li") {
|
return
|
||||||
blockNode = {
|
}
|
||||||
type: "element",
|
|
||||||
tagName: "ul",
|
let blockRef = node.properties?.dataBlock as string | undefined
|
||||||
children: [blockNode],
|
if (blockRef?.startsWith("^")) {
|
||||||
|
// block transclude
|
||||||
|
blockRef = blockRef.slice(1)
|
||||||
|
let blockNode = page.blocks?.[blockRef]
|
||||||
|
if (blockNode) {
|
||||||
|
if (blockNode.tagName === "li") {
|
||||||
|
blockNode = {
|
||||||
|
type: "element",
|
||||||
|
tagName: "ul",
|
||||||
|
children: [blockNode],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.children = [
|
||||||
|
blockNode,
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "a",
|
||||||
|
properties: { href: inner.properties?.href, class: ["internal"] },
|
||||||
|
children: [{ type: "text", value: `Link to original` }],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} else if (blockRef?.startsWith("#") && page.htmlAst) {
|
||||||
|
// header transclude
|
||||||
|
blockRef = blockRef.slice(1)
|
||||||
|
let startIdx = undefined
|
||||||
|
let endIdx = undefined
|
||||||
|
for (const [i, el] of page.htmlAst.children.entries()) {
|
||||||
|
if (el.type === "element" && el.tagName.match(/h[1-6]/)) {
|
||||||
|
if (endIdx) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startIdx) {
|
||||||
|
endIdx = i
|
||||||
|
} else if (el.properties?.id === blockRef) {
|
||||||
|
startIdx = i
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!startIdx) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
node.children = [
|
node.children = [
|
||||||
blockNode,
|
...(page.htmlAst.children.slice(startIdx, endIdx) as ElementContent[]),
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "a",
|
||||||
|
properties: { href: inner.properties?.href, class: ["internal"] },
|
||||||
|
children: [{ type: "text", value: `Link to original` }],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
} else if (page.htmlAst) {
|
||||||
|
// page transclude
|
||||||
|
node.children = [
|
||||||
|
{
|
||||||
|
type: "element",
|
||||||
|
tagName: "h1",
|
||||||
|
children: [
|
||||||
|
{ type: "text", value: page.frontmatter?.title ?? `Transclude of ${page.slug}` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
...(page.htmlAst.children as ElementContent[]),
|
||||||
{
|
{
|
||||||
type: "element",
|
type: "element",
|
||||||
tagName: "a",
|
tagName: "a",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { PluggableList } from "unified"
|
import { PluggableList } from "unified"
|
||||||
import { QuartzTransformerPlugin } from "../types"
|
import { QuartzTransformerPlugin } from "../types"
|
||||||
import { Root, HTML, BlockContent, DefinitionContent, Code, Paragraph } from "mdast"
|
import { Root, HTML, BlockContent, DefinitionContent, Code, Paragraph } from "mdast"
|
||||||
import { Element, Literal } from "hast"
|
import { Element, Literal, Root as HtmlRoot } from "hast"
|
||||||
import { Replace, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
|
import { Replace, findAndReplace as mdastFindReplace } from "mdast-util-find-and-replace"
|
||||||
import { slug as slugAnchor } from "github-slugger"
|
import { slug as slugAnchor } from "github-slugger"
|
||||||
import rehypeRaw from "rehype-raw"
|
import rehypeRaw from "rehype-raw"
|
||||||
@ -236,13 +236,13 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
|
|||||||
value: `<iframe src="${url}"></iframe>`,
|
value: `<iframe src="${url}"></iframe>`,
|
||||||
}
|
}
|
||||||
} else if (ext === "") {
|
} else if (ext === "") {
|
||||||
const block = anchor.slice(1)
|
const block = anchor
|
||||||
return {
|
return {
|
||||||
type: "html",
|
type: "html",
|
||||||
data: { hProperties: { transclude: true } },
|
data: { hProperties: { transclude: true } },
|
||||||
value: `<blockquote class="transclude" data-url="${url}" data-block="${block}"><a href="${
|
value: `<blockquote class="transclude" data-url="${url}" data-block="${block}"><a href="${
|
||||||
url + anchor
|
url + anchor
|
||||||
}" class="transclude-inner">Transclude of block ${block}</a></blockquote>`,
|
}" class="transclude-inner">Transclude of ${url}${block}</a></blockquote>`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -436,6 +436,7 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
|
|||||||
const blockTagTypes = new Set(["blockquote"])
|
const blockTagTypes = new Set(["blockquote"])
|
||||||
return (tree, file) => {
|
return (tree, file) => {
|
||||||
file.data.blocks = {}
|
file.data.blocks = {}
|
||||||
|
file.data.htmlAst = tree
|
||||||
|
|
||||||
visit(tree, "element", (node, index, parent) => {
|
visit(tree, "element", (node, index, parent) => {
|
||||||
if (blockTagTypes.has(node.tagName)) {
|
if (blockTagTypes.has(node.tagName)) {
|
||||||
@ -524,5 +525,6 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin<Partial<Options>
|
|||||||
declare module "vfile" {
|
declare module "vfile" {
|
||||||
interface DataMap {
|
interface DataMap {
|
||||||
blocks: Record<string, Element>
|
blocks: Record<string, Element>
|
||||||
|
htmlAst: HtmlRoot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user