@hirokisakabe/pom-editor 0.2.0
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 +60 -0
- package/dist/AstTree.d.ts +8 -0
- package/dist/AstTree.d.ts.map +1 -0
- package/dist/AstTree.js +113 -0
- package/dist/PomAstEditor.d.ts +6 -0
- package/dist/PomAstEditor.d.ts.map +1 -0
- package/dist/PomAstEditor.js +45 -0
- package/dist/ast.d.ts +13 -0
- package/dist/ast.d.ts.map +1 -0
- package/dist/ast.js +71 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# @hirokisakabe/pom-editor
|
|
2
|
+
|
|
3
|
+
Visual AST editor for [pom](https://github.com/hirokisakabe/pom) — drag-and-drop reordering of slide node trees.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @hirokisakabe/pom-editor @hirokisakabe/pom react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { PomAstEditor } from "@hirokisakabe/pom-editor";
|
|
15
|
+
import { useState } from "react";
|
|
16
|
+
|
|
17
|
+
const initialXml = `
|
|
18
|
+
<Slide>
|
|
19
|
+
<VStack gap="16" padding="24">
|
|
20
|
+
<Text fontSize="32" bold="true">Title</Text>
|
|
21
|
+
<Text>Body text</Text>
|
|
22
|
+
</VStack>
|
|
23
|
+
</Slide>
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
function App() {
|
|
27
|
+
const [xml, setXml] = useState(initialXml);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div style={{ display: "flex", height: "100vh" }}>
|
|
31
|
+
<div style={{ width: 300, borderRight: "1px solid #e5e7eb" }}>
|
|
32
|
+
<PomAstEditor xml={xml} onChange={setXml} />
|
|
33
|
+
</div>
|
|
34
|
+
<div style={{ flex: 1, padding: 24 }}>
|
|
35
|
+
<pre>{xml}</pre>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
### `<PomAstEditor xml onChange />`
|
|
45
|
+
|
|
46
|
+
| Prop | Type | Description |
|
|
47
|
+
| ---------- | ----------------------- | -------------------------------------------------------- |
|
|
48
|
+
| `xml` | `string` | pom XML string (one or more `<Slide>` elements) |
|
|
49
|
+
| `onChange` | `(xml: string) => void` | Called with updated XML after each drag-and-drop reorder |
|
|
50
|
+
|
|
51
|
+
Renders a tree of nodes from the parsed XML. Nodes within the same parent container (`VStack`, `HStack`, `Layer`) can be reordered by dragging. Top-level slides can also be reordered.
|
|
52
|
+
|
|
53
|
+
## Requirements
|
|
54
|
+
|
|
55
|
+
- React 18 or later
|
|
56
|
+
- `@hirokisakabe/pom` (peer dependency resolved automatically as a workspace dependency; install separately in non-monorepo setups)
|
|
57
|
+
|
|
58
|
+
## License
|
|
59
|
+
|
|
60
|
+
MIT
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AstNode } from "./ast.ts";
|
|
2
|
+
import type { POMNode } from "@hirokisakabe/pom/clientApi";
|
|
3
|
+
export interface AstTreeProps {
|
|
4
|
+
ast: AstNode[];
|
|
5
|
+
onChange: (nodes: POMNode[]) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function AstTree({ ast, onChange }: AstTreeProps): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=AstTree.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AstTree.d.ts","sourceRoot":"","sources":["../src/AstTree.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AA0J3D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,OAAO,EAAE,CAAC;IACf,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACtC;AAED,wBAAgB,OAAO,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,YAAY,2CAoEtD"}
|
package/dist/AstTree.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { DndContext, PointerSensor, useSensor, useSensors, closestCenter, } from "@dnd-kit/core";
|
|
3
|
+
import { SortableContext, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable";
|
|
4
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
5
|
+
import { applyReorder, rebuildNodes } from "./ast.js";
|
|
6
|
+
const NODE_LABELS = {
|
|
7
|
+
text: "Text",
|
|
8
|
+
image: "Image",
|
|
9
|
+
table: "Table",
|
|
10
|
+
shape: "Shape",
|
|
11
|
+
chart: "Chart",
|
|
12
|
+
timeline: "Timeline",
|
|
13
|
+
matrix: "Matrix",
|
|
14
|
+
tree: "Tree",
|
|
15
|
+
flow: "Flow",
|
|
16
|
+
processArrow: "ProcessArrow",
|
|
17
|
+
pyramid: "Pyramid",
|
|
18
|
+
ul: "Ul",
|
|
19
|
+
ol: "Ol",
|
|
20
|
+
line: "Line",
|
|
21
|
+
arrow: "Arrow",
|
|
22
|
+
vstack: "VStack",
|
|
23
|
+
hstack: "HStack",
|
|
24
|
+
layer: "Layer",
|
|
25
|
+
icon: "Icon",
|
|
26
|
+
svg: "Svg",
|
|
27
|
+
};
|
|
28
|
+
function nodeLabel(node) {
|
|
29
|
+
const base = NODE_LABELS[node.type] ?? node.type;
|
|
30
|
+
const record = node;
|
|
31
|
+
if (node.type === "text" && typeof record.text === "string") {
|
|
32
|
+
const preview = record.text.slice(0, 20);
|
|
33
|
+
return `${base}: "${preview}${record.text.length > 20 ? "…" : ""}"`;
|
|
34
|
+
}
|
|
35
|
+
if (node.type === "image" && typeof record.src === "string") {
|
|
36
|
+
return `${base}: ${record.src.split("/").pop() ?? record.src}`;
|
|
37
|
+
}
|
|
38
|
+
if (node.type === "icon" && typeof record.name === "string") {
|
|
39
|
+
return `${base}: ${record.name}`;
|
|
40
|
+
}
|
|
41
|
+
if (node.type === "shape" && typeof record.text === "string") {
|
|
42
|
+
const preview = record.text.slice(0, 20);
|
|
43
|
+
return `${base}: "${preview}${record.text.length > 20 ? "…" : ""}"`;
|
|
44
|
+
}
|
|
45
|
+
return base;
|
|
46
|
+
}
|
|
47
|
+
function SortableItem({ astNode, depth, onChange, ast }) {
|
|
48
|
+
const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({
|
|
49
|
+
id: astNode.id,
|
|
50
|
+
data: { parentId: astNode.parentId },
|
|
51
|
+
});
|
|
52
|
+
const style = {
|
|
53
|
+
transform: CSS.Transform.toString(transform),
|
|
54
|
+
transition,
|
|
55
|
+
opacity: isDragging ? 0.4 : 1,
|
|
56
|
+
};
|
|
57
|
+
return (_jsxs("div", { ref: setNodeRef, style: style, children: [_jsxs("div", { style: {
|
|
58
|
+
display: "flex",
|
|
59
|
+
alignItems: "center",
|
|
60
|
+
gap: "6px",
|
|
61
|
+
paddingLeft: `${depth * 16}px`,
|
|
62
|
+
paddingTop: "3px",
|
|
63
|
+
paddingBottom: "3px",
|
|
64
|
+
borderRadius: "4px",
|
|
65
|
+
userSelect: "none",
|
|
66
|
+
}, children: [_jsx("span", { ...listeners, ...attributes, style: {
|
|
67
|
+
cursor: isDragging ? "grabbing" : "grab",
|
|
68
|
+
color: "#9ca3af",
|
|
69
|
+
fontSize: "12px",
|
|
70
|
+
lineHeight: 1,
|
|
71
|
+
flexShrink: 0,
|
|
72
|
+
}, title: "\u30C9\u30E9\u30C3\u30B0\u3057\u3066\u4E26\u3073\u66FF\u3048", children: "\u283F" }), _jsx("span", { style: {
|
|
73
|
+
fontSize: "13px",
|
|
74
|
+
fontFamily: "monospace",
|
|
75
|
+
color: astNode.children ? "#1d4ed8" : "#374151",
|
|
76
|
+
whiteSpace: "nowrap",
|
|
77
|
+
overflow: "hidden",
|
|
78
|
+
textOverflow: "ellipsis",
|
|
79
|
+
}, children: nodeLabel(astNode.node) })] }), astNode.children && astNode.children.length > 0 && (_jsx(SortableChildrenList, { children: astNode.children, depth: depth + 1, onChange: onChange, ast: ast }))] }));
|
|
80
|
+
}
|
|
81
|
+
function SortableChildrenList({ children, depth, onChange, ast, }) {
|
|
82
|
+
const ids = children.map((c) => c.id);
|
|
83
|
+
return (_jsx(SortableContext, { items: ids, strategy: verticalListSortingStrategy, children: children.map((child) => (_jsx(SortableItem, { astNode: child, depth: depth, onChange: onChange, ast: ast }, child.id))) }));
|
|
84
|
+
}
|
|
85
|
+
export function AstTree({ ast, onChange }) {
|
|
86
|
+
const sensors = useSensors(useSensor(PointerSensor, {
|
|
87
|
+
activationConstraint: { distance: 4 },
|
|
88
|
+
}));
|
|
89
|
+
function onDragEnd({ active, over }) {
|
|
90
|
+
if (!over || active.id === over.id)
|
|
91
|
+
return;
|
|
92
|
+
const activeParentId = active.data.current
|
|
93
|
+
.parentId;
|
|
94
|
+
const overParentId = over.data.current.parentId;
|
|
95
|
+
if (activeParentId !== overParentId)
|
|
96
|
+
return;
|
|
97
|
+
const newAst = applyReorder(ast, active.id, over.id, activeParentId);
|
|
98
|
+
onChange(rebuildNodes(newAst));
|
|
99
|
+
}
|
|
100
|
+
const rootIds = ast.map((n) => n.id);
|
|
101
|
+
return (_jsx(DndContext, { sensors: sensors, collisionDetection: closestCenter, onDragEnd: onDragEnd, children: _jsx(SortableContext, { items: rootIds, strategy: verticalListSortingStrategy, children: ast.map((astNode, i) => (_jsxs("div", { children: [i > 0 && (_jsx("div", { style: {
|
|
102
|
+
height: "1px",
|
|
103
|
+
backgroundColor: "#e5e7eb",
|
|
104
|
+
margin: "8px 0",
|
|
105
|
+
} })), _jsxs("div", { style: {
|
|
106
|
+
fontSize: "11px",
|
|
107
|
+
color: "#6b7280",
|
|
108
|
+
padding: "2px 0 4px 0",
|
|
109
|
+
fontWeight: 600,
|
|
110
|
+
letterSpacing: "0.05em",
|
|
111
|
+
textTransform: "uppercase",
|
|
112
|
+
}, children: ["Slide ", i + 1] }), _jsx(SortableItem, { astNode: astNode, depth: 0, onChange: onChange, ast: ast })] }, astNode.id))) }) }));
|
|
113
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PomAstEditor.d.ts","sourceRoot":"","sources":["../src/PomAstEditor.tsx"],"names":[],"mappings":"AASA,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED,wBAAgB,YAAY,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,iBAAiB,2CA0DhE"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { parseXml, serializeXml } from "@hirokisakabe/pom/clientApi";
|
|
5
|
+
import { AstTree } from "./AstTree.js";
|
|
6
|
+
import { buildAst } from "./ast.js";
|
|
7
|
+
export function PomAstEditor({ xml, onChange }) {
|
|
8
|
+
const [ast, setAst] = useState([]);
|
|
9
|
+
const [error, setError] = useState(null);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
try {
|
|
12
|
+
const nodes = parseXml(xml);
|
|
13
|
+
const counter = { value: 0 };
|
|
14
|
+
setAst(buildAst(nodes, "root", counter));
|
|
15
|
+
setError(null);
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
setError(e instanceof Error ? e.message : "XML parse error");
|
|
19
|
+
}
|
|
20
|
+
}, [xml]);
|
|
21
|
+
function handleChange(nodes) {
|
|
22
|
+
const counter = { value: 0 };
|
|
23
|
+
const newAst = buildAst(nodes, "root", counter);
|
|
24
|
+
setAst(newAst);
|
|
25
|
+
onChange(serializeXml(nodes));
|
|
26
|
+
}
|
|
27
|
+
if (error) {
|
|
28
|
+
return (_jsx("div", { style: {
|
|
29
|
+
padding: "12px",
|
|
30
|
+
fontSize: "12px",
|
|
31
|
+
color: "#dc2626",
|
|
32
|
+
fontFamily: "monospace",
|
|
33
|
+
whiteSpace: "pre-wrap",
|
|
34
|
+
wordBreak: "break-word",
|
|
35
|
+
}, children: error }));
|
|
36
|
+
}
|
|
37
|
+
if (ast.length === 0) {
|
|
38
|
+
return (_jsx("div", { style: {
|
|
39
|
+
padding: "12px",
|
|
40
|
+
fontSize: "13px",
|
|
41
|
+
color: "#9ca3af",
|
|
42
|
+
}, children: "No nodes" }));
|
|
43
|
+
}
|
|
44
|
+
return (_jsx("div", { style: { overflow: "auto", height: "100%", padding: "8px" }, children: _jsx(AstTree, { ast: ast, onChange: handleChange }) }));
|
|
45
|
+
}
|
package/dist/ast.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { POMNode } from "@hirokisakabe/pom/clientApi";
|
|
2
|
+
export interface AstNode {
|
|
3
|
+
id: string;
|
|
4
|
+
node: POMNode;
|
|
5
|
+
parentId: string;
|
|
6
|
+
children?: AstNode[];
|
|
7
|
+
}
|
|
8
|
+
export declare function buildAst(nodes: POMNode[], parentId: string, counter: {
|
|
9
|
+
value: number;
|
|
10
|
+
}): AstNode[];
|
|
11
|
+
export declare function rebuildNodes(astNodes: AstNode[]): POMNode[];
|
|
12
|
+
export declare function applyReorder(ast: AstNode[], activeId: string, overId: string, activeParentId: string): AstNode[];
|
|
13
|
+
//# sourceMappingURL=ast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../src/ast.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAG3D,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAID,wBAAgB,QAAQ,CACtB,KAAK,EAAE,OAAO,EAAE,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACzB,OAAO,EAAE,CAeX;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAO3D;AAyCD,wBAAgB,YAAY,CAC1B,GAAG,EAAE,OAAO,EAAE,EACd,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,OAAO,EAAE,CAgBX"}
|
package/dist/ast.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { arrayMove } from "@dnd-kit/sortable";
|
|
2
|
+
const CONTAINER_TYPES = new Set(["vstack", "hstack", "layer"]);
|
|
3
|
+
export function buildAst(nodes, parentId, counter) {
|
|
4
|
+
return nodes.map((node) => {
|
|
5
|
+
const id = String(counter.value++);
|
|
6
|
+
if (CONTAINER_TYPES.has(node.type)) {
|
|
7
|
+
const children = node.children ?? [];
|
|
8
|
+
return {
|
|
9
|
+
id,
|
|
10
|
+
node,
|
|
11
|
+
parentId,
|
|
12
|
+
children: buildAst(children, id, counter),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return { id, node, parentId };
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export function rebuildNodes(astNodes) {
|
|
19
|
+
return astNodes.map(({ node, children }) => {
|
|
20
|
+
if (children) {
|
|
21
|
+
return { ...node, children: rebuildNodes(children) };
|
|
22
|
+
}
|
|
23
|
+
return node;
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function findNode(astNodes, id) {
|
|
27
|
+
for (const n of astNodes) {
|
|
28
|
+
if (n.id === id)
|
|
29
|
+
return n;
|
|
30
|
+
if (n.children) {
|
|
31
|
+
const found = findNode(n.children, id);
|
|
32
|
+
if (found)
|
|
33
|
+
return found;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
function reorderInParent(astNodes, parentId, oldIndex, newIndex) {
|
|
39
|
+
return astNodes.map((astNode) => {
|
|
40
|
+
if (astNode.id === parentId && astNode.children) {
|
|
41
|
+
return {
|
|
42
|
+
...astNode,
|
|
43
|
+
children: arrayMove(astNode.children, oldIndex, newIndex),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
if (astNode.children) {
|
|
47
|
+
return {
|
|
48
|
+
...astNode,
|
|
49
|
+
children: reorderInParent(astNode.children, parentId, oldIndex, newIndex),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return astNode;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
export function applyReorder(ast, activeId, overId, activeParentId) {
|
|
56
|
+
if (activeParentId === "root") {
|
|
57
|
+
const oldIndex = ast.findIndex((c) => c.id === activeId);
|
|
58
|
+
const newIndex = ast.findIndex((c) => c.id === overId);
|
|
59
|
+
if (oldIndex === -1 || newIndex === -1)
|
|
60
|
+
return ast;
|
|
61
|
+
return arrayMove(ast, oldIndex, newIndex);
|
|
62
|
+
}
|
|
63
|
+
const parent = findNode(ast, activeParentId);
|
|
64
|
+
if (!parent?.children)
|
|
65
|
+
return ast;
|
|
66
|
+
const oldIndex = parent.children.findIndex((c) => c.id === activeId);
|
|
67
|
+
const newIndex = parent.children.findIndex((c) => c.id === overId);
|
|
68
|
+
if (oldIndex === -1 || newIndex === -1)
|
|
69
|
+
return ast;
|
|
70
|
+
return reorderInParent(ast, activeParentId, oldIndex, newIndex);
|
|
71
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { PomAstEditor } from "./PomAstEditor.js";
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hirokisakabe/pom-editor",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Visual AST editor component for pom — drag-and-drop slide structure editing.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"README.md",
|
|
17
|
+
"LICENSE"
|
|
18
|
+
],
|
|
19
|
+
"homepage": "https://github.com/hirokisakabe/pom#readme",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/hirokisakabe/pom/issues"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/hirokisakabe/pom.git",
|
|
26
|
+
"directory": "packages/pom-editor"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"author": "Hiroki Sakabe",
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc",
|
|
35
|
+
"fmt": "prettier --write .",
|
|
36
|
+
"fmt:check": "prettier --check .",
|
|
37
|
+
"knip": "knip",
|
|
38
|
+
"lint": "eslint",
|
|
39
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": ">=18"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@dnd-kit/core": "^6.3.1",
|
|
46
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
47
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
48
|
+
"@hirokisakabe/pom": "^8.2.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/react": "^19",
|
|
52
|
+
"typescript-eslint": "^8.59.4"
|
|
53
|
+
}
|
|
54
|
+
}
|