diff --git a/README.md b/README.md index 3eb2afc..a43de7b 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,26 @@ You can make a [pull request](https://github.com/elias-sundqvist/obsidian-react- ## Changelog +### 0.1.2 (2021-12-03) *Component Codeblocks + improved startup and stability* +* Components should no longer dissapear when navigating between views. +* Removed flickering of components at startup. +* It is now possible to write codeblocks like + * ````markdown + ```jsx::ComponentName + someText + ``` + ```` + Which is equivalent to + ````markdown + ```jsx: + + ``` + ```` + but less cluttered. + + Example: + ![Component Codeblock Example](https://user-images.githubusercontent.com/9102856/144520183-5dbdee07-76ae-49a6-aca6-732f53971a55.png) + ### 0.1.1 (2021-08-29) *Improved component unmounting* * Old components are now more reliably removed/disposed during rerenders. diff --git a/manifest.json b/manifest.json index b62b8c1..06dc878 100644 --- a/manifest.json +++ b/manifest.json @@ -1,8 +1,8 @@ { "id": "obsidian-react-components", "name": "React Components", - "version": "0.1.1", - "minAppVersion": "0.9.12", + "version": "0.1.2", + "minAppVersion": "0.12.19", "description": "This is a plugin for Obsidian. It allows you to write and use React components with Jsx inside your Obsidian notes.", "author": "Elias Sundqvist", "authorUrl": "https://github.com/elias-sundqvist", diff --git a/package.json b/package.json index 56843a8..a474b81 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "obsidian-react-components", - "version": "0.1.0", + "version": "0.1.2", "description": "This is a plugin for Obsidian (https://obsidian.md). It allows you to write and use React components with Jsx inside your Obsidian notes.", - "main": "main.js", + "main": "src/main.js", "scripts": { "prettier-format": "prettier --config .prettierrc.js *.tsx --write", "lint": "eslint . --ext .ts,.tsx", diff --git a/rollup.config.js b/rollup.config.js index 486f41f..aafa679 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -21,7 +21,7 @@ const vault_plugin_dir = fs.existsSync('./.vault_plugin_dir') ? '.'; export default { - input: 'main.tsx', + input: 'src/main.tsx', output: { dir: vault_plugin_dir, sourcemap: 'inline', diff --git a/main.tsx b/src/main.tsx similarity index 92% rename from main.tsx rename to src/main.tsx index 9fb545f..fe54073 100644 --- a/main.tsx +++ b/src/main.tsx @@ -4,8 +4,6 @@ import { normalizePath, Notice, Plugin, - PluginSettingTab, - Setting, TAbstractFile, TFile, TFolder, @@ -17,6 +15,7 @@ import OfflineReactDOM from 'react-dom'; import Babel from '@babel/standalone'; import isVarName from 'is-var-name'; import reactToWebComponent from './react-webcomponent'; +import { ReactComponentsSettingTab } from './settings'; type ReactComponentContextData = { markdownPostProcessorContext: MarkdownPostProcessorContext; @@ -49,6 +48,7 @@ export default class ReactComponentsPlugin extends Plugin { ReactDOM: typeof OfflineReactDOM; renderedHeaderMap: WeakMap = new WeakMap(); mountPoints: Set = new Set(); + refreshTimeoutId?: NodeJS.Timeout; noteHeaderComponent: (any) => JSX.Element = () => { const React = this.React; @@ -202,10 +202,10 @@ export default class ReactComponentsPlugin extends Plugin { getPropertyValue(propertyName: string, file: TFile) { const dataViewPropertyValue = (this.app as any)?.plugins?.plugins?.dataview?.api // eslint-disable-line - ?.page(file.path)?.[propertyName]; + ?.page(file?.path)?.[propertyName]; if (dataViewPropertyValue) { if (dataViewPropertyValue.path) { - return this.app.metadataCache.getFirstLinkpathDest(dataViewPropertyValue.path, file.path).path; + return this.app.metadataCache.getFirstLinkpathDest(dataViewPropertyValue?.path, file?.path)?.path; } const externalLinkMatch = /^\[.*\]\((.*)\)$/gm.exec(dataViewPropertyValue)?.[1]; if (externalLinkMatch) { @@ -359,7 +359,7 @@ export default class ReactComponentsPlugin extends Plugin { const newNoteHeaderComponent = namespaceObject[file.basename]; if (this.noteHeaderComponent != newNoteHeaderComponent && typeof newNoteHeaderComponent == 'function') { this.noteHeaderComponent = newNoteHeaderComponent; - this.app.workspace.trigger('react-components:component-updated'); + this.requestComponentUpdate(); } } } @@ -373,7 +373,7 @@ export default class ReactComponentsPlugin extends Plugin { codeBlocks.set(componentName, code); await this.refreshComponentScope(); if (this.settings.auto_refresh && !suppressComponentRefresh) { - this.app.workspace.trigger('react-components:component-updated'); + this.requestComponentUpdate(); } } try { @@ -426,7 +426,7 @@ export default class ReactComponentsPlugin extends Plugin { await this.registerComponents(file, true); } await this.refreshComponentScope(); - this.app.workspace.trigger('react-components:component-updated'); + this.requestComponentUpdate() } catch (e) {} } @@ -513,9 +513,16 @@ export default class ReactComponentsPlugin extends Plugin { name: 'Refresh React Components', callback: async () => { await this.loadComponents(); - this.app.workspace.trigger('react-components:component-updated'); + this.requestComponentUpdate(); } }); + this.addCommand({ + id: 'cleanup-react-components', + name: 'Clean Up React Components', + callback: async () => { + this.cleanUpComponents(); + } + }) this.registerEvent(this.app.vault.on('create', registerIfCodeBlockFile.bind(this))); this.registerEvent(this.app.vault.on('modify', registerIfCodeBlockFile.bind(this))); this.registerEvent(this.app.vault.on('rename', registerIfCodeBlockFile.bind(this))); @@ -532,6 +539,14 @@ export default class ReactComponentsPlugin extends Plugin { this.app.workspace.onLayoutReady(async () => this.refreshPanes()); } + async requestComponentUpdate() { + if(this.refreshTimeoutId !== null) { + clearTimeout(this.refreshTimeoutId); + } + // Only rerender after no new request has been made for 2 seconds. + this.refreshTimeoutId = setTimeout(()=>this.app.workspace.trigger('react-components:component-updated'), 2000); + } + registerHeaderProcessor() { this.registerMarkdownPostProcessor(async (_, ctx) => { if (!ctx.containerEl?.hasClass('markdown-preview-section')) { @@ -573,7 +588,6 @@ export default class ReactComponentsPlugin extends Plugin { registerCodeProcessor() { this.registerMarkdownPostProcessor(async (el, ctx) => { - this.cleanUpComponents(); const codeblocks = el.querySelectorAll('code'); const toReplace = []; for (let index = 0; index < codeblocks.length; index++) { @@ -581,6 +595,10 @@ export default class ReactComponentsPlugin extends Plugin { if (codeblock.className == 'language-jsx:' || codeblock.className == 'language-jsx-') { const source = codeblock.innerText; toReplace.push({ codeblock: codeblock.parentNode, source }); + } else if (codeblock.className.startsWith('language-jsx::')) { + const componentName = codeblock.className.substr('language-jsx::'.length).trim(); + const source = `<${componentName} src={${JSON.stringify(codeblock.innerText)}}/>`; + toReplace.push({ codeblock: codeblock.parentNode, source }); } else { const text = codeblock.innerText.trim(); if (text.startsWith('jsx-') || text.startsWith('jsx:')) { @@ -638,45 +656,3 @@ export default class ReactComponentsPlugin extends Plugin { return files; } } - -class ReactComponentsSettingTab extends PluginSettingTab { - plugin: ReactComponentsPlugin; - - constructor(plugin: ReactComponentsPlugin) { - super(plugin.app, plugin); - this.plugin = plugin; - } - - display(): void { - const { containerEl } = this; - - containerEl.empty(); - - containerEl.createEl('h2', { text: 'React Components Settings' }); - - new Setting(containerEl) - .setName('Components folder location') - .setDesc('Files in this folder will be available as components/functions.') - .addText(text => { - text.setPlaceholder('Example: folder 1/folder 2') - .setValue(this.plugin.settings.template_folder) - .onChange(new_folder => { - this.plugin.settings.template_folder = new_folder; - this.plugin.loadComponents(); - this.plugin.saveSettings(); - }); - }); - - new Setting(containerEl) - .setName('Automatically Refresh Components') - .setDesc( - 'Useful to disable if reloading components is costly (like if they perform api calls or read a lot of files). To refresh the components manually, run the `Refresh React Components` command' - ) - .addToggle(toggle => { - toggle.setValue(this.plugin.settings.auto_refresh).onChange(auto_refresh => { - this.plugin.settings.auto_refresh = auto_refresh; - this.plugin.saveSettings(); - }); - }); - } -} diff --git a/react-webcomponent.tsx b/src/react-webcomponent.tsx similarity index 100% rename from react-webcomponent.tsx rename to src/react-webcomponent.tsx diff --git a/src/settings.tsx b/src/settings.tsx new file mode 100644 index 0000000..1da0fc3 --- /dev/null +++ b/src/settings.tsx @@ -0,0 +1,44 @@ +import { PluginSettingTab, Setting } from "obsidian"; +import ReactComponentsPlugin from "./main"; + +export class ReactComponentsSettingTab extends PluginSettingTab { + plugin: ReactComponentsPlugin; + + constructor(plugin: ReactComponentsPlugin) { + super(plugin.app, plugin); + this.plugin = plugin; + } + + display(): void { + const { containerEl } = this; + + containerEl.empty(); + + containerEl.createEl('h2', { text: 'React Components Settings' }); + + new Setting(containerEl) + .setName('Components folder location') + .setDesc('Files in this folder will be available as components/functions.') + .addText(text => { + text.setPlaceholder('Example: folder 1/folder 2') + .setValue(this.plugin.settings.template_folder) + .onChange(new_folder => { + this.plugin.settings.template_folder = new_folder; + this.plugin.loadComponents(); + this.plugin.saveSettings(); + }); + }); + + new Setting(containerEl) + .setName('Automatically Refresh Components') + .setDesc( + 'Useful to disable if reloading components is costly (like if they perform api calls or read a lot of files). To refresh the components manually, run the `Refresh React Components` command' + ) + .addToggle(toggle => { + toggle.setValue(this.plugin.settings.auto_refresh).onChange(auto_refresh => { + this.plugin.settings.auto_refresh = auto_refresh; + this.plugin.saveSettings(); + }); + }); + } +} \ No newline at end of file diff --git a/versions.json b/versions.json index 16a868f..46ee0b8 100644 --- a/versions.json +++ b/versions.json @@ -9,5 +9,6 @@ "0.0.8": "0.9.12", "0.0.9": "0.9.12", "0.1.0": "0.9.12", - "0.1.1": "0.9.12" + "0.1.1": "0.9.12", + "0.1.2": "0.12.19" }