@edux-design/tree-select 0.0.1
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 +58 -0
- package/dist/index.d.mts +23 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +425 -0
- package/dist/index.mjs +401 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# @edux-design/tree-select
|
|
2
|
+
|
|
3
|
+
Tree-select component for hierarchical navigation and selection. Supports checkbox or chevron controls, drag-and-drop reordering, and add actions at any level.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @edux-design/tree-select @edux-design/forms @edux-design/icons @edux-design/utils
|
|
11
|
+
# or
|
|
12
|
+
npm install @edux-design/tree-select @edux-design/forms @edux-design/icons @edux-design/utils
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Peer deps also include `react@^19.1.0`, `react-dom@^19.1.0`.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```jsx
|
|
22
|
+
import { TreeSelect } from "@edux-design/tree-select";
|
|
23
|
+
|
|
24
|
+
const data = [
|
|
25
|
+
{
|
|
26
|
+
id: "docs",
|
|
27
|
+
label: "Documentation",
|
|
28
|
+
checked: true,
|
|
29
|
+
children: [
|
|
30
|
+
{ id: "intro", label: "Introduction" },
|
|
31
|
+
{ id: "install", label: "Installation", indeterminate: true },
|
|
32
|
+
],
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
export function Example() {
|
|
37
|
+
return (
|
|
38
|
+
<TreeSelect
|
|
39
|
+
data={data}
|
|
40
|
+
control="checkbox"
|
|
41
|
+
defaultExpandedIds={["docs"]}
|
|
42
|
+
onCheck={(node, nextChecked) => console.log(node, nextChecked)}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Development
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pnpm --filter @edux-design/tree-select lint
|
|
54
|
+
pnpm --filter @edux-design/tree-select check-types
|
|
55
|
+
pnpm --filter @edux-design/tree-select build
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Storybook examples live in `src/demos/TreeSelect.stories.jsx`.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
declare function TreeSelect({ data, control, className, itemClassName, draggable, onReorder, onDataChange, onExpandedChange, expandedIds, defaultExpandedIds, onCheck, onAdd, addLabel, showRootAdd, rootAddLabel, renderLabel, expandOnLabelClick, }: {
|
|
4
|
+
data: any;
|
|
5
|
+
control?: string;
|
|
6
|
+
className: any;
|
|
7
|
+
itemClassName: any;
|
|
8
|
+
draggable?: boolean;
|
|
9
|
+
onReorder: any;
|
|
10
|
+
onDataChange: any;
|
|
11
|
+
onExpandedChange: any;
|
|
12
|
+
expandedIds: any;
|
|
13
|
+
defaultExpandedIds?: any[];
|
|
14
|
+
onCheck: any;
|
|
15
|
+
onAdd: any;
|
|
16
|
+
addLabel?: string;
|
|
17
|
+
showRootAdd?: boolean;
|
|
18
|
+
rootAddLabel?: string;
|
|
19
|
+
renderLabel: any;
|
|
20
|
+
expandOnLabelClick?: boolean;
|
|
21
|
+
}): react_jsx_runtime.JSX.Element;
|
|
22
|
+
|
|
23
|
+
export { TreeSelect };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
declare function TreeSelect({ data, control, className, itemClassName, draggable, onReorder, onDataChange, onExpandedChange, expandedIds, defaultExpandedIds, onCheck, onAdd, addLabel, showRootAdd, rootAddLabel, renderLabel, expandOnLabelClick, }: {
|
|
4
|
+
data: any;
|
|
5
|
+
control?: string;
|
|
6
|
+
className: any;
|
|
7
|
+
itemClassName: any;
|
|
8
|
+
draggable?: boolean;
|
|
9
|
+
onReorder: any;
|
|
10
|
+
onDataChange: any;
|
|
11
|
+
onExpandedChange: any;
|
|
12
|
+
expandedIds: any;
|
|
13
|
+
defaultExpandedIds?: any[];
|
|
14
|
+
onCheck: any;
|
|
15
|
+
onAdd: any;
|
|
16
|
+
addLabel?: string;
|
|
17
|
+
showRootAdd?: boolean;
|
|
18
|
+
rootAddLabel?: string;
|
|
19
|
+
renderLabel: any;
|
|
20
|
+
expandOnLabelClick?: boolean;
|
|
21
|
+
}): react_jsx_runtime.JSX.Element;
|
|
22
|
+
|
|
23
|
+
export { TreeSelect };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/index.js
|
|
30
|
+
var index_exports = {};
|
|
31
|
+
__export(index_exports, {
|
|
32
|
+
TreeSelect: () => TreeSelect
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(index_exports);
|
|
35
|
+
|
|
36
|
+
// src/elements/TreeSelect.jsx
|
|
37
|
+
var import_react = __toESM(require("react"));
|
|
38
|
+
var import_core = require("@dnd-kit/core");
|
|
39
|
+
var import_sortable = require("@dnd-kit/sortable");
|
|
40
|
+
var import_utilities = require("@dnd-kit/utilities");
|
|
41
|
+
var import_forms = require("@edux-design/forms");
|
|
42
|
+
var import_icons = require("@edux-design/icons");
|
|
43
|
+
var import_utils = require("@edux-design/utils");
|
|
44
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
45
|
+
var normalizeNodes = (data) => {
|
|
46
|
+
if (!data) return [];
|
|
47
|
+
if (Array.isArray(data)) return data;
|
|
48
|
+
return [data];
|
|
49
|
+
};
|
|
50
|
+
var collectNodeInfo = (nodes, parentId = null, map = /* @__PURE__ */ new Map()) => {
|
|
51
|
+
nodes.forEach((node, index) => {
|
|
52
|
+
const nodeId = String(node.id);
|
|
53
|
+
map.set(nodeId, { parentId, index, node });
|
|
54
|
+
if (Array.isArray(node.children)) {
|
|
55
|
+
collectNodeInfo(node.children, nodeId, map);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
return map;
|
|
59
|
+
};
|
|
60
|
+
var reorderArray = (list, fromIndex, toIndex) => {
|
|
61
|
+
const next = list.slice();
|
|
62
|
+
const [moved] = next.splice(fromIndex, 1);
|
|
63
|
+
next.splice(toIndex, 0, moved);
|
|
64
|
+
return next;
|
|
65
|
+
};
|
|
66
|
+
var reorderTree = (nodes, parentId, fromIndex, toIndex) => {
|
|
67
|
+
if (parentId === null) {
|
|
68
|
+
return reorderArray(nodes, fromIndex, toIndex);
|
|
69
|
+
}
|
|
70
|
+
let didReorder = false;
|
|
71
|
+
const next = nodes.map((node) => {
|
|
72
|
+
const nodeId = String(node.id);
|
|
73
|
+
if (nodeId === parentId) {
|
|
74
|
+
const children = Array.isArray(node.children) ? node.children : [];
|
|
75
|
+
didReorder = true;
|
|
76
|
+
return {
|
|
77
|
+
...node,
|
|
78
|
+
children: reorderArray(children, fromIndex, toIndex)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (!Array.isArray(node.children)) return node;
|
|
82
|
+
const nextChildren = reorderTree(
|
|
83
|
+
node.children,
|
|
84
|
+
parentId,
|
|
85
|
+
fromIndex,
|
|
86
|
+
toIndex
|
|
87
|
+
);
|
|
88
|
+
if (nextChildren === node.children) return node;
|
|
89
|
+
didReorder = true;
|
|
90
|
+
return { ...node, children: nextChildren };
|
|
91
|
+
});
|
|
92
|
+
return didReorder ? next : nodes;
|
|
93
|
+
};
|
|
94
|
+
var getNodeLabel = (node, renderLabel) => {
|
|
95
|
+
if (renderLabel) return renderLabel(node);
|
|
96
|
+
return node.label ?? node.title ?? String(node.id);
|
|
97
|
+
};
|
|
98
|
+
var TreeItem = ({
|
|
99
|
+
node,
|
|
100
|
+
depth,
|
|
101
|
+
control,
|
|
102
|
+
draggable,
|
|
103
|
+
expandedIds,
|
|
104
|
+
onToggle,
|
|
105
|
+
onCheck,
|
|
106
|
+
onAdd,
|
|
107
|
+
renderLabel,
|
|
108
|
+
itemClassName,
|
|
109
|
+
addLabel,
|
|
110
|
+
expandOnLabelClick
|
|
111
|
+
}) => {
|
|
112
|
+
const nodeId = String(node.id);
|
|
113
|
+
const hasChildren = Array.isArray(node.children) && node.children.length > 0;
|
|
114
|
+
const isExpanded = expandedIds.has(nodeId);
|
|
115
|
+
const {
|
|
116
|
+
attributes,
|
|
117
|
+
listeners,
|
|
118
|
+
setNodeRef,
|
|
119
|
+
transform,
|
|
120
|
+
transition,
|
|
121
|
+
isDragging
|
|
122
|
+
} = (0, import_sortable.useSortable)({ id: nodeId, disabled: !draggable });
|
|
123
|
+
const style = {
|
|
124
|
+
transform: import_utilities.CSS.Transform.toString(transform),
|
|
125
|
+
transition
|
|
126
|
+
};
|
|
127
|
+
const label = getNodeLabel(node, renderLabel);
|
|
128
|
+
const handleToggle = (0, import_react.useCallback)(() => {
|
|
129
|
+
if (hasChildren) onToggle(nodeId);
|
|
130
|
+
}, [hasChildren, onToggle, nodeId]);
|
|
131
|
+
const handleCheck = (event) => {
|
|
132
|
+
if (!onCheck) return;
|
|
133
|
+
onCheck(node, event.target.checked);
|
|
134
|
+
};
|
|
135
|
+
const handleAdd = () => {
|
|
136
|
+
onAdd == null ? void 0 : onAdd(nodeId);
|
|
137
|
+
};
|
|
138
|
+
const checkboxProps = {};
|
|
139
|
+
if (typeof node.checked === "boolean") {
|
|
140
|
+
checkboxProps.checked = node.checked;
|
|
141
|
+
}
|
|
142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
143
|
+
"li",
|
|
144
|
+
{
|
|
145
|
+
ref: setNodeRef,
|
|
146
|
+
style,
|
|
147
|
+
role: "treeitem",
|
|
148
|
+
"aria-expanded": hasChildren ? isExpanded : void 0,
|
|
149
|
+
"aria-level": depth + 1,
|
|
150
|
+
className: (0, import_utils.cx)("group", isDragging && "opacity-60"),
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
153
|
+
"div",
|
|
154
|
+
{
|
|
155
|
+
className: (0, import_utils.cx)(
|
|
156
|
+
"flex items-center gap-2 rounded-md px-2 py-2 text-sm",
|
|
157
|
+
"text-fg-base transition-colors",
|
|
158
|
+
"hover:bg-bg-minimal",
|
|
159
|
+
itemClassName
|
|
160
|
+
),
|
|
161
|
+
style: { paddingLeft: depth * 16 + 8 },
|
|
162
|
+
children: [
|
|
163
|
+
draggable ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
164
|
+
"button",
|
|
165
|
+
{
|
|
166
|
+
type: "button",
|
|
167
|
+
"data-tree-drag-handle": true,
|
|
168
|
+
className: (0, import_utils.cx)(
|
|
169
|
+
"inline-flex h-7 w-7 items-center justify-center rounded-md",
|
|
170
|
+
"text-fg-muted hover:bg-bg-subtle"
|
|
171
|
+
),
|
|
172
|
+
...attributes,
|
|
173
|
+
...listeners,
|
|
174
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_icons.Draghandle, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
175
|
+
}
|
|
176
|
+
) : null,
|
|
177
|
+
control === "chevron" || hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
178
|
+
"button",
|
|
179
|
+
{
|
|
180
|
+
type: "button",
|
|
181
|
+
"data-tree-toggle": true,
|
|
182
|
+
className: (0, import_utils.cx)(
|
|
183
|
+
"inline-flex h-7 w-7 items-center justify-center rounded-md",
|
|
184
|
+
"text-fg-muted transition-transform",
|
|
185
|
+
hasChildren ? "hover:bg-bg-subtle" : "opacity-40"
|
|
186
|
+
),
|
|
187
|
+
onClick: handleToggle,
|
|
188
|
+
"aria-label": isExpanded ? "Collapse" : "Expand",
|
|
189
|
+
children: hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
190
|
+
import_icons.Chevron,
|
|
191
|
+
{
|
|
192
|
+
className: (0, import_utils.cx)(
|
|
193
|
+
"h-4 w-4 transition-transform duration-200",
|
|
194
|
+
isExpanded && "rotate-90"
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "h-4 w-4", "aria-hidden": "true" })
|
|
198
|
+
}
|
|
199
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "h-7 w-7", "aria-hidden": "true" }),
|
|
200
|
+
control === "checkbox" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_forms.Field, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
201
|
+
import_forms.Checkbox,
|
|
202
|
+
{
|
|
203
|
+
"aria-label": typeof label === "string" ? label : "Select",
|
|
204
|
+
indeterminate: Boolean(node.indeterminate),
|
|
205
|
+
plus: Boolean(node.plus),
|
|
206
|
+
disabled: Boolean(node.disabled),
|
|
207
|
+
onChange: handleCheck,
|
|
208
|
+
...checkboxProps
|
|
209
|
+
}
|
|
210
|
+
) }) : null,
|
|
211
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
212
|
+
"button",
|
|
213
|
+
{
|
|
214
|
+
type: "button",
|
|
215
|
+
className: (0, import_utils.cx)(
|
|
216
|
+
"flex-1 text-left",
|
|
217
|
+
"text-fg-base hover:text-fg-strong",
|
|
218
|
+
expandOnLabelClick && hasChildren && "cursor-pointer"
|
|
219
|
+
),
|
|
220
|
+
onClick: expandOnLabelClick ? handleToggle : void 0,
|
|
221
|
+
children: label
|
|
222
|
+
}
|
|
223
|
+
),
|
|
224
|
+
onAdd ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
225
|
+
"button",
|
|
226
|
+
{
|
|
227
|
+
type: "button",
|
|
228
|
+
"data-tree-add": true,
|
|
229
|
+
className: (0, import_utils.cx)(
|
|
230
|
+
"rounded-md border border-border-subtle px-2 py-1 text-xs",
|
|
231
|
+
"text-fg-muted hover:border-border-base hover:text-fg-base"
|
|
232
|
+
),
|
|
233
|
+
onClick: handleAdd,
|
|
234
|
+
children: addLabel
|
|
235
|
+
}
|
|
236
|
+
) : null
|
|
237
|
+
]
|
|
238
|
+
}
|
|
239
|
+
),
|
|
240
|
+
hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
241
|
+
"div",
|
|
242
|
+
{
|
|
243
|
+
className: (0, import_utils.cx)(
|
|
244
|
+
"grid overflow-hidden transition-[grid-template-rows,opacity] duration-200 ease-out",
|
|
245
|
+
isExpanded ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"
|
|
246
|
+
),
|
|
247
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
248
|
+
TreeList,
|
|
249
|
+
{
|
|
250
|
+
nodes: node.children,
|
|
251
|
+
depth: depth + 1,
|
|
252
|
+
control,
|
|
253
|
+
draggable,
|
|
254
|
+
expandedIds,
|
|
255
|
+
onToggle,
|
|
256
|
+
onCheck,
|
|
257
|
+
onAdd,
|
|
258
|
+
renderLabel,
|
|
259
|
+
itemClassName,
|
|
260
|
+
addLabel,
|
|
261
|
+
expandOnLabelClick
|
|
262
|
+
}
|
|
263
|
+
) })
|
|
264
|
+
}
|
|
265
|
+
) : null
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
};
|
|
270
|
+
var TreeList = ({
|
|
271
|
+
nodes,
|
|
272
|
+
depth,
|
|
273
|
+
control,
|
|
274
|
+
draggable,
|
|
275
|
+
expandedIds,
|
|
276
|
+
onToggle,
|
|
277
|
+
onCheck,
|
|
278
|
+
onAdd,
|
|
279
|
+
renderLabel,
|
|
280
|
+
itemClassName,
|
|
281
|
+
addLabel,
|
|
282
|
+
expandOnLabelClick
|
|
283
|
+
}) => {
|
|
284
|
+
const items = nodes.map((node) => String(node.id));
|
|
285
|
+
const content = nodes.map((node) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
286
|
+
TreeItem,
|
|
287
|
+
{
|
|
288
|
+
node,
|
|
289
|
+
depth,
|
|
290
|
+
control,
|
|
291
|
+
draggable,
|
|
292
|
+
expandedIds,
|
|
293
|
+
onToggle,
|
|
294
|
+
onCheck,
|
|
295
|
+
onAdd,
|
|
296
|
+
renderLabel,
|
|
297
|
+
itemClassName,
|
|
298
|
+
addLabel,
|
|
299
|
+
expandOnLabelClick
|
|
300
|
+
},
|
|
301
|
+
String(node.id)
|
|
302
|
+
));
|
|
303
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sortable.SortableContext, { items, strategy: import_sortable.verticalListSortingStrategy, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { role: depth === 0 ? "tree" : "group", className: "space-y-1", children: content }) });
|
|
304
|
+
};
|
|
305
|
+
function TreeSelect({
|
|
306
|
+
data,
|
|
307
|
+
control = "checkbox",
|
|
308
|
+
className,
|
|
309
|
+
itemClassName,
|
|
310
|
+
draggable = true,
|
|
311
|
+
onReorder,
|
|
312
|
+
onDataChange,
|
|
313
|
+
onExpandedChange,
|
|
314
|
+
expandedIds,
|
|
315
|
+
defaultExpandedIds = [],
|
|
316
|
+
onCheck,
|
|
317
|
+
onAdd,
|
|
318
|
+
addLabel = "Add",
|
|
319
|
+
showRootAdd = false,
|
|
320
|
+
rootAddLabel = "Add root",
|
|
321
|
+
renderLabel,
|
|
322
|
+
expandOnLabelClick = true
|
|
323
|
+
}) {
|
|
324
|
+
const nodes = (0, import_react.useMemo)(() => normalizeNodes(data), [data]);
|
|
325
|
+
const [internalExpanded, setInternalExpanded] = (0, import_react.useState)(
|
|
326
|
+
new Set(defaultExpandedIds.map(String))
|
|
327
|
+
);
|
|
328
|
+
const isControlled = Array.isArray(expandedIds);
|
|
329
|
+
const expandedSet = (0, import_react.useMemo)(() => {
|
|
330
|
+
if (isControlled) {
|
|
331
|
+
return new Set(expandedIds.map(String));
|
|
332
|
+
}
|
|
333
|
+
return internalExpanded;
|
|
334
|
+
}, [expandedIds, internalExpanded, isControlled]);
|
|
335
|
+
const nodeInfo = (0, import_react.useMemo)(() => collectNodeInfo(nodes), [nodes]);
|
|
336
|
+
const handleToggle = (0, import_react.useCallback)(
|
|
337
|
+
(nodeId) => {
|
|
338
|
+
const next = new Set(expandedSet);
|
|
339
|
+
if (next.has(nodeId)) {
|
|
340
|
+
next.delete(nodeId);
|
|
341
|
+
} else {
|
|
342
|
+
next.add(nodeId);
|
|
343
|
+
}
|
|
344
|
+
if (!isControlled) {
|
|
345
|
+
setInternalExpanded(next);
|
|
346
|
+
}
|
|
347
|
+
onExpandedChange == null ? void 0 : onExpandedChange(Array.from(next));
|
|
348
|
+
},
|
|
349
|
+
[expandedSet, isControlled, onExpandedChange]
|
|
350
|
+
);
|
|
351
|
+
const sensors = (0, import_core.useSensors)(
|
|
352
|
+
(0, import_core.useSensor)(import_core.PointerSensor),
|
|
353
|
+
(0, import_core.useSensor)(import_core.KeyboardSensor, { coordinateGetter: import_sortable.sortableKeyboardCoordinates })
|
|
354
|
+
);
|
|
355
|
+
const handleDragEnd = (0, import_react.useCallback)(
|
|
356
|
+
(event) => {
|
|
357
|
+
const { active, over } = event || {};
|
|
358
|
+
if (!active || !over) return;
|
|
359
|
+
if (active.id === over.id) return;
|
|
360
|
+
const activeInfo = nodeInfo.get(String(active.id));
|
|
361
|
+
const overInfo = nodeInfo.get(String(over.id));
|
|
362
|
+
if (!activeInfo || !overInfo) return;
|
|
363
|
+
if (activeInfo.parentId !== overInfo.parentId) return;
|
|
364
|
+
const parentId = activeInfo.parentId;
|
|
365
|
+
const fromIndex = activeInfo.index;
|
|
366
|
+
const toIndex = overInfo.index;
|
|
367
|
+
if (fromIndex === toIndex) return;
|
|
368
|
+
const nextData = reorderTree(nodes, parentId, fromIndex, toIndex);
|
|
369
|
+
onReorder == null ? void 0 : onReorder({
|
|
370
|
+
parentId,
|
|
371
|
+
fromIndex,
|
|
372
|
+
toIndex,
|
|
373
|
+
activeId: String(active.id),
|
|
374
|
+
overId: String(over.id)
|
|
375
|
+
});
|
|
376
|
+
onDataChange == null ? void 0 : onDataChange(nextData);
|
|
377
|
+
},
|
|
378
|
+
[nodeInfo, nodes, onReorder, onDataChange]
|
|
379
|
+
);
|
|
380
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
381
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
382
|
+
TreeList,
|
|
383
|
+
{
|
|
384
|
+
nodes,
|
|
385
|
+
depth: 0,
|
|
386
|
+
control,
|
|
387
|
+
draggable,
|
|
388
|
+
expandedIds: expandedSet,
|
|
389
|
+
onToggle: handleToggle,
|
|
390
|
+
onCheck,
|
|
391
|
+
onAdd,
|
|
392
|
+
renderLabel,
|
|
393
|
+
itemClassName,
|
|
394
|
+
addLabel,
|
|
395
|
+
expandOnLabelClick
|
|
396
|
+
}
|
|
397
|
+
),
|
|
398
|
+
onAdd && showRootAdd ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mt-2 px-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
399
|
+
"button",
|
|
400
|
+
{
|
|
401
|
+
type: "button",
|
|
402
|
+
"data-tree-add-root": true,
|
|
403
|
+
className: (0, import_utils.cx)(
|
|
404
|
+
"rounded-md border border-border-subtle px-2 py-1 text-xs",
|
|
405
|
+
"text-fg-muted hover:border-border-base hover:text-fg-base"
|
|
406
|
+
),
|
|
407
|
+
onClick: () => onAdd(null),
|
|
408
|
+
children: rootAddLabel
|
|
409
|
+
}
|
|
410
|
+
) }) : null
|
|
411
|
+
] });
|
|
412
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: (0, import_utils.cx)("w-full", className), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
413
|
+
import_core.DndContext,
|
|
414
|
+
{
|
|
415
|
+
sensors,
|
|
416
|
+
collisionDetection: import_core.closestCenter,
|
|
417
|
+
onDragEnd: handleDragEnd,
|
|
418
|
+
children: content
|
|
419
|
+
}
|
|
420
|
+
) });
|
|
421
|
+
}
|
|
422
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
423
|
+
0 && (module.exports = {
|
|
424
|
+
TreeSelect
|
|
425
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
// src/elements/TreeSelect.jsx
|
|
2
|
+
import React, { useCallback, useMemo, useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
DndContext,
|
|
5
|
+
PointerSensor,
|
|
6
|
+
KeyboardSensor,
|
|
7
|
+
useSensor,
|
|
8
|
+
useSensors,
|
|
9
|
+
closestCenter
|
|
10
|
+
} from "@dnd-kit/core";
|
|
11
|
+
import {
|
|
12
|
+
SortableContext,
|
|
13
|
+
useSortable,
|
|
14
|
+
verticalListSortingStrategy,
|
|
15
|
+
sortableKeyboardCoordinates
|
|
16
|
+
} from "@dnd-kit/sortable";
|
|
17
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
18
|
+
import { Field, Checkbox } from "@edux-design/forms";
|
|
19
|
+
import { Chevron, Draghandle } from "@edux-design/icons";
|
|
20
|
+
import { cx } from "@edux-design/utils";
|
|
21
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
22
|
+
var normalizeNodes = (data) => {
|
|
23
|
+
if (!data) return [];
|
|
24
|
+
if (Array.isArray(data)) return data;
|
|
25
|
+
return [data];
|
|
26
|
+
};
|
|
27
|
+
var collectNodeInfo = (nodes, parentId = null, map = /* @__PURE__ */ new Map()) => {
|
|
28
|
+
nodes.forEach((node, index) => {
|
|
29
|
+
const nodeId = String(node.id);
|
|
30
|
+
map.set(nodeId, { parentId, index, node });
|
|
31
|
+
if (Array.isArray(node.children)) {
|
|
32
|
+
collectNodeInfo(node.children, nodeId, map);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
return map;
|
|
36
|
+
};
|
|
37
|
+
var reorderArray = (list, fromIndex, toIndex) => {
|
|
38
|
+
const next = list.slice();
|
|
39
|
+
const [moved] = next.splice(fromIndex, 1);
|
|
40
|
+
next.splice(toIndex, 0, moved);
|
|
41
|
+
return next;
|
|
42
|
+
};
|
|
43
|
+
var reorderTree = (nodes, parentId, fromIndex, toIndex) => {
|
|
44
|
+
if (parentId === null) {
|
|
45
|
+
return reorderArray(nodes, fromIndex, toIndex);
|
|
46
|
+
}
|
|
47
|
+
let didReorder = false;
|
|
48
|
+
const next = nodes.map((node) => {
|
|
49
|
+
const nodeId = String(node.id);
|
|
50
|
+
if (nodeId === parentId) {
|
|
51
|
+
const children = Array.isArray(node.children) ? node.children : [];
|
|
52
|
+
didReorder = true;
|
|
53
|
+
return {
|
|
54
|
+
...node,
|
|
55
|
+
children: reorderArray(children, fromIndex, toIndex)
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (!Array.isArray(node.children)) return node;
|
|
59
|
+
const nextChildren = reorderTree(
|
|
60
|
+
node.children,
|
|
61
|
+
parentId,
|
|
62
|
+
fromIndex,
|
|
63
|
+
toIndex
|
|
64
|
+
);
|
|
65
|
+
if (nextChildren === node.children) return node;
|
|
66
|
+
didReorder = true;
|
|
67
|
+
return { ...node, children: nextChildren };
|
|
68
|
+
});
|
|
69
|
+
return didReorder ? next : nodes;
|
|
70
|
+
};
|
|
71
|
+
var getNodeLabel = (node, renderLabel) => {
|
|
72
|
+
if (renderLabel) return renderLabel(node);
|
|
73
|
+
return node.label ?? node.title ?? String(node.id);
|
|
74
|
+
};
|
|
75
|
+
var TreeItem = ({
|
|
76
|
+
node,
|
|
77
|
+
depth,
|
|
78
|
+
control,
|
|
79
|
+
draggable,
|
|
80
|
+
expandedIds,
|
|
81
|
+
onToggle,
|
|
82
|
+
onCheck,
|
|
83
|
+
onAdd,
|
|
84
|
+
renderLabel,
|
|
85
|
+
itemClassName,
|
|
86
|
+
addLabel,
|
|
87
|
+
expandOnLabelClick
|
|
88
|
+
}) => {
|
|
89
|
+
const nodeId = String(node.id);
|
|
90
|
+
const hasChildren = Array.isArray(node.children) && node.children.length > 0;
|
|
91
|
+
const isExpanded = expandedIds.has(nodeId);
|
|
92
|
+
const {
|
|
93
|
+
attributes,
|
|
94
|
+
listeners,
|
|
95
|
+
setNodeRef,
|
|
96
|
+
transform,
|
|
97
|
+
transition,
|
|
98
|
+
isDragging
|
|
99
|
+
} = useSortable({ id: nodeId, disabled: !draggable });
|
|
100
|
+
const style = {
|
|
101
|
+
transform: CSS.Transform.toString(transform),
|
|
102
|
+
transition
|
|
103
|
+
};
|
|
104
|
+
const label = getNodeLabel(node, renderLabel);
|
|
105
|
+
const handleToggle = useCallback(() => {
|
|
106
|
+
if (hasChildren) onToggle(nodeId);
|
|
107
|
+
}, [hasChildren, onToggle, nodeId]);
|
|
108
|
+
const handleCheck = (event) => {
|
|
109
|
+
if (!onCheck) return;
|
|
110
|
+
onCheck(node, event.target.checked);
|
|
111
|
+
};
|
|
112
|
+
const handleAdd = () => {
|
|
113
|
+
onAdd == null ? void 0 : onAdd(nodeId);
|
|
114
|
+
};
|
|
115
|
+
const checkboxProps = {};
|
|
116
|
+
if (typeof node.checked === "boolean") {
|
|
117
|
+
checkboxProps.checked = node.checked;
|
|
118
|
+
}
|
|
119
|
+
return /* @__PURE__ */ jsxs(
|
|
120
|
+
"li",
|
|
121
|
+
{
|
|
122
|
+
ref: setNodeRef,
|
|
123
|
+
style,
|
|
124
|
+
role: "treeitem",
|
|
125
|
+
"aria-expanded": hasChildren ? isExpanded : void 0,
|
|
126
|
+
"aria-level": depth + 1,
|
|
127
|
+
className: cx("group", isDragging && "opacity-60"),
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ jsxs(
|
|
130
|
+
"div",
|
|
131
|
+
{
|
|
132
|
+
className: cx(
|
|
133
|
+
"flex items-center gap-2 rounded-md px-2 py-2 text-sm",
|
|
134
|
+
"text-fg-base transition-colors",
|
|
135
|
+
"hover:bg-bg-minimal",
|
|
136
|
+
itemClassName
|
|
137
|
+
),
|
|
138
|
+
style: { paddingLeft: depth * 16 + 8 },
|
|
139
|
+
children: [
|
|
140
|
+
draggable ? /* @__PURE__ */ jsx(
|
|
141
|
+
"button",
|
|
142
|
+
{
|
|
143
|
+
type: "button",
|
|
144
|
+
"data-tree-drag-handle": true,
|
|
145
|
+
className: cx(
|
|
146
|
+
"inline-flex h-7 w-7 items-center justify-center rounded-md",
|
|
147
|
+
"text-fg-muted hover:bg-bg-subtle"
|
|
148
|
+
),
|
|
149
|
+
...attributes,
|
|
150
|
+
...listeners,
|
|
151
|
+
children: /* @__PURE__ */ jsx(Draghandle, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
152
|
+
}
|
|
153
|
+
) : null,
|
|
154
|
+
control === "chevron" || hasChildren ? /* @__PURE__ */ jsx(
|
|
155
|
+
"button",
|
|
156
|
+
{
|
|
157
|
+
type: "button",
|
|
158
|
+
"data-tree-toggle": true,
|
|
159
|
+
className: cx(
|
|
160
|
+
"inline-flex h-7 w-7 items-center justify-center rounded-md",
|
|
161
|
+
"text-fg-muted transition-transform",
|
|
162
|
+
hasChildren ? "hover:bg-bg-subtle" : "opacity-40"
|
|
163
|
+
),
|
|
164
|
+
onClick: handleToggle,
|
|
165
|
+
"aria-label": isExpanded ? "Collapse" : "Expand",
|
|
166
|
+
children: hasChildren ? /* @__PURE__ */ jsx(
|
|
167
|
+
Chevron,
|
|
168
|
+
{
|
|
169
|
+
className: cx(
|
|
170
|
+
"h-4 w-4 transition-transform duration-200",
|
|
171
|
+
isExpanded && "rotate-90"
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
) : /* @__PURE__ */ jsx("span", { className: "h-4 w-4", "aria-hidden": "true" })
|
|
175
|
+
}
|
|
176
|
+
) : /* @__PURE__ */ jsx("span", { className: "h-7 w-7", "aria-hidden": "true" }),
|
|
177
|
+
control === "checkbox" ? /* @__PURE__ */ jsx(Field, { children: /* @__PURE__ */ jsx(
|
|
178
|
+
Checkbox,
|
|
179
|
+
{
|
|
180
|
+
"aria-label": typeof label === "string" ? label : "Select",
|
|
181
|
+
indeterminate: Boolean(node.indeterminate),
|
|
182
|
+
plus: Boolean(node.plus),
|
|
183
|
+
disabled: Boolean(node.disabled),
|
|
184
|
+
onChange: handleCheck,
|
|
185
|
+
...checkboxProps
|
|
186
|
+
}
|
|
187
|
+
) }) : null,
|
|
188
|
+
/* @__PURE__ */ jsx(
|
|
189
|
+
"button",
|
|
190
|
+
{
|
|
191
|
+
type: "button",
|
|
192
|
+
className: cx(
|
|
193
|
+
"flex-1 text-left",
|
|
194
|
+
"text-fg-base hover:text-fg-strong",
|
|
195
|
+
expandOnLabelClick && hasChildren && "cursor-pointer"
|
|
196
|
+
),
|
|
197
|
+
onClick: expandOnLabelClick ? handleToggle : void 0,
|
|
198
|
+
children: label
|
|
199
|
+
}
|
|
200
|
+
),
|
|
201
|
+
onAdd ? /* @__PURE__ */ jsx(
|
|
202
|
+
"button",
|
|
203
|
+
{
|
|
204
|
+
type: "button",
|
|
205
|
+
"data-tree-add": true,
|
|
206
|
+
className: cx(
|
|
207
|
+
"rounded-md border border-border-subtle px-2 py-1 text-xs",
|
|
208
|
+
"text-fg-muted hover:border-border-base hover:text-fg-base"
|
|
209
|
+
),
|
|
210
|
+
onClick: handleAdd,
|
|
211
|
+
children: addLabel
|
|
212
|
+
}
|
|
213
|
+
) : null
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
),
|
|
217
|
+
hasChildren ? /* @__PURE__ */ jsx(
|
|
218
|
+
"div",
|
|
219
|
+
{
|
|
220
|
+
className: cx(
|
|
221
|
+
"grid overflow-hidden transition-[grid-template-rows,opacity] duration-200 ease-out",
|
|
222
|
+
isExpanded ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"
|
|
223
|
+
),
|
|
224
|
+
children: /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: /* @__PURE__ */ jsx(
|
|
225
|
+
TreeList,
|
|
226
|
+
{
|
|
227
|
+
nodes: node.children,
|
|
228
|
+
depth: depth + 1,
|
|
229
|
+
control,
|
|
230
|
+
draggable,
|
|
231
|
+
expandedIds,
|
|
232
|
+
onToggle,
|
|
233
|
+
onCheck,
|
|
234
|
+
onAdd,
|
|
235
|
+
renderLabel,
|
|
236
|
+
itemClassName,
|
|
237
|
+
addLabel,
|
|
238
|
+
expandOnLabelClick
|
|
239
|
+
}
|
|
240
|
+
) })
|
|
241
|
+
}
|
|
242
|
+
) : null
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
);
|
|
246
|
+
};
|
|
247
|
+
var TreeList = ({
|
|
248
|
+
nodes,
|
|
249
|
+
depth,
|
|
250
|
+
control,
|
|
251
|
+
draggable,
|
|
252
|
+
expandedIds,
|
|
253
|
+
onToggle,
|
|
254
|
+
onCheck,
|
|
255
|
+
onAdd,
|
|
256
|
+
renderLabel,
|
|
257
|
+
itemClassName,
|
|
258
|
+
addLabel,
|
|
259
|
+
expandOnLabelClick
|
|
260
|
+
}) => {
|
|
261
|
+
const items = nodes.map((node) => String(node.id));
|
|
262
|
+
const content = nodes.map((node) => /* @__PURE__ */ jsx(
|
|
263
|
+
TreeItem,
|
|
264
|
+
{
|
|
265
|
+
node,
|
|
266
|
+
depth,
|
|
267
|
+
control,
|
|
268
|
+
draggable,
|
|
269
|
+
expandedIds,
|
|
270
|
+
onToggle,
|
|
271
|
+
onCheck,
|
|
272
|
+
onAdd,
|
|
273
|
+
renderLabel,
|
|
274
|
+
itemClassName,
|
|
275
|
+
addLabel,
|
|
276
|
+
expandOnLabelClick
|
|
277
|
+
},
|
|
278
|
+
String(node.id)
|
|
279
|
+
));
|
|
280
|
+
return /* @__PURE__ */ jsx(SortableContext, { items, strategy: verticalListSortingStrategy, children: /* @__PURE__ */ jsx("ul", { role: depth === 0 ? "tree" : "group", className: "space-y-1", children: content }) });
|
|
281
|
+
};
|
|
282
|
+
function TreeSelect({
|
|
283
|
+
data,
|
|
284
|
+
control = "checkbox",
|
|
285
|
+
className,
|
|
286
|
+
itemClassName,
|
|
287
|
+
draggable = true,
|
|
288
|
+
onReorder,
|
|
289
|
+
onDataChange,
|
|
290
|
+
onExpandedChange,
|
|
291
|
+
expandedIds,
|
|
292
|
+
defaultExpandedIds = [],
|
|
293
|
+
onCheck,
|
|
294
|
+
onAdd,
|
|
295
|
+
addLabel = "Add",
|
|
296
|
+
showRootAdd = false,
|
|
297
|
+
rootAddLabel = "Add root",
|
|
298
|
+
renderLabel,
|
|
299
|
+
expandOnLabelClick = true
|
|
300
|
+
}) {
|
|
301
|
+
const nodes = useMemo(() => normalizeNodes(data), [data]);
|
|
302
|
+
const [internalExpanded, setInternalExpanded] = useState(
|
|
303
|
+
new Set(defaultExpandedIds.map(String))
|
|
304
|
+
);
|
|
305
|
+
const isControlled = Array.isArray(expandedIds);
|
|
306
|
+
const expandedSet = useMemo(() => {
|
|
307
|
+
if (isControlled) {
|
|
308
|
+
return new Set(expandedIds.map(String));
|
|
309
|
+
}
|
|
310
|
+
return internalExpanded;
|
|
311
|
+
}, [expandedIds, internalExpanded, isControlled]);
|
|
312
|
+
const nodeInfo = useMemo(() => collectNodeInfo(nodes), [nodes]);
|
|
313
|
+
const handleToggle = useCallback(
|
|
314
|
+
(nodeId) => {
|
|
315
|
+
const next = new Set(expandedSet);
|
|
316
|
+
if (next.has(nodeId)) {
|
|
317
|
+
next.delete(nodeId);
|
|
318
|
+
} else {
|
|
319
|
+
next.add(nodeId);
|
|
320
|
+
}
|
|
321
|
+
if (!isControlled) {
|
|
322
|
+
setInternalExpanded(next);
|
|
323
|
+
}
|
|
324
|
+
onExpandedChange == null ? void 0 : onExpandedChange(Array.from(next));
|
|
325
|
+
},
|
|
326
|
+
[expandedSet, isControlled, onExpandedChange]
|
|
327
|
+
);
|
|
328
|
+
const sensors = useSensors(
|
|
329
|
+
useSensor(PointerSensor),
|
|
330
|
+
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
|
|
331
|
+
);
|
|
332
|
+
const handleDragEnd = useCallback(
|
|
333
|
+
(event) => {
|
|
334
|
+
const { active, over } = event || {};
|
|
335
|
+
if (!active || !over) return;
|
|
336
|
+
if (active.id === over.id) return;
|
|
337
|
+
const activeInfo = nodeInfo.get(String(active.id));
|
|
338
|
+
const overInfo = nodeInfo.get(String(over.id));
|
|
339
|
+
if (!activeInfo || !overInfo) return;
|
|
340
|
+
if (activeInfo.parentId !== overInfo.parentId) return;
|
|
341
|
+
const parentId = activeInfo.parentId;
|
|
342
|
+
const fromIndex = activeInfo.index;
|
|
343
|
+
const toIndex = overInfo.index;
|
|
344
|
+
if (fromIndex === toIndex) return;
|
|
345
|
+
const nextData = reorderTree(nodes, parentId, fromIndex, toIndex);
|
|
346
|
+
onReorder == null ? void 0 : onReorder({
|
|
347
|
+
parentId,
|
|
348
|
+
fromIndex,
|
|
349
|
+
toIndex,
|
|
350
|
+
activeId: String(active.id),
|
|
351
|
+
overId: String(over.id)
|
|
352
|
+
});
|
|
353
|
+
onDataChange == null ? void 0 : onDataChange(nextData);
|
|
354
|
+
},
|
|
355
|
+
[nodeInfo, nodes, onReorder, onDataChange]
|
|
356
|
+
);
|
|
357
|
+
const content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
358
|
+
/* @__PURE__ */ jsx(
|
|
359
|
+
TreeList,
|
|
360
|
+
{
|
|
361
|
+
nodes,
|
|
362
|
+
depth: 0,
|
|
363
|
+
control,
|
|
364
|
+
draggable,
|
|
365
|
+
expandedIds: expandedSet,
|
|
366
|
+
onToggle: handleToggle,
|
|
367
|
+
onCheck,
|
|
368
|
+
onAdd,
|
|
369
|
+
renderLabel,
|
|
370
|
+
itemClassName,
|
|
371
|
+
addLabel,
|
|
372
|
+
expandOnLabelClick
|
|
373
|
+
}
|
|
374
|
+
),
|
|
375
|
+
onAdd && showRootAdd ? /* @__PURE__ */ jsx("div", { className: "mt-2 px-2", children: /* @__PURE__ */ jsx(
|
|
376
|
+
"button",
|
|
377
|
+
{
|
|
378
|
+
type: "button",
|
|
379
|
+
"data-tree-add-root": true,
|
|
380
|
+
className: cx(
|
|
381
|
+
"rounded-md border border-border-subtle px-2 py-1 text-xs",
|
|
382
|
+
"text-fg-muted hover:border-border-base hover:text-fg-base"
|
|
383
|
+
),
|
|
384
|
+
onClick: () => onAdd(null),
|
|
385
|
+
children: rootAddLabel
|
|
386
|
+
}
|
|
387
|
+
) }) : null
|
|
388
|
+
] });
|
|
389
|
+
return /* @__PURE__ */ jsx("div", { className: cx("w-full", className), children: /* @__PURE__ */ jsx(
|
|
390
|
+
DndContext,
|
|
391
|
+
{
|
|
392
|
+
sensors,
|
|
393
|
+
collisionDetection: closestCenter,
|
|
394
|
+
onDragEnd: handleDragEnd,
|
|
395
|
+
children: content
|
|
396
|
+
}
|
|
397
|
+
) });
|
|
398
|
+
}
|
|
399
|
+
export {
|
|
400
|
+
TreeSelect
|
|
401
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@edux-design/tree-select",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"sideEffects": [
|
|
6
|
+
"**/*.css"
|
|
7
|
+
],
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"lint": "eslint . --max-warnings 0",
|
|
21
|
+
"build": "tsup src/index.js --format esm,cjs --dts",
|
|
22
|
+
"dev": "tsup src/index.js --watch --format esm,cjs --dts",
|
|
23
|
+
"check-types": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@dnd-kit/core": "6.3.1",
|
|
27
|
+
"@dnd-kit/sortable": "^8.0.0",
|
|
28
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
29
|
+
"@edux-design/forms": "*",
|
|
30
|
+
"@edux-design/icons": "*",
|
|
31
|
+
"@edux-design/utils": "*"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"react": ">=18",
|
|
35
|
+
"react-dom": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"tsup": "^8.5.0",
|
|
39
|
+
"typescript": "^5.9.2"
|
|
40
|
+
}
|
|
41
|
+
}
|