obsidian#MarkdownRenderChild TypeScript Examples

The following examples show how to use obsidian#MarkdownRenderChild. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: markdown.tsx    From obsidian-dataview with MIT License 6 votes vote down vote up
/** A trivial wrapper which allows a react component to live for the duration of a `MarkdownRenderChild`. */
export class ReactRenderer extends MarkdownRenderChild {
    public constructor(public init: DataviewInit, public element: h.JSX.Element) {
        super(init.container);
    }

    public onload(): void {
        const context = Object.assign({}, { component: this }, this.init);
        render(<DataviewContext.Provider value={context}>{this.element}</DataviewContext.Provider>, this.containerEl);
    }

    public onunload(): void {
        unmountComponentAtNode(this.containerEl);
    }
}
Example #2
Source File: refreshable-view.ts    From obsidian-dataview with MIT License 6 votes vote down vote up
/** Generic code for embedded Dataviews. */
export abstract class DataviewRefreshableRenderer extends MarkdownRenderChild {
    public container: HTMLElement;
    public index: FullIndex;
    public app: App;
    public settings: DataviewSettings;
    private lastReload: number;

    abstract render(): Promise<void>;

    onload() {
        this.render();
        this.lastReload = this.index.revision;
        // Refresh after index changes stop.
        this.registerEvent(this.app.workspace.on("dataview:refresh-views", this.maybeRefresh));
        // ...or when the DOM is shown (sidebar expands, tab selected, nodes scrolled into view).
        this.register(this.container.onNodeInserted(this.maybeRefresh));
    }

    maybeRefresh = () => {
        // If the index revision has changed recently, then queue a reload.
        // But only if we're mounted in the DOM and auto-refreshing is active.
        if (this.lastReload != this.index.revision && this.container.isShown() && this.settings.refreshEnabled) {
            this.lastReload = this.index.revision;
            this.render();
        }
    };
}
Example #3
Source File: queryInjector.ts    From obsidian-todoist-plugin with MIT License 6 votes vote down vote up
class InjectedQuery extends MarkdownRenderChild {
  private readonly createComp: (root: HTMLElement) => SvelteComponentDev;
  private component: SvelteComponentDev;

  constructor(
    container: HTMLElement,
    createComp: (root: HTMLElement) => SvelteComponentDev
  ) {
    super(container);
    this.createComp = createComp;
    this.containerEl = container;
  }

  onload() {
    this.component = this.createComp(this.containerEl);
  }

  onunload() {
    if (this.component) {
      this.component.$destroy();
    }
  }
}
Example #4
Source File: IconMD.ts    From obsidian-banners with MIT License 6 votes vote down vote up
export default class Icon extends MarkdownRenderChild {
  plugin: BannersPlugin;
  wrapper: HTMLElement;
  file: TFile;
  icon: string;

  constructor(plugin: BannersPlugin, wrapper: HTMLElement, icon: string, file: TFile) {
    super(document.createElement('div'));
    this.plugin = plugin;
    this.wrapper = wrapper;
    this.icon = icon;
    this.file = file;
  }

  onload() {
    const { iconHorizontalAlignment: ha, iconVerticalAlignment: va } = this.plugin.settings;

    this.wrapper.addClass('has-banner-icon');
    this.containerEl.addClass('obsidian-banner-icon', 'cm5-banner-icon', `h-${ha}`, `v-${va}`);

    const el = buildIcon(this.plugin, this.icon, this.file);
    this.containerEl.append(el);
    this.wrapper.prepend(this.containerEl);
  }
}
Example #5
Source File: list-view.tsx    From obsidian-dataview with MIT License 5 votes vote down vote up
export function createListView(init: DataviewInit, query: Query, sourcePath: string): MarkdownRenderChild {
    return new ReactRenderer(init, <ListView query={query} sourcePath={sourcePath} />);
}
Example #6
Source File: list-view.tsx    From obsidian-dataview with MIT License 5 votes vote down vote up
export function createFixedListView(init: DataviewInit, elements: Literal[], sourcePath: string): MarkdownRenderChild {
    return new ReactRenderer(init, <SimpleListGrouping items={elements} sourcePath={sourcePath} />);
}
Example #7
Source File: table-view.tsx    From obsidian-dataview with MIT License 5 votes vote down vote up
export function createTableView(init: DataviewInit, query: Query, sourcePath: string): MarkdownRenderChild {
    return new ReactRenderer(init, <TableView query={query} sourcePath={sourcePath} />);
}
Example #8
Source File: table-view.tsx    From obsidian-dataview with MIT License 5 votes vote down vote up
export function createFixedTableView(
    init: DataviewInit,
    headings: string[],
    values: Literal[][],
    sourcePath: string
): MarkdownRenderChild {
    return new ReactRenderer(init, <TableGrouping values={values} headings={headings} sourcePath={sourcePath} />);
}
Example #9
Source File: task-view.tsx    From obsidian-dataview with MIT License 5 votes vote down vote up
export function createTaskView(init: DataviewInit, query: Query, sourcePath: string): MarkdownRenderChild {
    return new ReactRenderer(init, <TaskView query={query} sourcePath={sourcePath} />);
}
Example #10
Source File: task-view.tsx    From obsidian-dataview with MIT License 5 votes vote down vote up
export function createFixedTaskView(
    init: DataviewInit,
    items: Grouping<SListItem>,
    sourcePath: string
): MarkdownRenderChild {
    return new ReactRenderer(init, <TaskGrouping items={items} sourcePath={sourcePath} />);
}
Example #11
Source File: BannerMD.ts    From obsidian-banners with MIT License 5 votes vote down vote up
export default class Banner extends MarkdownRenderChild {
  wrapper: HTMLElement;
  plugin: BannersPlugin;
  ctx: IMPPCPlus;
  bannerData: IBannerMetadata
  isEmbed: boolean;
  removeListeners: () => void;

