@keyhole-koro/paper-in-paper
v0.2.0
Published
React library for hierarchical paper tree visualization with drag-and-drop
Downloads
22
Readme
@keyhole-koro/paper-in-paper
ドラッグ&ドロップと展開表現を持つ階層的な paper tree を、React で扱うためのライブラリです。
現在は、ライブラリの再構築に向けて、仕様整理とアーキテクチャ設計を先に固めています。
まず読むべき文書:
現在の位置づけ
現在のコードベースには、動作するプロトタイプ実装があります。 次の段階では、以下を明確に切り分けます。
- 今のインタラクションモデルが実際にどうなっているか
- そのうち何が挙動で、何が見た目なのか
- その挙動を React library の API にどう写像するか
この整理においては、上記ドキュメント群を第一の source of truth とします。
プロトタイプの使い方
npm install @keyhole-koro/paper-in-paperBasic Usage
import { buildPaperMap, PaperCanvas } from '@keyhole-koro/paper-in-paper';
const papers = [
{ id: 'root', title: 'Root', description: '', content: '', parentId: null, childIds: ['a', 'b'] },
{ id: 'a', title: 'A', description: '', content: '', parentId: 'root', childIds: [] },
{ id: 'b', title: 'B', description: '', content: '', parentId: 'root', childIds: [] },
];
const paperMap = buildPaperMap(papers);
export default function App() {
return <PaperCanvas paperMap={paperMap} />;
}Data Structure
Paper
Each node in the tree is a Paper object:
interface Paper {
id: PaperId; // unique string identifier
title: string;
description: string;
content: string;
parentId: PaperId | null; // null for the root node
childIds: PaperId[];
}The tree must have exactly one root node (parentId: null).
Building a PaperMap
PaperMap is a Map<PaperId, Paper> used internally for O(1) lookups. Use buildPaperMap to create one from an array:
const paperMap = buildPaperMap(papers);API Reference
Components
PaperCanvas
Main canvas component that renders the paper tree.
<PaperCanvas
paperMap={paperMap} // required: Map<PaperId, Paper>
rootId="root" // optional: explicit root node ID (auto-detected if omitted)
/>Data Utilities
buildPaperMap(papers: Paper[]): PaperMap
Converts a Paper[] array into a PaperMap.
findRootId(paperMap: PaperMap): PaperId | null
Returns the ID of the root node (the node with parentId: null), or null if not found.
Expansion State
For use cases where expansion state needs to be managed externally.
Types
interface NodeExpansion {
openChildIds: PaperId[]; // currently expanded children
primaryChildId: PaperId | null; // the focused child
}
type ExpansionMap = Map<PaperId, NodeExpansion>;
type ExpansionAction =
| { type: 'OPEN'; parentId: PaperId; childId: PaperId }
| { type: 'CLOSE'; parentId: PaperId; childId: PaperId }
| { type: 'SET_PRIMARY'; parentId: PaperId; childId: PaperId };Functions
openNode(expansionMap, parentId, childId): ExpansionMap
closeNode(expansionMap, paperMap, parentId, childId): ExpansionMap
setPrimaryNode(expansionMap, parentId, childId): ExpansionMap
expansionReducer(expansionMap, paperMap, action): ExpansionMap設計方針
再構築の方向性は、概ね次の通りです。
- controlled な React API を先に定義する
- UI component の外に純粋な状態遷移を置く
- role、breadcrumbs、pass-through は selector で導く
- drag-and-drop は semantic な reorder intent を出す層として扱う
この考え方の詳細は react-architecture-considerations.md にまとめています。
Peer Dependencies
| Package | Version |
|---|---|
| react | >= 18 |
| framer-motion | >= 11 |
