mirror of
https://github.com/jackyzha0/quartz.git
synced 2025-12-24 21:34:06 -06:00
Canvas stub
This commit is contained in:
parent
1175c7af87
commit
2fb4b7474d
@ -1,12 +1,19 @@
|
|||||||
import { ComponentChildren } from "preact"
|
import { ComponentChildren } from "preact"
|
||||||
import { htmlToJsx } from "../../util/jsx"
|
import { htmlToJsx } from "../../util/jsx"
|
||||||
|
import d3 from "d3"
|
||||||
import {
|
import {
|
||||||
QuartzComponent,
|
QuartzComponent,
|
||||||
QuartzComponentConstructor,
|
QuartzComponentConstructor,
|
||||||
QuartzComponentProps,
|
QuartzComponentProps,
|
||||||
QuartzCanvasComponent,
|
QuartzCanvasComponent,
|
||||||
|
CanvasNode,
|
||||||
|
CanvasEdge,
|
||||||
|
CanvasTextNode,
|
||||||
|
CanvasFileNode,
|
||||||
|
CanvasLinkNode,
|
||||||
|
CanvasGroupNode,
|
||||||
} from "../types"
|
} from "../types"
|
||||||
import { type FilePath } from "../../util/path"
|
import { type FilePath, slugifyFilePath } from "../../util/path"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
|
|
||||||
function loadCanvas(file: FilePath): QuartzCanvasComponent {
|
function loadCanvas(file: FilePath): QuartzCanvasComponent {
|
||||||
@ -15,13 +22,84 @@ function loadCanvas(file: FilePath): QuartzCanvasComponent {
|
|||||||
return JSON.parse(data) as QuartzCanvasComponent
|
return JSON.parse(data) as QuartzCanvasComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderTextNodes(nodes: CanvasTextNode[]): ComponentChildren {
|
||||||
|
return nodes.map((node) => (
|
||||||
|
<div
|
||||||
|
class="canvas canvas-node canvas-text-node"
|
||||||
|
style={{
|
||||||
|
position: "fixed",
|
||||||
|
width: node.width,
|
||||||
|
height: node.height,
|
||||||
|
left: node.x,
|
||||||
|
top: node.y,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{node["text"]}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderFileNodes(nodes: CanvasFileNode[]): ComponentChildren {
|
||||||
|
return nodes.map((node) => (
|
||||||
|
<div
|
||||||
|
class="internal alias internal-canvas"
|
||||||
|
data-slug={slugifyFilePath(node.file as FilePath)}
|
||||||
|
data-x={node.x}
|
||||||
|
data-y={node.y}
|
||||||
|
style={{
|
||||||
|
position: "fixed",
|
||||||
|
width: node.width,
|
||||||
|
height: node.height,
|
||||||
|
left: node.x,
|
||||||
|
top: node.y,
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderLinkNodes(nodes: CanvasLinkNode[]): ComponentChildren {
|
||||||
|
return nodes.map((node) => (
|
||||||
|
<iframe
|
||||||
|
src={node["url"]}
|
||||||
|
style={{
|
||||||
|
position: "fixed",
|
||||||
|
width: node.width,
|
||||||
|
height: node.height,
|
||||||
|
left: node.x,
|
||||||
|
top: node.y,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
const CanvasContent: QuartzComponent = ({ fileData, tree }: QuartzComponentProps) => {
|
const CanvasContent: QuartzComponent = ({ fileData, tree }: QuartzComponentProps) => {
|
||||||
//const content = htmlToJsx(fileData.filePath!, tree) as ComponentChildren
|
//const content = htmlToJsx(fileData.filePath!, tree) as ComponentChildren
|
||||||
const content = loadCanvas(fileData.filePath!)
|
const canvas = loadCanvas(fileData.filePath!)
|
||||||
|
const canvasNodes = (canvas["nodes"] ?? []) as CanvasNode[]
|
||||||
|
const canvasEdges = (canvas["edges"] ?? []) as CanvasEdge[]
|
||||||
const classes: string[] = fileData.frontmatter?.cssclasses ?? []
|
const classes: string[] = fileData.frontmatter?.cssclasses ?? []
|
||||||
const classString = ["popover-hint", ...classes].join(" ")
|
const classString = ["popover-hint", ...classes].join(" ")
|
||||||
|
|
||||||
|
// Canvas parts
|
||||||
|
const textNodes = (canvasNodes.filter((node) => node["type"] === "text") ??
|
||||||
|
[]) as CanvasTextNode[]
|
||||||
|
const fileNodes = (canvasNodes.filter((node) => node["type"] === "file") ??
|
||||||
|
[]) as CanvasFileNode[]
|
||||||
|
const linkNodes = (canvasNodes.filter((node) => node["type"] === "link") ??
|
||||||
|
[]) as CanvasLinkNode[]
|
||||||
|
const groupNodes = (canvasNodes.filter((node) => node["type"] === "group") ??
|
||||||
|
[]) as CanvasGroupNode[]
|
||||||
|
|
||||||
|
const result = (
|
||||||
|
<article class={classString}>
|
||||||
|
{renderTextNodes(textNodes)}
|
||||||
|
{renderFileNodes(fileNodes)}
|
||||||
|
{renderLinkNodes(linkNodes)}
|
||||||
|
</article>
|
||||||
|
)
|
||||||
|
|
||||||
//TODO: Implement canvas rendering
|
//TODO: Implement canvas rendering
|
||||||
return <article class={classString}>{content["nodes"]}</article>
|
return <article class={classString}>{result}</article>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (() => CanvasContent) satisfies QuartzComponentConstructor
|
export default (() => CanvasContent) satisfies QuartzComponentConstructor
|
||||||
|
|||||||
@ -91,7 +91,7 @@ export function renderCanvas(
|
|||||||
console.log(root)
|
console.log(root)
|
||||||
|
|
||||||
// process transcludes in componentData
|
// process transcludes in componentData
|
||||||
/*visit(root, "element", (node, _index, _parent) => {
|
visit(root, "element", (node, _index, _parent) => {
|
||||||
if (node.tagName === "blockquote") {
|
if (node.tagName === "blockquote") {
|
||||||
const classNames = (node.properties?.className ?? []) as string[]
|
const classNames = (node.properties?.className ?? []) as string[]
|
||||||
if (classNames.includes("transclude")) {
|
if (classNames.includes("transclude")) {
|
||||||
@ -204,7 +204,7 @@ export function renderCanvas(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})*/
|
})
|
||||||
|
|
||||||
// set componentData.tree to the edited html that has transclusions rendered
|
// set componentData.tree to the edited html that has transclusions rendered
|
||||||
componentData.tree = root
|
componentData.tree = root
|
||||||
|
|||||||
@ -100,10 +100,121 @@ async function mouseEnterHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function navigationHandler(this: HTMLAnchorElement) {
|
||||||
|
const link = this
|
||||||
|
if (link.dataset.noPopover === "true") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const canvasX = Number(link.getAttribute("data-x") ?? "0")
|
||||||
|
const canvasY = Number(link.getAttribute("data-y") ?? "0")
|
||||||
|
|
||||||
|
async function setPosition(popoverElement: HTMLElement) {
|
||||||
|
const { x, y } = await computePosition(link, popoverElement, {
|
||||||
|
middleware: [inline({ x: canvasX, y: canvasY }), shift(), flip()],
|
||||||
|
})
|
||||||
|
Object.assign(popoverElement.style, {
|
||||||
|
left: `${x}px`,
|
||||||
|
top: `${y}px`,
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasAlreadyBeenFetched = () =>
|
||||||
|
[...link.children].some((child) => child.classList.contains("popover"))
|
||||||
|
|
||||||
|
// dont refetch if there's already a popover
|
||||||
|
if (hasAlreadyBeenFetched()) {
|
||||||
|
return setPosition(link.lastChild as HTMLElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
const thisUrl = new URL(document.location.href)
|
||||||
|
thisUrl.hash = ""
|
||||||
|
thisUrl.search = ""
|
||||||
|
const targetUrl = new URL(link.href)
|
||||||
|
const hash = decodeURIComponent(targetUrl.hash)
|
||||||
|
targetUrl.hash = ""
|
||||||
|
targetUrl.search = ""
|
||||||
|
|
||||||
|
const response = await fetchCanonical(targetUrl).catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// bailout if another popover exists
|
||||||
|
if (hasAlreadyBeenFetched()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response) return
|
||||||
|
const [contentType] = response.headers.get("Content-Type")!.split(";")
|
||||||
|
const [contentTypeCategory, typeInfo] = contentType.split("/")
|
||||||
|
|
||||||
|
const popoverElement = document.createElement("div")
|
||||||
|
popoverElement.classList.add("popover")
|
||||||
|
const popoverInner = document.createElement("div")
|
||||||
|
popoverInner.classList.add("popover-inner")
|
||||||
|
popoverElement.appendChild(popoverInner)
|
||||||
|
|
||||||
|
popoverInner.dataset.contentType = contentType ?? undefined
|
||||||
|
|
||||||
|
switch (contentTypeCategory) {
|
||||||
|
case "image":
|
||||||
|
const img = document.createElement("img")
|
||||||
|
img.src = targetUrl.toString()
|
||||||
|
img.alt = targetUrl.pathname
|
||||||
|
|
||||||
|
popoverInner.appendChild(img)
|
||||||
|
break
|
||||||
|
case "application":
|
||||||
|
switch (typeInfo) {
|
||||||
|
case "pdf":
|
||||||
|
const pdf = document.createElement("iframe")
|
||||||
|
pdf.src = targetUrl.toString()
|
||||||
|
popoverInner.appendChild(pdf)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
const contents = await response.text()
|
||||||
|
const html = p.parseFromString(contents, "text/html")
|
||||||
|
normalizeRelativeURLs(html, targetUrl)
|
||||||
|
const elts = [...html.getElementsByClassName("popover-hint")]
|
||||||
|
if (elts.length === 0) return
|
||||||
|
|
||||||
|
elts.forEach((elt) => popoverInner.appendChild(elt))
|
||||||
|
}
|
||||||
|
|
||||||
|
setPosition(popoverElement)
|
||||||
|
link.appendChild(popoverElement)
|
||||||
|
|
||||||
|
if (hash !== "") {
|
||||||
|
const heading = popoverInner.querySelector(hash) as HTMLElement | null
|
||||||
|
if (heading) {
|
||||||
|
// leave ~12px of buffer when scrolling to a heading
|
||||||
|
popoverInner.scroll({ top: heading.offsetTop - 12, behavior: "instant" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener("nav", () => {
|
document.addEventListener("nav", () => {
|
||||||
const links = [...document.getElementsByClassName("internal")] as HTMLAnchorElement[]
|
const links = [...document.getElementsByClassName("internal")] as HTMLAnchorElement[]
|
||||||
for (const link of links) {
|
console.log(links)
|
||||||
|
const pageLinks = links.filter((link) =>
|
||||||
|
link.classList.contains("internal-canvas"),
|
||||||
|
) as HTMLAnchorElement[]
|
||||||
|
const canvasLinks = links.filter(
|
||||||
|
(link) => !link.classList.contains("internal-canvas"),
|
||||||
|
) as HTMLAnchorElement[]
|
||||||
|
for (const link of pageLinks) {
|
||||||
link.addEventListener("mouseenter", mouseEnterHandler)
|
link.addEventListener("mouseenter", mouseEnterHandler)
|
||||||
window.addCleanup(() => link.removeEventListener("mouseenter", mouseEnterHandler))
|
window.addCleanup(() => link.removeEventListener("mouseenter", mouseEnterHandler))
|
||||||
}
|
}
|
||||||
|
//TODO: Fix canvas loading of popovers
|
||||||
|
for (const link of canvasLinks) {
|
||||||
|
link.addEventListener("load", navigationHandler)
|
||||||
|
window.addCleanup(() => link.removeEventListener("load", navigationHandler))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user