[[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]]以外のを使った方がいいかも。。