@tatamicks/core 0.3.2 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +518 -81
- package/dist/PalettePanel-BMZ6dfI3.cjs +9 -0
- package/dist/PalettePanel-BMZ6dfI3.cjs.map +1 -0
- package/dist/PalettePanel-Cigl9i1N.js +5142 -0
- package/dist/PalettePanel-Cigl9i1N.js.map +1 -0
- package/dist/PalettePanel.css +1 -0
- package/dist/canvas.cjs +1 -0
- package/dist/canvas.cjs.map +1 -0
- package/dist/canvas.mjs +354 -0
- package/dist/canvas.mjs.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +3 -1
- package/dist/index.css.d.ts +2 -0
- package/dist/index.mjs +1297 -6905
- package/dist/index.mjs.map +1 -1
- package/dist/paperSettingsWidget-7766a5Fe.cjs +2 -0
- package/dist/paperSettingsWidget-7766a5Fe.cjs.map +1 -0
- package/dist/paperSettingsWidget-DvtghSA5.js +5137 -0
- package/dist/paperSettingsWidget-DvtghSA5.js.map +1 -0
- package/dist/paperSettingsWidget.css +1 -0
- package/dist/shell.cjs +1 -0
- package/dist/shell.cjs.map +1 -0
- package/dist/shell.mjs +65 -0
- package/dist/shell.mjs.map +1 -0
- package/dist/sidebarPortal-BN6FVwaF.js +870 -0
- package/dist/sidebarPortal-BN6FVwaF.js.map +1 -0
- package/dist/sidebarPortal-Cr-dLvA_.cjs +3 -0
- package/dist/sidebarPortal-Cr-dLvA_.cjs.map +1 -0
- package/dist/src/canvas.d.ts +2523 -0
- package/dist/src/index.d.ts +1803 -2880
- package/dist/src/shell.d.ts +1982 -0
- package/package.json +21 -8
- package/dist/index.js +0 -70
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,81 +1,518 @@
|
|
|
1
|
-
# @tatamicks/core
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
|
|
14
|
-
##
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
npm install @tatamicks/core react react-dom
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
1
|
+
# @tatamicks/core
|
|
2
|
+
|
|
3
|
+
React 向けのヘッドレスで拡張可能なドキュメントエディターです。グリッドベースのレイアウト、複数ページの `Book` スキーマ、プラグインによるブロック拡張、編集 UI を組み合わせるための ActionBar / Sidebar を提供します。
|
|
4
|
+
|
|
5
|
+
## 特徴
|
|
6
|
+
|
|
7
|
+
- **グリッドレイアウト** — 列/行グリッドにブロックを配置。`fr`、`mm`、`px` などの単位を扱えます。
|
|
8
|
+
- **3 つのモード** — `FORM` はテンプレート作成、`EDIT` は値入力、`VIEW` は読み取り専用表示です。
|
|
9
|
+
- **プラグインシステム** — 組み込みブロックに加えて、独自の `BlockPlugin` を `createPluginRegistry([...plugins])` に渡すだけで登録できます。
|
|
10
|
+
- **コンポーザブル UI** — `ActionBar`、`Sidebar`、`DefaultSelectionActionBarOverlay` をそのまま使うか、必要なパネルだけ差し替えられます。
|
|
11
|
+
- **Undo / Redo** — `useNoteContext` が履歴、選択、ページ移動、値管理をまとめて配線します。
|
|
12
|
+
- **バリデーションと印刷** — 入力検証の表示、複数ページ印刷、印刷用 CSS 生成に対応します。
|
|
13
|
+
|
|
14
|
+
## インストール
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @tatamicks/core react react-dom
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
アプリのエントリーポイントで CSS を読み込んでください。
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import "@tatamicks/core/styles";
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Next.js App Router では `app/layout.tsx`、Pages Router では `pages/_app.tsx` のようなグローバル CSS を読み込める場所で import します。Vite / SPA では `main.tsx` や `App.tsx` で読み込めます。
|
|
27
|
+
|
|
28
|
+
## クイックスタート
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { useState } from "react";
|
|
32
|
+
import {
|
|
33
|
+
ActionBar,
|
|
34
|
+
CheckboxPlugin,
|
|
35
|
+
DEFAULT_BOOK,
|
|
36
|
+
DefaultSelectionActionBarOverlay,
|
|
37
|
+
Note,
|
|
38
|
+
NoteMode,
|
|
39
|
+
SelectPlugin,
|
|
40
|
+
Sidebar,
|
|
41
|
+
TextPlugin,
|
|
42
|
+
createPluginRegistry,
|
|
43
|
+
getDefaultActionBarSections,
|
|
44
|
+
getDefaultSidebarTabs,
|
|
45
|
+
useNoteContext,
|
|
46
|
+
} from "@tatamicks/core";
|
|
47
|
+
import "@tatamicks/core/styles";
|
|
48
|
+
|
|
49
|
+
const pluginRegistry = createPluginRegistry([
|
|
50
|
+
TextPlugin,
|
|
51
|
+
CheckboxPlugin,
|
|
52
|
+
SelectPlugin,
|
|
53
|
+
]);
|
|
54
|
+
|
|
55
|
+
export default function App() {
|
|
56
|
+
const [mode, setMode] = useState<NoteMode>(NoteMode.FORM);
|
|
57
|
+
const { context } = useNoteContext({
|
|
58
|
+
initialBook: DEFAULT_BOOK,
|
|
59
|
+
pluginRegistry,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
|
|
64
|
+
<ActionBar sections={getDefaultActionBarSections({ context })} />
|
|
65
|
+
|
|
66
|
+
<div style={{ display: "flex", gap: 8, padding: 8 }}>
|
|
67
|
+
<button type="button" onClick={() => setMode(NoteMode.FORM)}>
|
|
68
|
+
テンプレート作成
|
|
69
|
+
</button>
|
|
70
|
+
<button type="button" onClick={() => setMode(NoteMode.EDIT)}>
|
|
71
|
+
値入力
|
|
72
|
+
</button>
|
|
73
|
+
<button type="button" onClick={() => setMode(NoteMode.VIEW)}>
|
|
74
|
+
閲覧
|
|
75
|
+
</button>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div style={{ display: "flex", flex: 1, overflow: "hidden" }}>
|
|
79
|
+
<div style={{ flex: 1, overflow: "auto", padding: 16 }}>
|
|
80
|
+
<div ref={context.containerRef} style={{ position: "relative" }}>
|
|
81
|
+
<Note mode={mode} context={context} />
|
|
82
|
+
<DefaultSelectionActionBarOverlay context={context} />
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<Sidebar tabs={getDefaultSidebarTabs({ context })} />
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 中心 API
|
|
94
|
+
|
|
95
|
+
### `Note`
|
|
96
|
+
|
|
97
|
+
`Note` は `mode` に応じて `NoteForm` / `NoteEdit` / `NoteView` を切り替えるトップレベルコンポーネントです。状態は `context` に集約します。
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
<Note
|
|
101
|
+
mode={NoteMode.FORM}
|
|
102
|
+
context={context}
|
|
103
|
+
showValidation={false}
|
|
104
|
+
scale={1}
|
|
105
|
+
className="my-note"
|
|
106
|
+
/>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
`NoteForm`、`NoteEdit`、`NoteView` も export されています。`Note` を使うと実行時にモードを切り替えられますが、ページ固定のモードが分かっている場合は各コンポーネントを直接使う方がシンプルです。
|
|
110
|
+
|
|
111
|
+
### `useNoteContext`
|
|
112
|
+
|
|
113
|
+
フルエディターを組み立てるための統合フックです。Book 履歴、入力値、バインディング、アクション、ページ移動、選択状態をまとめた `context` を返します。
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
const { context } = useNoteContext({
|
|
117
|
+
initialBook,
|
|
118
|
+
pluginRegistry,
|
|
119
|
+
defaultValues,
|
|
120
|
+
extra,
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`containerRef` は `context.containerRef` 経由でアクセスします。独自の履歴層だけが必要な場合は、低レベル API として `useBookHistory({ initialBook, maxHistory })` も利用できます。
|
|
125
|
+
|
|
126
|
+
## データモデル
|
|
127
|
+
|
|
128
|
+
### `Book`
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
interface Book {
|
|
132
|
+
paper: Paper;
|
|
133
|
+
pages: [Page, ...Page[]];
|
|
134
|
+
metaData?: Record<string, Value>;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### `Page`
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
interface Page {
|
|
142
|
+
grid: Grid;
|
|
143
|
+
blocks: Block[];
|
|
144
|
+
blockDefaults?: BlockDefaults;
|
|
145
|
+
metaData?: Record<string, Value>;
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### `Paper`
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
interface Paper {
|
|
153
|
+
size: PaperSize;
|
|
154
|
+
margin: PaperMargin;
|
|
155
|
+
orientation?: boolean;
|
|
156
|
+
autoWidth?: boolean;
|
|
157
|
+
autoHeight?: boolean;
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### `Block`
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
interface Block<P = Record<string, Value>> {
|
|
165
|
+
id: string;
|
|
166
|
+
kind: string;
|
|
167
|
+
layout: { x: number; y: number; w: number; h: number };
|
|
168
|
+
props: P;
|
|
169
|
+
style?: BlockStyle;
|
|
170
|
+
behavior?: BlockBehavior;
|
|
171
|
+
hiddenBinding?: HiddenBinding;
|
|
172
|
+
initValue?: Value;
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
`props` の解決順序は低い順に `PropDef.defaultProps`、`page.blockDefaults[kind]`、`block.props` です。後からマージされる値ほど優先されます。
|
|
177
|
+
|
|
178
|
+
## プラグイン
|
|
179
|
+
|
|
180
|
+
### 組み込みプラグイン
|
|
181
|
+
|
|
182
|
+
`TextPlugin`、`CheckboxPlugin`、`SelectPlugin` はメインエントリから import できます。
|
|
183
|
+
`ButtonPlugin`、`StepperPlugin`、`NoteBlockPlugin` は Advanced プラグインとして `@tatamicks/core/canvas` からのみ公開されています。
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
// 基本プラグイン(@tatamicks/core)
|
|
187
|
+
import {
|
|
188
|
+
CheckboxPlugin,
|
|
189
|
+
SelectPlugin,
|
|
190
|
+
TextPlugin,
|
|
191
|
+
createPluginRegistry,
|
|
192
|
+
} from "@tatamicks/core";
|
|
193
|
+
|
|
194
|
+
// Advanced プラグイン(@tatamicks/core/canvas)
|
|
195
|
+
import {
|
|
196
|
+
ButtonPlugin,
|
|
197
|
+
NoteBlockPlugin,
|
|
198
|
+
StepperPlugin,
|
|
199
|
+
} from "@tatamicks/core/canvas";
|
|
200
|
+
|
|
201
|
+
const pluginRegistry = createPluginRegistry([
|
|
202
|
+
TextPlugin,
|
|
203
|
+
CheckboxPlugin,
|
|
204
|
+
SelectPlugin,
|
|
205
|
+
ButtonPlugin,
|
|
206
|
+
StepperPlugin,
|
|
207
|
+
NoteBlockPlugin,
|
|
208
|
+
]);
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`createPluginRegistry` には `BlockPlugin` を直接渡します。追加の wrapper 型や registry 用 plugin 型をユーザー側で用意する必要はありません。
|
|
212
|
+
|
|
213
|
+
### カスタムプラグイン
|
|
214
|
+
|
|
215
|
+
```tsx
|
|
216
|
+
import { forwardRef, useImperativeHandle, useRef } from "react";
|
|
217
|
+
import {
|
|
218
|
+
alignmentProp,
|
|
219
|
+
paddingProp,
|
|
220
|
+
placeholderProp,
|
|
221
|
+
} from "@tatamicks/core/canvas";
|
|
222
|
+
import type {
|
|
223
|
+
BaseBlockProps,
|
|
224
|
+
BlockPlugin,
|
|
225
|
+
BlockRef,
|
|
226
|
+
RendererProps,
|
|
227
|
+
} from "@tatamicks/core/canvas";
|
|
228
|
+
|
|
229
|
+
interface LabelProps extends BaseBlockProps {
|
|
230
|
+
label: string;
|
|
231
|
+
placeholder: string;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const LabelRenderer = forwardRef<
|
|
235
|
+
BlockRef,
|
|
236
|
+
RendererProps<LabelProps, string>
|
|
237
|
+
>(({ props, value, onChange, readOnly }, ref) => {
|
|
238
|
+
const inputRef = useRef<HTMLInputElement>(null);
|
|
239
|
+
|
|
240
|
+
useImperativeHandle(ref, () => ({
|
|
241
|
+
focus: () => inputRef.current?.focus(),
|
|
242
|
+
}));
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<label style={{ display: "grid", gap: 4 }}>
|
|
246
|
+
<span>{props.label}</span>
|
|
247
|
+
<input
|
|
248
|
+
ref={inputRef}
|
|
249
|
+
value={value ?? ""}
|
|
250
|
+
placeholder={props.placeholder}
|
|
251
|
+
readOnly={readOnly}
|
|
252
|
+
onChange={(event) => onChange(event.target.value)}
|
|
253
|
+
/>
|
|
254
|
+
</label>
|
|
255
|
+
);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
LabelRenderer.displayName = "LabelRenderer";
|
|
259
|
+
|
|
260
|
+
export const LabelPlugin: BlockPlugin<LabelProps, string> = {
|
|
261
|
+
kind: "label",
|
|
262
|
+
meta: {
|
|
263
|
+
displayName: "ラベル入力",
|
|
264
|
+
description: "ラベル付きのテキスト入力",
|
|
265
|
+
defaultSize: { w: 4, h: 2 },
|
|
266
|
+
},
|
|
267
|
+
Renderer: LabelRenderer,
|
|
268
|
+
properties: [
|
|
269
|
+
alignmentProp,
|
|
270
|
+
paddingProp,
|
|
271
|
+
placeholderProp,
|
|
272
|
+
{
|
|
273
|
+
kind: "labelText",
|
|
274
|
+
defaultProps: { label: "Label" },
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
validateProps: (raw): LabelProps => {
|
|
278
|
+
const value =
|
|
279
|
+
typeof raw === "object" && raw !== null
|
|
280
|
+
? (raw as Record<string, unknown>)
|
|
281
|
+
: {};
|
|
282
|
+
return {
|
|
283
|
+
label: typeof value.label === "string" ? value.label : "Label",
|
|
284
|
+
placeholder:
|
|
285
|
+
typeof value.placeholder === "string" ? value.placeholder : "",
|
|
286
|
+
};
|
|
287
|
+
},
|
|
288
|
+
validateValue: (raw): string | null => {
|
|
289
|
+
return typeof raw === "string" ? raw : null;
|
|
290
|
+
},
|
|
291
|
+
};
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### `PropDef`
|
|
295
|
+
|
|
296
|
+
`properties` に渡す各 `PropDef` は、プロパティパネルに表示する設定単位です。
|
|
297
|
+
|
|
298
|
+
```ts
|
|
299
|
+
interface PropDef {
|
|
300
|
+
kind: string;
|
|
301
|
+
defaultProps: Record<string, Value>;
|
|
302
|
+
component?: React.ComponentType<{
|
|
303
|
+
value: Value;
|
|
304
|
+
onChange: (value: Value) => void;
|
|
305
|
+
readOnly?: boolean;
|
|
306
|
+
}>;
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
組み込みの `alignmentProp`、`paddingProp`、`fontStyleProp`、`placeholderProp`、`requiredProp` はすべて `PropDef` です。独自 UI が必要な場合は `component` を指定し、省略時は既定の編集 UI にフォールバックします。
|
|
311
|
+
|
|
312
|
+
## ActionBar / Sidebar
|
|
313
|
+
|
|
314
|
+
既定 UI は `useNoteContext` の `context` を渡すだけで動作します。
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
<ActionBar sections={getDefaultActionBarSections({ context })} />
|
|
318
|
+
|
|
319
|
+
<Sidebar tabs={getDefaultSidebarTabs({ context })} />
|
|
320
|
+
|
|
321
|
+
<DefaultSelectionActionBarOverlay context={context} />
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
`getDefaultActionBarSections()` や `getDefaultSidebarTabs()` の戻り値は配列なので、`filter` / `map` で一部を差し替えられます。
|
|
325
|
+
|
|
326
|
+
## 値の管理
|
|
327
|
+
|
|
328
|
+
`useNoteContext` は入力値(`EDIT` / `VIEW` モードで各ブロックに入力された値)を内部で管理します。
|
|
329
|
+
現在の全入力値は `context.values`(`Record<string, Value>`)から読み取れます。
|
|
330
|
+
|
|
331
|
+
### 値の保存
|
|
332
|
+
|
|
333
|
+
外部に保存したい場合(API 送信・LocalStorage など)は `useEffect` で変化を監視します。
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
const { context } = useNoteContext({ initialBook, pluginRegistry });
|
|
337
|
+
|
|
338
|
+
// context.values が変わるたびにバックエンドへ送信する例
|
|
339
|
+
useEffect(() => {
|
|
340
|
+
save(context.values);
|
|
341
|
+
}, [context.values]);
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### 値の復元
|
|
345
|
+
|
|
346
|
+
保存済みの値を復元するには `defaultValues` に渡してコンポーネントをマウントします。
|
|
347
|
+
`defaultValues` はマウント時のみ参照されます(詳細は `useNoteContext` の JSDoc を参照)。
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
if (!savedValues) return <Loading />;
|
|
351
|
+
return (
|
|
352
|
+
<MyEditor
|
|
353
|
+
key={documentId}
|
|
354
|
+
initialBook={template}
|
|
355
|
+
defaultValues={savedValues}
|
|
356
|
+
/>
|
|
357
|
+
);
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### 不要な値のクリーン
|
|
361
|
+
|
|
362
|
+
ブロックを削除すると `context.values` にそのブロックの値が残ります。
|
|
363
|
+
保存前に `cleanValues` でブロックが存在しないエントリを除去できます。
|
|
364
|
+
|
|
365
|
+
```ts
|
|
366
|
+
import { cleanValues } from "@tatamicks/core";
|
|
367
|
+
|
|
368
|
+
const valuesToSave = cleanValues(context.book, context.values);
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## シリアライズと検証
|
|
372
|
+
|
|
373
|
+
```ts
|
|
374
|
+
import {
|
|
375
|
+
deserializeBook,
|
|
376
|
+
parseBook,
|
|
377
|
+
serializeBook,
|
|
378
|
+
validateBook,
|
|
379
|
+
} from "@tatamicks/core";
|
|
380
|
+
|
|
381
|
+
const json = serializeBook(book);
|
|
382
|
+
const restored = deserializeBook(json);
|
|
383
|
+
const parsed = parseBook(JSON.parse(json));
|
|
384
|
+
const errors = validateBook(parsed);
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
`validateBook` は Book 全体の構造検証ではなく、現在はブロック ID の重複検出とページあたりのブロック数上限チェックを行います。未検証データを `Book` として扱う前には `parseBook` または `deserializeBook` を使ってください。
|
|
388
|
+
|
|
389
|
+
### 値のシリアライズ
|
|
390
|
+
|
|
391
|
+
`context.values` は JSON シリアライズ可能なので `JSON.stringify` でそのまま保存できます。復元時は `deserializeValues` で検証してから使います。
|
|
392
|
+
|
|
393
|
+
```ts
|
|
394
|
+
import { deserializeValues } from "@tatamicks/core";
|
|
395
|
+
|
|
396
|
+
// 保存
|
|
397
|
+
const valuesJson = JSON.stringify(context.values);
|
|
398
|
+
|
|
399
|
+
// 復元(JSON が不正または無効な Value が含まれる場合は例外を投げる)
|
|
400
|
+
const restoredValues = deserializeValues(valuesJson);
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## ページ操作
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
import { addPage, movePage, removePage, setPage } from "@tatamicks/core";
|
|
407
|
+
|
|
408
|
+
const withNewPage = addPage(book);
|
|
409
|
+
const moved = movePage(withNewPage, 0, 1);
|
|
410
|
+
const removed = removePage(moved, 1);
|
|
411
|
+
const updated = setPage(removed, 0, nextPage);
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## 印刷
|
|
415
|
+
|
|
416
|
+
```ts
|
|
417
|
+
import { printNote } from "@tatamicks/core";
|
|
418
|
+
import type { PrintSettings } from "@tatamicks/core";
|
|
419
|
+
|
|
420
|
+
// コンテキストをそのまま渡すだけで印刷できる
|
|
421
|
+
printNote(context);
|
|
422
|
+
|
|
423
|
+
// 用紙サイズ・向き・余白を上書きして印刷する場合
|
|
424
|
+
const settings: PrintSettings = {
|
|
425
|
+
paperSize: "A4",
|
|
426
|
+
orientation: false, // false = 縦向き
|
|
427
|
+
margin: { top: "10mm", bottom: "10mm", left: "15mm", right: "15mm" },
|
|
428
|
+
};
|
|
429
|
+
printNote(context, settings);
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
低レベル API として `resolveEffectivePaper()` は `@tatamicks/core/canvas` サブパスから export されています。
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
import { resolveEffectivePaper } from "@tatamicks/core/canvas";
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## TypeScript
|
|
439
|
+
|
|
440
|
+
主要な型は `@tatamicks/core` から export されています。
|
|
441
|
+
|
|
442
|
+
```ts
|
|
443
|
+
// @tatamicks/core からインポートできる型
|
|
444
|
+
import type {
|
|
445
|
+
ActionContext,
|
|
446
|
+
BindingContext,
|
|
447
|
+
BuiltinActionId,
|
|
448
|
+
Block,
|
|
449
|
+
BlockBehavior,
|
|
450
|
+
Book,
|
|
451
|
+
Grid,
|
|
452
|
+
NoteContext,
|
|
453
|
+
Page,
|
|
454
|
+
Paper,
|
|
455
|
+
PluginRegistry,
|
|
456
|
+
PrintMargin,
|
|
457
|
+
PrintSettings,
|
|
458
|
+
Value,
|
|
459
|
+
} from "@tatamicks/core";
|
|
460
|
+
|
|
461
|
+
// カスタムプラグイン実装に必要な型は @tatamicks/core/canvas からインポートする
|
|
462
|
+
import type {
|
|
463
|
+
BaseBlockProps,
|
|
464
|
+
BlockPlugin,
|
|
465
|
+
BlockPluginMeta,
|
|
466
|
+
BlockRef,
|
|
467
|
+
PropDef,
|
|
468
|
+
RendererProps,
|
|
469
|
+
} from "@tatamicks/core/canvas";
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
## v0.x からの移行
|
|
473
|
+
|
|
474
|
+
現在の公開スキーマ名は `Book` / `Page` です。古い資料で `NoteBook` / `FormSchema` と呼んでいるものは、それぞれ現在の `Book` / `Page` に相当します。
|
|
475
|
+
|
|
476
|
+
## 組み込みブロックを追加する(Contributor 向け)
|
|
477
|
+
|
|
478
|
+
新しいブロックを `src/canvas/blocks/` に追加する手順です。
|
|
479
|
+
|
|
480
|
+
### ファイル構成
|
|
481
|
+
|
|
482
|
+
`src/canvas/blocks/<kindName>/` ディレクトリを作成し、以下のファイルを追加します。
|
|
483
|
+
|
|
484
|
+
| ファイル | 役割 |
|
|
485
|
+
|---|---|
|
|
486
|
+
| `types.ts` | `<Kind>BlockProps` 型定義 |
|
|
487
|
+
| `props.ts` | `PropDef[]` 定義(プロパティパネル設定) |
|
|
488
|
+
| `renderer.tsx` | `<Kind>Renderer` — `forwardRef` コンポーネント |
|
|
489
|
+
| `plugin.ts` | `<Kind>Plugin: BlockPlugin<...>` 本体 |
|
|
490
|
+
| `index.ts` | `plugin.ts` を re-export |
|
|
491
|
+
| `__tests__/<kind>Plugin.test.ts` | プラグイン単体テスト |
|
|
492
|
+
| `__stories__/<Kind>.stories.tsx` | Storybook ストーリー |
|
|
493
|
+
|
|
494
|
+
### チェックリスト
|
|
495
|
+
|
|
496
|
+
1. `types.ts` — `<Kind>BlockProps extends BaseBlockProps` を定義する
|
|
497
|
+
2. `props.ts` — `properties: PropDef[]` と `defaultProps` を定義する
|
|
498
|
+
3. `renderer.tsx` — `forwardRef<BlockRef, RendererProps<...>>` で実装し `useImperativeHandle` で `focus()` を公開する
|
|
499
|
+
4. `plugin.ts` — `BlockPlugin<KindProps, KindValue>` として組み立て、`validateProps` / `validateValue` を実装する(値は `Value` 互換型のみ。日付は `Date` インスタンスではなく ISO 8601 文字列で表現)
|
|
500
|
+
5. `index.ts` — `export { <Kind>Plugin } from "./plugin";` のみを記述する
|
|
501
|
+
6. `src/canvas/blocks/index.ts` — `export * from "./<kindName>";` を追記し、`basePlugins.ts` のデフォルトリストにも追加する
|
|
502
|
+
7. `__tests__/<kind>Plugin.test.ts` — `validateProps` / `validateValue` のユニットテスト(エッジケース含む)を追加する
|
|
503
|
+
8. `__stories__/<Kind>.stories.tsx` — `Default` など代表ストーリーを最低1件追加する
|
|
504
|
+
9. typecheck / lint / unit test / build がすべて通ることを確認する
|
|
505
|
+
|
|
506
|
+
旧 `@tatamicks/text`、`@tatamicks/checkbox`、`@tatamicks/select` は `@tatamicks/core` に統合されています。
|
|
507
|
+
|
|
508
|
+
| 旧 import | 新 import |
|
|
509
|
+
|-----------|-----------|
|
|
510
|
+
| `import { TextPlugin } from "@tatamicks/text"` | `import { TextPlugin } from "@tatamicks/core"` |
|
|
511
|
+
| `import { CheckboxPlugin } from "@tatamicks/checkbox"` | `import { CheckboxPlugin } from "@tatamicks/core"` |
|
|
512
|
+
| `import { SelectPlugin } from "@tatamicks/select"` | `import { SelectPlugin } from "@tatamicks/core"` |
|
|
513
|
+
|
|
514
|
+
移行の詳細は [docs/20_migration_guide.md](../../docs/20_migration_guide.md) を参照してください。
|
|
515
|
+
|
|
516
|
+
## ライセンス
|
|
517
|
+
|
|
518
|
+
MIT
|