Skip to content

Commit d0a9096

Browse files
committed
reactive rendering
1 parent b29c6f9 commit d0a9096

File tree

10 files changed

+275
-40
lines changed

10 files changed

+275
-40
lines changed

exampleVault/Advanced Example/Note 1.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ mdBuilder.createTable(
5353
["Quest", "Completed", "Started On", "Completed On"],
5454
quests.map(x => [
5555
x.name,
56-
x.removed !== -1 ? "[x]" : "[ ]",
56+
x.removed !== -1 ? "" : "",
5757
x.added,
5858
x.removed !== -1 ? x.removed : "",
5959
])

exampleVault/Advanced Example/Note 2.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ mdBuilder.createTable(
5454
["Quest", "Completed", "Started On", "Completed On"],
5555
quests.map(x => [
5656
x.name,
57-
x.removed !== -1 ? "[x]" : "[ ]",
57+
x.removed !== -1 ? "" : "",
5858
x.added,
5959
x.removed !== -1 ? x.removed : "",
6060
])

exampleVault/Advanced Example/Note 3.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ mdBuilder.createTable(
5454
["Quest", "Completed", "Started On", "Completed On"],
5555
quests.map(x => [
5656
x.name,
57-
x.removed !== -1 ? "[x]" : "[ ]",
57+
x.removed !== -1 ? "" : "",
5858
x.added,
5959
x.removed !== -1 ? x.removed : "",
6060
])

exampleVault/Test.md

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
text: taasd
2+
text: asdakdbg
33
number: 12234234
44
---
55

@@ -15,6 +15,8 @@ return engine.markdown.create(a);
1515
```
1616
Some more text
1717

18+
# Big Markdown Chunk
19+
1820
```js-engine
1921
let markdownBuilder = engine.markdown.createBuilder()
2022
@@ -79,3 +81,40 @@ component.addChild(inputField)
7981
component.addChild(inputField2)
8082
```
8183

84+
# Reactive Idea (TODO)
85+
86+
```js
87+
// define a function that takes in some args and returns what should be rendered
88+
function render(args) {
89+
return 'Something';
90+
}
91+
92+
// create a reactive component, passing in the function and arguments for the initial render
93+
const reactive = engine.reactive(render, initialArgs);
94+
95+
// subscribe to events and call refresh with new arguments, which causes the render function to be rerun with these arguments, replacing the existing content
96+
event.on('...', (args) => reactive.refresh(args));
97+
98+
// return the reactive component
99+
return reactive;
100+
```
101+
102+
```js-engine
103+
104+
function render(args) {
105+
console.log(args)
106+
return args?.text ?? "undefined";
107+
}
108+
109+
const reactive = engine.reactive(render, context.metadata.frontmatter);
110+
111+
const unloadCb = engine.app.metadataCache.on('changed', async (file, data, cache) => {
112+
if (file.path === context.file.path) {
113+
reactive.refresh(cache.frontmatter);
114+
}
115+
});
116+
117+
component.registerEvent(unloadCb);
118+
119+
return reactive;
120+
```

exampleVault/dv.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
const indexNotes = dv.pages('"IndexNotes"');
2+
const mainNotes = dv.pages('"MainNotes"');
3+
4+
console.log(indexNotes, mainNotes);
5+
6+
let error = '';
7+
8+
let mainNotesArr = mainNotes.map(x => ({
9+
splitFileName: x.file.name.split('-'),
10+
file: x.file,
11+
}));
12+
13+
let mainNoteTree = {
14+
type: 'root',
15+
groups: {},
16+
children: {},
17+
};
18+
19+
for (const mainNote of mainNotesArr) {
20+
if (mainNoteTree.groups[mainNote.splitFileName[0]] === undefined) {
21+
mainNoteTree.groups[mainNote.splitFileName[0]] = [mainNote];
22+
} else {
23+
mainNoteTree.groups[mainNote.splitFileName[0]].push(mainNote);
24+
}
25+
}
26+
27+
for (const [key, value] of Object.entries(mainNoteTree.groups)) {
28+
value.sort((a, b) => a.splitFileName.length - b.splitFileName.length);
29+
30+
let subtree = {
31+
type: 'subtree',
32+
file: undefined,
33+
fileName: key,
34+
fileNameParts: [],
35+
children: {},
36+
}
37+
38+
for (const v of value) {
39+
// we skip the first note
40+
if (v.splitFileName.length === 1) {
41+
subtree.file = v.file;
42+
continue;
43+
}
44+
45+
let parent = subtree;
46+
47+
for (let i = 1; i < v.splitFileName.length - 1; i++) {
48+
parent = parent.children[v.splitFileName[i]];
49+
}
50+
51+
parent.children[v.splitFileName[v.splitFileName.length - 1]] = {
52+
type: 'subtree',
53+
file: v.file,
54+
fileName: key,
55+
fileNameParts: v.splitFileName.splice(1),
56+
children: {},
57+
}
58+
}
59+
60+
mainNoteTree.children[key] = subtree;
61+
}
62+
63+
console.log(mainNoteTree);
64+
65+
const selectedFileName = "Plugins";
66+
67+
const selectedIndexNote = indexNotes.find(x => x.file.name === selectedFileName);
68+
69+
const selectedIndexNoteLinks = selectedIndexNote.file.link;
70+
71+
72+
console.log(selectedIndexNoteLinks);
73+
74+
75+
76+
77+

exampleVault/test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Step 1: Get Prompts
2+
const title = await tp.system.prompt("Enter Site Name");
3+
const parentLocation = await tp.system.prompt("Enter Parent Location");
4+
5+
const parentFile = app.vault.getAbstractFileByPath("3. Gazetteer/1. Settlements/" + parentLocation + "/" + parentLocation + ".md");
6+
const parentFileCache = await app.metadataCache.getFileCache(parentFile);
7+
8+
// Step 2: Change Frontmatter
9+
await app.fileManager.processFrontMatter(tp.config.target_file, (frontmatter) => {
10+
frontmatter.Location = parentLocation;
11+
frontmatter.LocationHex = parentFileCache.LocationHex;
12+
});
13+
14+
15+
// Step 3: Move and Rename
16+
await tp.file.move("3. Gazetteer/1. Settlements/" + parentLocation + "/" + title);
17+

src/JsMDRC.ts

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { MessageWrapper } from './messages/MessageManager';
77
import MessageComponent from './messages/MessageComponent.svelte';
88

99
import { JsExecution } from './jsEngine/JsExecution';
10+
import { ResultRenderer } from './ResultRenderer';
1011

1112
export class JsMDRC extends MarkdownRenderChild {
1213
plugin: JsEnginePlugin;
@@ -68,43 +69,48 @@ export class JsMDRC extends MarkdownRenderChild {
6869
async renderResults(container: HTMLElement): Promise<void> {
6970
const args = this.buildExecutionArgs(container);
7071
const context = this.buildExecutionContext();
71-
this.jsExecution = await this.tryRun(args, context);
72-
let result = this.jsExecution.result;
73-
74-
if (!result) {
75-
return;
76-
}
77-
78-
if (typeof result === 'string') {
79-
container.innerText = result;
80-
return;
81-
}
82-
83-
if (result instanceof MarkdownBuilder) {
84-
result = result.toMarkdown();
85-
}
8672

87-
if (result instanceof MarkdownString) {
88-
console.log(result.content);
89-
await result.render(container, this.ctx.sourcePath, this);
90-
return;
91-
}
92-
93-
if (result instanceof HTMLElement) {
94-
container.append(result);
95-
}
96-
97-
if (result instanceof MessageWrapper) {
98-
new MessageComponent({
99-
target: container,
100-
props: {
101-
messageWrapper: result,
102-
messageManager: this.plugin.messageManager,
103-
showDeleteButton: false,
104-
showMessageSource: false,
105-
},
106-
});
107-
}
73+
this.jsExecution = await this.tryRun(args, context);
74+
const result = this.jsExecution.result;
75+
76+
const renderer = new ResultRenderer(this.plugin, container, this.ctx.sourcePath, this);
77+
await renderer.render(result);
78+
79+
//
80+
// if (!result) {
81+
// return;
82+
// }
83+
//
84+
// if (typeof result === 'string') {
85+
// container.innerText = result;
86+
// return;
87+
// }
88+
//
89+
// if (result instanceof MarkdownBuilder) {
90+
// result = result.toMarkdown();
91+
// }
92+
//
93+
// if (result instanceof MarkdownString) {
94+
// console.log(result.content);
95+
// await result.render(container, this.ctx.sourcePath, this);
96+
// return;
97+
// }
98+
//
99+
// if (result instanceof HTMLElement) {
100+
// container.append(result);
101+
// }
102+
//
103+
// if (result instanceof MessageWrapper) {
104+
// new MessageComponent({
105+
// target: container,
106+
// props: {
107+
// messageWrapper: result,
108+
// messageManager: this.plugin.messageManager,
109+
// showDeleteButton: false,
110+
// showMessageSource: false,
111+
// },
112+
// });
113+
// }
108114
}
109115

110116
renderExecutionStats(container: HTMLElement): void {

src/ResultRenderer.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { MarkdownBuilder } from './api/markdown/MarkdownBuilder';
2+
import { MarkdownString } from './api/markdown/MarkdownString';
3+
import { MessageWrapper } from './messages/MessageManager';
4+
import MessageComponent from './messages/MessageComponent.svelte';
5+
import { Component } from 'obsidian';
6+
import JsEnginePlugin from './main';
7+
import { ReactiveComponent } from './api/reactive/ReactiveComponent';
8+
9+
export class ResultRenderer {
10+
readonly plugin: JsEnginePlugin;
11+
readonly container: HTMLElement;
12+
readonly sourcePath: string;
13+
readonly component: Component;
14+
15+
constructor(plugin: JsEnginePlugin, container: HTMLElement, sourcePath: string, component: Component) {
16+
this.plugin = plugin;
17+
this.container = container;
18+
this.sourcePath = sourcePath;
19+
this.component = component;
20+
}
21+
22+
public async render(content: unknown): Promise<void> {
23+
if (content == null) {
24+
return;
25+
}
26+
27+
this.container.empty();
28+
29+
if (typeof content === 'string') {
30+
this.container.innerText = content;
31+
return;
32+
}
33+
34+
if (content instanceof MarkdownBuilder) {
35+
content = content.toMarkdown();
36+
}
37+
38+
if (content instanceof MarkdownString) {
39+
console.log(content.content);
40+
await content.render(this.container, this.sourcePath, this.component);
41+
return;
42+
}
43+
44+
if (content instanceof HTMLElement) {
45+
this.container.append(content);
46+
}
47+
48+
if (content instanceof MessageWrapper) {
49+
new MessageComponent({
50+
target: this.container,
51+
props: {
52+
messageWrapper: content,
53+
messageManager: this.plugin.messageManager,
54+
showDeleteButton: false,
55+
showMessageSource: false,
56+
},
57+
});
58+
}
59+
60+
if (content instanceof ReactiveComponent) {
61+
content.setRenderer(this);
62+
content.initialRender();
63+
}
64+
}
65+
}

src/api/API.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { App, Plugin } from 'obsidian';
33
import JsEnginePlugin from '../main';
44
import { InstanceId } from './InstanceId';
55
import { MessageAPI } from './MessageAPI';
6+
import { ReactiveComponent, ReactiveRenderFunction } from './reactive/ReactiveComponent';
67

78
export class API {
89
/**
@@ -50,4 +51,8 @@ export class API {
5051
public getPlugin(pluginId: string): Plugin {
5152
return this.app.plugins.getPlugin(pluginId);
5253
}
54+
55+
public reactive(fn: ReactiveRenderFunction, ...initialArgs: any[]): ReactiveComponent {
56+
return new ReactiveComponent(fn, initialArgs);
57+
}
5358
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ResultRenderer } from '../../ResultRenderer';
2+
3+
export type ReactiveRenderFunction = (...args: any[]) => unknown;
4+
5+
export class ReactiveComponent {
6+
readonly _render: ReactiveRenderFunction;
7+
readonly initialArgs: any[];
8+
renderer: ResultRenderer | undefined;
9+
10+
constructor(_render: ReactiveRenderFunction, initialArgs: any[]) {
11+
this._render = _render;
12+
this.initialArgs = initialArgs;
13+
}
14+
15+
public refresh(...args: any[]): void {
16+
this.renderer?.render(this._render(...args));
17+
}
18+
19+
public initialRender(): void {
20+
this.refresh(...this.initialArgs);
21+
}
22+
23+
public setRenderer(renderer: ResultRenderer): void {
24+
this.renderer = renderer;
25+
}
26+
}

0 commit comments

Comments
 (0)