[[Panda CSS]]をベースに[[React]]で[[Obsidianプラグイン]]の開発ベースを構築。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://marcusolsson.dev/images/profile_hu12289597471521576410.jpg" /> <span class="link-card-v2-site-name">Marcus Olsson</span> </div> <div class="link-card-v2-title"> New maintainer for the Obsidian plugin docs - Marcus Olsson </div> <div class="link-card-v2-content"> I’ve donated the unofficial plugin documentation to the Obsidian team. </div> <img class="link-card-v2-image" src="https://marcusolsson.dev/images/social.png" /> <a href="https://marcus.se.net/obsidian-plugin-docs/getting-started/react"></a> </div> ## インストールと設定 ```console npm install react react-dom npm install --save-dev @types/react @types/react-dom ``` [[tsconfig.json]]で[[JSX]]を有効にする。 ```json { "compilerOptions": { "jsx": "react" }, } ``` ## [[React]]でカスタムビューを作成 `ReactView.tsx`を作ってみる。 ```tsx import * as React from "react"; export const ReactView = () => { return <h4>Hello, React!</h4>; }; ``` `view.tsx`を作って上記[[React]]コンポーネントをwrapする。 ```tsx import { ItemView, WorkspaceLeaf } from "obsidian"; import * as React from "react"; import * as ReactDOM from "react-dom"; import { ReactView } from "./ReactView"; import { createRoot } from "react-dom/client"; export const VIEW_TYPE_EXAMPLE = "example-view"; export class ExampleView extends ItemView { constructor(leaf: WorkspaceLeaf) { super(leaf); } getViewType() { return VIEW_TYPE_EXAMPLE; } getDisplayText() { return "Example view"; } async onOpen() { const root = createRoot(this.containerEl.children[1]); root.render( <React.StrictMode> <ReactView /> </React.StrictMode> ); } async onClose() { ReactDOM.unmountComponentAtNode(this.containerEl.children[1]); } } ``` ## [[React]]のカスタムビューを表示 `main.ts`にてViewを登録し、サイドメニューのアイコンがクリックされた表示するようにする。 ```ts import { Plugin, View } from "obsidian"; import { DEFAULT_SETTINGS, Settings, FreeWritingSettingTab } from "./settings"; import { AppHelper } from "./app-helper"; import { createCommands } from "./commands"; import { ExampleView, VIEW_TYPE_EXAMPLE } from "./ui/view"; export default class FreeWritingPlugin extends Plugin { settings: Settings; appHelper: AppHelper; async onload() { await this.loadSettings(); this.appHelper = new AppHelper(this.app); this.init(); createCommands(this.appHelper, this.settings).forEach((c) => this.addCommand(c) ); this.addSettingTab(new FreeWritingSettingTab(this.app, this)); } async onunload() { this.app.workspace.detachLeavesOfType(VIEW_TYPE_EXAMPLE); } private init() { // UIなどの登録系はここ this.registerView(VIEW_TYPE_EXAMPLE, (leaf) => new ExampleView(leaf)); this.addRibbonIcon("dice", "Activate view", () => { this.activateView(); }); } async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); } async saveSettings() { await this.saveData(this.settings); this.init(); } async activateView() { this.app.workspace.detachLeavesOfType(VIEW_TYPE_EXAMPLE); await this.app.workspace.getActiveViewOfType(View)?.leaf.setViewState({ type: VIEW_TYPE_EXAMPLE, active: true, }); } } ``` > [!note] > 上記コードには今回の内容とは関係ないコードが混ざっている基本的に `init`メソッドと`onunload`メソッドの内容が大事。 ## `unmountComponentAtNode()`に関する警告の対応 ```error Warning: You are calling ReactDOM.unmountComponentAtNode() on a container that was previously passed to ReactDOMClient.createRoot(). This is not supported. Did you mean to call root.unmount()? ``` `unmountComponentAtNode`ではなく`unmount()`を使うべき? <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" /> <span class="link-card-v2-site-name">GitHub</span> </div> <div class="link-card-v2-title"> Bug: Unable to use `unmountComponentAtNode` with `unstable_createRoot` · Issue #21441 · facebook/react </div> <div class="link-card-v2-content"> When calling unmountComponentAtNode(document.getElementById('react-root')), it returns false. This would normall ... </div> <img class="link-card-v2-image" src="https://opengraph.githubassets.com/3474ae737cb25228d820fc8e4797b992b8bd299e5f62bb1fb868e24bac228d48/facebook/react/issues/21441" /> <a href="https://github.com/facebook/react/issues/21441"></a> </div> `view.tsx`を以下のように書きなおすことでWarningは消えた。これでいいのかは不明。 ```tsx export class ExampleView extends ItemView { root: Root; constructor(leaf: WorkspaceLeaf) { super(leaf); } getViewType() { return VIEW_TYPE_EXAMPLE; } getDisplayText() { return "Example view"; } async onOpen() { this.root = createRoot(this.containerEl.children[1]); this.root.render( <React.StrictMode> <ReactView /> </React.StrictMode> ); } async onClose() { this.root.unmount(); } } ``` ## [[Chakra UI]]の適応 [[React]]用の[[UIフレームワーク]]として[[Chakra UI]]を適応する。 ```console npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^6 ``` ```tsx async onOpen() { this.root = createRoot(this.containerEl.children[1]); this.root.render( <React.StrictMode> <ReactView /> </React.StrictMode> ); } ``` あとは普通に使える。 > [!caution] `<ChakraProvider>`を使わないのは? > [[Obsidian]]のstyleがグローバルで汚染されてしまうため、[[Chakra UI]]が提供しているstyleは使わない。 # #2023/07/08 追記 ## [[Chakra UI]]の代わりに[[Panda CSS]]を導入する [[Chakra UI]]のstyleが使えないため微妙な感じなので、[[Panda CSS]]を導入してみる。 該当パッケージのアンインストール。 ```console npm uninstall @chakra-ui/react @emotion/react @emotion/styled framer-motion ``` [[Panda CSS]]のインストール。 ```console npm i -D @pandacss/dev ``` セットアップ。 ```console $ npx panda init -p 🐼 info [cli] Panda v0.5.1 🐼 info [init:postcss] creating postcss config file: `postcss.config.cjs` 🐼 info [init:config] creating panda config file: `panda.config.ts` 🚀 Thanks for choosing Panda to write your css. You are set up to start using Panda! ✔️ `styled-system/css`: the css function to author styles ✔️ `styled-system/tokens`: the css variables and js function to query your tokens ✔️ `styled-system/patterns`: functions to implement apply common layout patterns ╭───────────────────────── 🐼 Divine! ✨ ──────────────────────────╮ │ │ │ │ │ Next steps: │ │ │ │ [1] Create a `index.css` file in your project that contains: │ │ │ │ @layer reset, base, tokens, recipes, utilities; │ │ │ │ │ │ [2] Import the `index.css` file at the root of your project. │ │ │ │ │ ╰──────────────────────────────────────────────────────────────────╯ 🐼 info [hrtime] ✨ Panda initialized (260.57ms) ``` エラーになる。[[IDE]]では型を認識するのに。 ```error > [email protected] dev > node esbuild.config.mjs [watch] build finished, watching for changes... ✘ [ERROR] Could not resolve "../../styled-system/css" src/ui/ReactView.tsx:9:20: 9 │ import { css } from "../../styled-system/css"; ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~ 1 error ``` [[Panda CSS]]が出力するスクリプトが[[ESモジュール (JavaScript)|ESモジュール]]だからな気がする...。`esbuild.config.mjs`には以下のようになっているし。 ```js format: "cjs", ``` `panda.config.ts` に[[outExtension (Panda CSS)|outExtension]]オプションを指定する。 ```ts { outExtension: "js", } ``` ビルドしなおせば通るはず。 ```console npx panda codegen ``` ただスタイルは反映されない... 多分CSSファイルとかがないせい。 また、サイズは5MB弱になっているので[[Tree-shaking]]しないとまずそう。。 [[Panda CSS]]以外のを使った方がいいかも。。