");
+ renderer.Write($"
');
renderer.Write("
");
@@ -75,9 +81,16 @@ internal sealed class CalloutRenderer : HtmlObjectRenderer
string calloutTitle = title.Length == 0 ? typeString.Humanize(LetterCasing.Sentence) : title;
WriteTitle(renderer, pipeline, calloutTitle);
+ if (block.Foldable)
+ {
+ renderer.Write("");
+ }
+
renderer.WriteLine("
");
+ renderer.Write("
");
renderer.WriteChildren(block);
renderer.WriteLine("
");
+ renderer.WriteLine("
");
renderer.EnsureLine();
}
diff --git a/src/scss/markdown-callouts.scss b/src/scss/markdown-callouts.scss
index a200195..558c608 100644
--- a/src/scss/markdown-callouts.scss
+++ b/src/scss/markdown-callouts.scss
@@ -29,6 +29,45 @@ $callout-fg-grey: #9e9e9e;
margin-bottom: 0;
}
+ &.collapsible {
+ .callout-fold {
+ transform: rotate(180deg);
+ transition: transform 500ms;
+ margin-left: 0.5em;
+
+ svg {
+ transform: rotate(180deg);
+ transition: transform 500ms;
+ }
+ }
+
+ .callout-title {
+ cursor: pointer;
+ transition: margin-bottom 500ms;
+ }
+
+ .callout-content {
+ opacity: 1;
+ max-height: 500px;
+ transition: opacity 500ms, max-height 500ms;
+ }
+
+ &.collapsed {
+ .callout-title {
+ margin-bottom: 0;
+ }
+
+ .callout-fold svg {
+ transform: rotate(0deg);
+ }
+
+ .callout-content {
+ opacity: 0;
+ max-height: 0;
+ }
+ }
+ }
+
&[data-callout="note"] {
background-color: $callout-bg-blue;
diff --git a/src/ts/Callout.ts b/src/ts/Callout.ts
new file mode 100644
index 0000000..125f034
--- /dev/null
+++ b/src/ts/Callout.ts
@@ -0,0 +1,63 @@
+class Callout {
+ private readonly _callout: HTMLElement;
+ private readonly _title: HTMLElement;
+ private readonly _content: HTMLElement;
+ private _foldEnabled: boolean;
+
+ constructor(element: HTMLElement) {
+ this._callout = element;
+ this._title = element.querySelector(".callout-title");
+ this._content = element.querySelector(".callout-content");
+ }
+
+ public static foldAll(element?: HTMLElement): void {
+ element = element || document.body;
+ this.findAll(element).forEach(c => c.fold());
+ }
+
+ public static findAll(element?: HTMLElement): Array
{
+ element = element || document.body;
+ return Array.from(element.querySelectorAll("div.callout")).map(c => {
+ return new Callout(c as HTMLElement);
+ });
+ }
+
+ public get content(): HTMLElement {
+ return this._content;
+ }
+
+ public get element(): HTMLElement {
+ return this._callout;
+ }
+
+ public get isFoldable(): boolean {
+ const fold: string = this._callout.dataset.calloutFold;
+ return fold !== null && fold !== undefined;
+ }
+
+ public get title(): HTMLElement {
+ return this._title;
+ }
+
+ public fold(): void {
+ if (this._foldEnabled || !this.isFoldable) {
+ return;
+ }
+
+ const callout: HTMLElement = this._callout;
+
+ if (callout === null) {
+ console.error("Callout element for ", this, " is null!");
+ return;
+ }
+
+ callout.classList.add("collapsible", "collapsed");
+ this._title.addEventListener("click", () => {
+ callout.classList.toggle("collapsed");
+ });
+
+ this._foldEnabled = true;
+ }
+}
+
+export default Callout;
\ No newline at end of file
diff --git a/src/ts/app.ts b/src/ts/app.ts
index 472fc86..ef56897 100644
--- a/src/ts/app.ts
+++ b/src/ts/app.ts
@@ -3,12 +3,14 @@ import UI from "./UI";
import Input from "./Input";
import Author from "./Author";
import BlogPost from "./BlogPost";
+import Callout from "./Callout";
declare const Handlebars: any;
declare const Prism: any;
declare const lucide: any;
(() => {
+ Callout.foldAll();
lucide.createIcons();
Prism.languages.extend('markup', {});