From 4bd714b7be4a5d891ab187a94e64e537cb43809e Mon Sep 17 00:00:00 2001 From: Stephen Tse Date: Sat, 26 Apr 2025 11:05:51 -0700 Subject: [PATCH] fix(callout): Grid-based callout collapsible animation (#1944) * Fixed broken nested callout maxHeight calculation * Implemented grid-based callout collapsible --- quartz/components/scripts/callout.inline.ts | 29 ++++----------- quartz/plugins/transformers/ofm.ts | 39 +++++++++++++-------- quartz/styles/callouts.scss | 14 ++++++-- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/quartz/components/scripts/callout.inline.ts b/quartz/components/scripts/callout.inline.ts index 3b7e16d..242ce51 100644 --- a/quartz/components/scripts/callout.inline.ts +++ b/quartz/components/scripts/callout.inline.ts @@ -1,25 +1,10 @@ function toggleCallout(this: HTMLElement) { const outerBlock = this.parentElement! outerBlock.classList.toggle("is-collapsed") + const content = outerBlock.getElementsByClassName("callout-content")[0] as HTMLElement + if (!content) return const collapsed = outerBlock.classList.contains("is-collapsed") - const height = collapsed ? this.scrollHeight : outerBlock.scrollHeight - outerBlock.style.maxHeight = height + "px" - - // walk and adjust height of all parents - let current = outerBlock - let parent = outerBlock.parentElement - while (parent) { - if (!parent.classList.contains("callout")) { - return - } - - const collapsed = parent.classList.contains("is-collapsed") - const height = collapsed ? parent.scrollHeight : parent.scrollHeight + current.scrollHeight - parent.style.maxHeight = height + "px" - - current = parent - parent = parent.parentElement - } + content.style.gridTemplateRows = collapsed ? "0fr" : "1fr" } function setupCallout() { @@ -27,15 +12,15 @@ function setupCallout() { `callout is-collapsible`, ) as HTMLCollectionOf for (const div of collapsible) { - const title = div.firstElementChild - if (!title) continue + const title = div.getElementsByClassName("callout-title")[0] as HTMLElement + const content = div.getElementsByClassName("callout-content")[0] as HTMLElement + if (!title || !content) continue title.addEventListener("click", toggleCallout) window.addCleanup(() => title.removeEventListener("click", toggleCallout)) const collapsed = div.classList.contains("is-collapsed") - const height = collapsed ? title.scrollHeight : div.scrollHeight - div.style.maxHeight = height + "px" + content.style.gridTemplateRows = collapsed ? "0fr" : "1fr" } } diff --git a/quartz/plugins/transformers/ofm.ts b/quartz/plugins/transformers/ofm.ts index 983fd73..e958027 100644 --- a/quartz/plugins/transformers/ofm.ts +++ b/quartz/plugins/transformers/ofm.ts @@ -464,6 +464,30 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin> }) } + // For the rest of the MD callout elements other than the title, wrap them with + // two nested HTML
s (use some hacked mdhast component to achieve this) of + // class `callout-content` and `callout-content-inner` respectively for + // grid-based collapsible animation. + if (calloutContent.length > 0) { + node.children = [ + node.children[0], + { + data: { hProperties: { className: ["callout-content"] }, hName: "div" }, + type: "blockquote", + children: [ + { + data: { + hProperties: { className: ["callout-content-inner"] }, + hName: "div", + }, + type: "blockquote", + children: [...calloutContent], + }, + ], + }, + ] + } + // replace first line of blockquote with title and rest of the paragraph text node.children.splice(0, 1, ...blockquoteContent) @@ -485,21 +509,6 @@ export const ObsidianFlavoredMarkdown: QuartzTransformerPlugin> "data-callout-metadata": calloutMetaData, }, } - - // Add callout-content class to callout body if it has one. - if (calloutContent.length > 0) { - const contentData: BlockContent | DefinitionContent = { - data: { - hProperties: { - className: "callout-content", - }, - hName: "div", - }, - type: "blockquote", - children: [...calloutContent], - } - node.children = [node.children[0], contentData] - } } }) } diff --git a/quartz/styles/callouts.scss b/quartz/styles/callouts.scss index d6f65aa..02921ae 100644 --- a/quartz/styles/callouts.scss +++ b/quartz/styles/callouts.scss @@ -7,11 +7,19 @@ border-radius: 5px; padding: 0 1rem; overflow-y: hidden; - transition: max-height 0.3s ease; box-sizing: border-box; - & > .callout-content > :first-child { - margin-top: 0; + & > .callout-content { + display: grid; + transition: grid-template-rows 0.3s ease; + + & > .callout-content-inner { + overflow: hidden; + + & > :first-child { + margin-top: 0; + } + } } --callout-icon-note: url('data:image/svg+xml; utf8, ');