  constructor(
    plugin: BannersPlugin,
    wrapper: HTMLElement,
    ctx: IMPPCPlus,
    bannerData: IBannerMetadata,
    isEmbed: boolean
  ) {
    super(document.createElement('div'));
    this.plugin = plugin;
    this.wrapper = wrapper;
    this.ctx = ctx;
    this.bannerData = bannerData;
    this.isEmbed = isEmbed;
    this.removeListeners = () => {};
  }

  onload() {
    const { style } = this.plugin.settings;
    const { containerEl: contentEl, sourcePath } = this.ctx;

    this.wrapper.addClass('obsidian-banner-wrapper');
    this.containerEl.addClass('obsidian-banner', 'cm5-banner', style);

    const [elements, removeListeners] = buildBanner(this.plugin, this.bannerData, sourcePath, this.containerEl, contentEl, this.isEmbed);
    this.containerEl.append(...elements);
    this.removeListeners = removeListeners;
    this.wrapper.prepend(this.containerEl);
  }

  onunload(): void {
    this.removeListeners();
  }
}
Example #12
Source File: main.ts    From obsidian-admonition with MIT License 5 votes vote down vote up
renderAdmonitionContent(
        admonitionElement: HTMLElement,
        type: string,
        content: string,
        ctx: MarkdownPostProcessorContext,
        sourcePath: string,
        src: string
    ) {
        let markdownRenderChild = new MarkdownRenderChild(admonitionElement);
        markdownRenderChild.containerEl = admonitionElement;
        if (ctx && !(typeof ctx == "string")) {
            ctx.addChild(markdownRenderChild);
        }

        if (content && content?.trim().length) {
            /**
             * Render the content as markdown and append it to the admonition.
             */
            const contentEl = this.getAdmonitionContentElement(
                type,
                admonitionElement,
                content
            );
            if (/^`{3,}mermaid/m.test(content)) {
                const wasCollapsed = !admonitionElement.hasAttribute("open");
                if (admonitionElement instanceof HTMLDetailsElement) {
                    admonitionElement.setAttribute("open", "open");
                }
                setImmediate(() => {
                    MarkdownRenderer.renderMarkdown(
                        content,
                        contentEl,
                        sourcePath,
                        markdownRenderChild
                    );
                    if (
                        admonitionElement instanceof HTMLDetailsElement &&
                        wasCollapsed
                    ) {
                        admonitionElement.removeAttribute("open");
                    }
                });
            } else {
                MarkdownRenderer.renderMarkdown(
                    content,
                    contentEl,
                    sourcePath,
                    markdownRenderChild
                );
            }

            if (
                (!content.length || contentEl.textContent.trim() == "") &&
                this.data.hideEmpty
            )
                admonitionElement.addClass("no-content");

            const taskLists = contentEl.querySelectorAll<HTMLInputElement>(
                ".task-list-item-checkbox"
            );
            if (taskLists?.length) {
                const split = src.split("\n");
                let slicer = 0;
                taskLists.forEach((task) => {
                    const line = split
                        .slice(slicer)
                        .findIndex((l) => /^[ \t>]*\- \[.\]/.test(l));

                    if (line == -1) return;
                    task.dataset.line = `${line + slicer + 1}`;
                    slicer = line + slicer + 1;
                });
            }
        }
    }
Example #13
Source File: main.ts    From obsidian-dataview with MIT License 4 votes vote down vote up
/** Replaces raw textual inline fields in text containers with pretty HTML equivalents. */
async function replaceInlineFields(
    ctx: MarkdownPostProcessorContext,
    container: HTMLElement,
    settings: QuerySettings
): Promise<void> {
    const originFile = ctx.sourcePath;
    const walker = document.createTreeWalker(container, NodeFilter.SHOW_ALL, {
        acceptNode,
    });
    let currentNode: Node | null = walker.currentNode;
    while (currentNode) {
        // if being a text node, replace inline fields
        if (currentNode.nodeType === 3) {
            const text = currentNode as Text & { __PENDING__?: Promise<any> };
            // don't wait for new node to be inserted
            (async () => {
                let textNodes = [text];
                if (text.__PENDING__) {
                    // wait for prevous post processor to finish
                    await text.__PENDING__;
                    // rescan for new text nodes
                    textNodes = [...text.parentElement!.childNodes].filter((n): n is Text => n instanceof Text);
                }
                const pending = Promise.all(textNodes.map(insertInlineFieldsToText));
                // save promise to __PENDING__ to notify other async post processor
                text.__PENDING__ = pending;
                await pending;
                delete text.__PENDING__;
            })();
        }
        currentNode = walker.nextNode();
    }

    async function insertInlineFieldsToText(text: Text) {
        const inlineFields = extractInlineFields(text.wholeText);

        for (let i = inlineFields.length - 1; i >= 0; i--) {
            const field = inlineFields[i];
            let component = new MarkdownRenderChild(container);
            ctx.addChild(component);
            let renderContainer = document.createElement("span");
            renderContainer.addClasses(["dataview", "inline-field"]);

            // Block inline fields render the key, parenthesis ones do not.
            if (field.wrapping == "[") {
                renderContainer.createSpan({
                    text: field.key,
                    cls: ["dataview", "inline-field-key"],
                    attr: {
                        "data-dv-key": field.key,
                        "data-dv-norm-key": canonicalizeVarName(field.key),
                    },
                });

                let valueContainer = renderContainer.createSpan({ cls: ["dataview", "inline-field-value"] });
                await renderValue(
                    parseInlineValue(field.value),
                    valueContainer,
                    originFile,
                    component,
                    settings,
                    false
                );
            } else {
                let valueContainer = renderContainer.createSpan({ cls: ["dataview", "inline-field-standalone-value"] });
                await renderValue(
                    parseInlineValue(field.value),
                    valueContainer,
                    originFile,
                    component,
                    settings,
                    false
                );
            }
            const toReplace = text.splitText(field.start);
            toReplace.parentElement?.insertBefore(renderContainer, toReplace);
            toReplace.textContent = toReplace.wholeText.substring(field.end - field.start);
        }
    }
}
Example #14
Source File: chartRenderer.ts    From obsidian-charts with GNU Affero General Public License v3.0 4 votes vote down vote up
class ChartRenderChild extends MarkdownRenderChild {
    data: any;
    chart: null | Chart;
    renderer: Renderer;
    ownPath: string;
    el: HTMLElement;

    constructor(data: any, el: HTMLElement, renderer: Renderer, ownPath: string) {
        super(el);
        this.el = el;
        this.data = data;
        this.renderer = renderer;
        this.ownPath = ownPath;
        this.changeHandler = this.changeHandler.bind(this);
        this.reload = this.reload.bind(this);
    }

    async onload() {
        try {
            const data = await this.renderer.datasetPrep(this.data, this.el);
            let x: any = {};
            if (this.data.id) {
                const colors = [];
                if (this.renderer.plugin.settings.themeable) {
                    let i = 1;
                    while (true) {
                        let color = getComputedStyle(this.el).getPropertyValue(`--chart-color-${i}`);
                        if (color) {
                            colors.push(color);
                            i++;
                        } else {
                            break;
                        }
                    }
                }
                x.datasets = [];
                let linkDest: TFile;
                if (this.data.file) linkDest = this.renderer.plugin.app.metadataCache.getFirstLinkpathDest(this.data.file, this.renderer.plugin.app.workspace.getActiveFile().path);
                const pos = this.renderer.plugin.app.metadataCache.getFileCache(
                    linkDest ?? this.renderer.plugin.app.vault.getAbstractFileByPath(this.ownPath) as TFile).sections.find(pre => pre.id === this.data.id)?.position;
                if (!pos) {
                    throw "Invalid id and/or file";
                }

                const tableString = (await this.renderer.plugin.app.vault.cachedRead(this.data.file ? linkDest : this.renderer.plugin.app.vault.getAbstractFileByPath(this.ownPath) as TFile)).substring(pos.start.offset, pos.end.offset);
                let tableData;
                try {
                    tableData = generateTableData(tableString, this.data.layout ?? 'columns', this.data.select);
                } catch (error) {
                    throw "There is no table at that id and/or file"
                }
                x.labels = tableData.labels;
                for (let i = 0; tableData.dataFields.length > i; i++) {
                    x.datasets.push({
                        label: tableData.dataFields[i].dataTitle ?? "",
                        data: tableData.dataFields[i].data,
                        backgroundColor: this.data.labelColors ? colors.length ? generateInnerColors(colors, this.data.transparency) : generateInnerColors(this.renderer.plugin.settings.colors, this.data.transparency) : colors.length ? generateInnerColors(colors, this.data.transparency)[i] : generateInnerColors(this.renderer.plugin.settings.colors, this.data.transparency)[i],
                        borderColor: this.data.labelColors ? colors.length ? colors : this.renderer.plugin.settings.colors : colors.length ? colors[i] : this.renderer.plugin.settings.colors[i],
                        borderWidth: 1,
                        fill: this.data.fill ? this.data.stacked ? i == 0 ? 'origin' : '-1' : true : false,
                        tension: this.data.tension ?? 0,
                    });
                }
                data.chartOptions.data.labels = x.labels;
                data.chartOptions.data.datasets = x.datasets;


            }
            this.chart = this.renderer.renderRaw(data, this.containerEl);
        } catch (error) {
            renderError(error, this.el);
        }
        if (this.data.id) {
            this.renderer.plugin.app.metadataCache.on("changed", this.changeHandler);
        }
        this.renderer.plugin.app.workspace.on('css-change', this.reload);
    }

    changeHandler(file: TFile) {
        if (this.data.file ? file.basename === this.data.file : file.path === this.ownPath) {
            this.reload();
        }
    }

    reload() {
        this.onunload();
        this.onload();
    }

    onunload() {
        this.renderer.plugin.app.metadataCache.off("changed", this.changeHandler);
        this.renderer.plugin.app.workspace.off('css-change', this.reload);
        this.el.empty();
        this.chart && this.chart.destroy();
        this.chart = null;
    }
}
Example #15
Source File: index.ts    From obsidian-initiative-tracker with GNU General Public License v3.0 4 votes vote down vote up
export class EncounterBlock extends MarkdownRenderChild {
    parser = new EncounterParser(this.plugin);
    constructor(
        public plugin: InitiativeTracker,
        public src: string,
        public containerEl: HTMLElement,
        public table = false
    ) {
        super(containerEl);
    }
    onload(): void {
        if (this.table) {
            this.postprocessTable();
        } else {
            this.postprocess();
        }
    }
    async postprocess() {
        const encounters = this.src.split("---") ?? [];
        const containerEl = this.containerEl.createDiv("encounter-container");
        const empty = containerEl.createSpan({
            text: "No encounters created. Please check your syntax and try again."
        });

        for (let encounter of encounters) {
            if (!encounter?.trim().length) continue;
            try {
                const params: EncounterParameters = parseYaml(encounter);
                new EncounterComponent(
                    await this.parser.parse(params),
                    containerEl.createDiv("encounter-instance"),
                    this.plugin
                );
                empty.detach();
            } catch (e) {
                console.error(e);
                new Notice(
                    "Initiative Tracker: here was an issue parsing: \n\n" +
                        encounter
                );
            }
        }
        this.registerEvent(
            this.plugin.app.workspace.on("initiative-tracker:unload", () => {
                this.containerEl.empty();
                this.containerEl.createEl("pre").createEl("code", {
                    text: `\`\`\`encounter\n${this.src}\`\`\``
                });
            })
        );
    }
    async postprocessTable() {
        const encounterSource = this.src.split("---") ?? [];
        const containerEl = this.containerEl.createDiv("encounter-container");
        const empty = containerEl.createSpan({
            text: "No encounters created. Please check your syntax and try again."
        });

        const encounters: ParsedParams[] = [];

        for (let encounter of encounterSource) {
            if (!encounter?.trim().length) continue;
            try {
                const params: EncounterParameters = parseYaml(encounter);
                encounters.push(await this.parser.parse(params));
            } catch (e) {
                console.error(e);
                new Notice(
                    "Initiative Tracker: here was an issue parsing: \n\n" +
                        encounter
                );
            }
        }
        if (encounters.length) {
            empty.detach();
            new EncounterTable({
                target: this.containerEl,
                props: {
                    encounters,
                    plugin: this.plugin
                }
            });
        }
        this.registerEvent(
            this.plugin.app.workspace.on("initiative-tracker:unload", () => {
                this.containerEl.empty();
                this.containerEl.createEl("pre").createEl("code", {
                    text: `\`\`\`encounter-table\n${this.src}\`\`\``
                });
            })
        );
    }
}