react-tree-node-view 0.0.0-stage → 1.0.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/LICENSE +23 -0
- package/README.md +277 -2
- package/dist/TreeView.d.ts +78 -0
- package/dist/index.d.ts +2 -0
- package/dist/react-tree-node-view.cjs +1 -0
- package/dist/react-tree-node-view.js +544 -0
- package/dist/styles.css +1 -0
- package/package.json +65 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hossein Najafi
|
|
4
|
+
|
|
5
|
+
Website: https://hosseinajafi.com/
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,278 @@
|
|
|
1
|
-
#
|
|
1
|
+
# react-tree-node-view
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+

|
|
8
|
+

|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
A lightweight, dependency-free React tree view with selection, inline editing, drag-and-drop, and node management.
|
|
14
|
+
|
|
15
|
+
<div align="center">
|
|
16
|
+
<img src="./images/react-tree-node-view-demo.gif" alt="React Tree Node Demo" width="100%" />
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install react-tree-node-view
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The component imports its scoped stylesheet automatically. You can also import it explicitly when your bundler handles CSS entry points:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
import "react-tree-node-view/styles.css";
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { useState } from "react";
|
|
35
|
+
import { TreeView, type TreeNode } from "react-tree-node-view";
|
|
36
|
+
|
|
37
|
+
const initialItems: TreeNode[] = [
|
|
38
|
+
{
|
|
39
|
+
key: "products",
|
|
40
|
+
title: "Products",
|
|
41
|
+
children: [{ key: "books", title: "Books" }],
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
export default function Example() {
|
|
46
|
+
const [items, setItems] = useState(initialItems);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<TreeView
|
|
50
|
+
items={items}
|
|
51
|
+
onChange={setItems}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Click an editable title to rename it. Press Enter or move focus away to save; press Escape to cancel. Drag rows before, inside, or after another row to reorder them.
|
|
58
|
+
|
|
59
|
+
## Preview
|
|
60
|
+
|
|
61
|
+
### Light & Dark Themes
|
|
62
|
+
|
|
63
|
+
<p align="center">
|
|
64
|
+
<img src="./images/Light.png" alt="Light Theme" width="49%" />
|
|
65
|
+
<img src="./images/Dark.png" alt="Dark Theme" width="49%" />
|
|
66
|
+
</p>
|
|
67
|
+
|
|
68
|
+
### Custom Colors
|
|
69
|
+
|
|
70
|
+
Use the `defaultColor` prop to customize the primary color across the entire component.
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
export default function CustomColorExample() {
|
|
74
|
+
return (
|
|
75
|
+
<TreeView
|
|
76
|
+
defaultItems={categories}
|
|
77
|
+
defaultColor="#8b5cf6"
|
|
78
|
+
theme="light"
|
|
79
|
+
/>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
<p align="center">
|
|
85
|
+
<img src="./images/Light-color.png" alt="Light Theme with Custom Color" width="49%" />
|
|
86
|
+
<img src="./images/Dark-color.png" alt="Dark Theme with Custom Color" width="49%" />
|
|
87
|
+
</p>
|
|
88
|
+
|
|
89
|
+
The `defaultColor` prop customizes the primary color throughout the tree view, including buttons, icons, active states, hover effects, and focus indicators. It works with both light and dark themes.
|
|
90
|
+
|
|
91
|
+
## Selectable mode
|
|
92
|
+
|
|
93
|
+
Set `type="selectable"` to enable checkbox-based node selection.
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
<TreeView
|
|
97
|
+
type="selectable"
|
|
98
|
+
defaultItems={initialItems}
|
|
99
|
+
defaultChecked={[{ _id: "books", title: "Books" }]}
|
|
100
|
+
onCheckedChange={(checked) => console.log(checked)}
|
|
101
|
+
/>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Selecting a child also includes its ancestors, matching the serialized `TreeItem[]` format used by the component.
|
|
105
|
+
|
|
106
|
+
<p align="center">
|
|
107
|
+
<img src="./images/selectable.png" alt="Selectable Tree View" width="75%" />
|
|
108
|
+
</p>
|
|
109
|
+
|
|
110
|
+
## Custom icons and labels
|
|
111
|
+
|
|
112
|
+
Every built-in icon can be replaced with a `ReactNode`. Omitted icons continue to use the package's inline SVG defaults.
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
import { TreeView, type TreeViewIcons } from "react-tree-node-view";
|
|
116
|
+
|
|
117
|
+
const icons: TreeViewIcons = {
|
|
118
|
+
folder: <span aria-hidden="true">📁</span>,
|
|
119
|
+
openFolder: <span aria-hidden="true">📂</span>,
|
|
120
|
+
add: <span aria-hidden="true">+</span>,
|
|
121
|
+
delete: <span aria-hidden="true">×</span>,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
<TreeView
|
|
125
|
+
defaultItems={initialItems}
|
|
126
|
+
icons={icons}
|
|
127
|
+
labels={{
|
|
128
|
+
addCategory: "Create category",
|
|
129
|
+
defaultCategoryTitle: "Untitled category",
|
|
130
|
+
before: "Above",
|
|
131
|
+
}}
|
|
132
|
+
direction="ltr"
|
|
133
|
+
/>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The `labels` prop allows you to customize individual text labels. English defaults are used for any omitted labels. Dynamic accessibility labels also support the `{title}` placeholder, for example `addChild: "Add {title} below"`.
|
|
137
|
+
|
|
138
|
+
The `direction` prop supports both `ltr` and `rtl` layouts, controlling indentation, text alignment, icon placement, actions, and drop indicators.
|
|
139
|
+
|
|
140
|
+
## Drop indicator colors
|
|
141
|
+
|
|
142
|
+
Customize the colors of drag-and-drop indicators using the `dropIndicatorColors` prop. It accepts an object with three optional properties:
|
|
143
|
+
|
|
144
|
+
- `before`: The color displayed when dropping a category above another category.
|
|
145
|
+
- `inside`: The color displayed when dropping a category inside another category.
|
|
146
|
+
- `after`: The color displayed when dropping a category below another category.
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
<TreeView
|
|
150
|
+
defaultItems={initialItems}
|
|
151
|
+
dropIndicatorColors={{
|
|
152
|
+
before: "#0ea5e9",
|
|
153
|
+
inside: "#f59e0b",
|
|
154
|
+
after: "#22c55e",
|
|
155
|
+
}}
|
|
156
|
+
/>
|
|
157
|
+
|
|
158
|
+
Each color can be customized independently. Omitted values fall back to the package's default colors.
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## API
|
|
162
|
+
|
|
163
|
+
All `TreeView` props are optional. When `items` or `checkedItems` is provided, that value is controlled; otherwise the corresponding `defaultItems` or `defaultChecked` value initializes internal state.
|
|
164
|
+
|
|
165
|
+
| Prop | Type | Description |
|
|
166
|
+
| --- | --- | --- |
|
|
167
|
+
| `items` | `TreeNode[]` | Optional controlled tree data. If omitted, the component uses `defaultItems` and internal state. |
|
|
168
|
+
| `defaultItems` | `TreeNode[]` | Optional initial data for an uncontrolled tree. Defaults to `[]`. |
|
|
169
|
+
| `type` | `"editable" \| "selectable"` | Enables editing and drag actions or checkboxes. Defaults to `"editable"`. |
|
|
170
|
+
| `onChange` | `(items: TreeNode[]) => void` | Optional callback called after tree data changes. |
|
|
171
|
+
| `defaultChecked` | `TreeItem[]` | Optional initial checked items in selectable mode. Defaults to `[]`. |
|
|
172
|
+
| `checkedItems` | `TreeItem[]` | Optional controlled checked items. If omitted, checked state is managed internally. |
|
|
173
|
+
| `onCheckedChange` | `(items: TreeItem[]) => void` | Optional callback called after selection changes. |
|
|
174
|
+
| `onTitleChange` | `(node, title) => void` | Optional callback called after an inline title edit. |
|
|
175
|
+
| `onDelete` | `(node) => void` | Optional callback called after a node is removed. |
|
|
176
|
+
| `height` | `number` | Optional maximum tree height in pixels. Applied as the shell's `max-height`; overflow is scrollable when needed. |
|
|
177
|
+
| `className` | `string` | Optional class name applied to the component root. |
|
|
178
|
+
| `defaultColor` | `string` | Optional CSS color used as the primary color in both themes, including the Add Category button, icons, active states, hover states, and focus indicators. |
|
|
179
|
+
| `theme` | `"light" \| "dark"` | Complete component theme. Defaults to `"light"`. |
|
|
180
|
+
| `direction` | `"ltr" \| "rtl"` | Layout direction. Defaults to `"ltr"`; use `"rtl"` for right-to-left layouts. |
|
|
181
|
+
| `icons` | `TreeViewIcons` | Optional ReactNode replacements for `expand`, `collapse`, `dragHandle`, `folder`, `openFolder`, `add`, `delete`, `check`, and `empty`. Defaults to inline SVG icons for omitted entries. |
|
|
182
|
+
| `labels` | `Partial<TreeViewLabels>` | Optional partial overrides for action text, tooltips, accessibility labels, default titles, drag guide labels, loading text, and empty-state text. Defaults to English labels. |
|
|
183
|
+
| `dropIndicatorColors` | `Partial<TreeViewDropIndicatorColors>` | Optional partial customization of `before`, `inside`, and `after` drop colors in rows and the guide legend. Defaults to blue `#3b82f6`, amber `#f59e0b`, and green `#10b981`. |
|
|
184
|
+
| `loading` | `boolean` | Shows the loading skeleton when `true`. Defaults to `false`. |
|
|
185
|
+
| `emptyMessage` | `ReactNode` | Optional replacement for the default `"No categories found"` message when there are no nodes. |
|
|
186
|
+
| `disabled` | `boolean` | Disables editing, selection, and drag actions when `true`. Defaults to `false`. |
|
|
187
|
+
|
|
188
|
+
## Checked items
|
|
189
|
+
|
|
190
|
+
Use the `checkedItems` prop to control the selected items externally. When provided, the checked state is controlled by the parent component. Otherwise, the component manages the checked state internally.
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
const [checkedItems, setCheckedItems] = useState<TreeItem[]>([]);
|
|
194
|
+
|
|
195
|
+
<TreeView
|
|
196
|
+
defaultItems={initialItems}
|
|
197
|
+
checkedItems={checkedItems}
|
|
198
|
+
onCheckedChange={setCheckedItems}
|
|
199
|
+
/>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Selection change callback
|
|
203
|
+
|
|
204
|
+
Use `onCheckedChange` to receive the updated list of checked items whenever the selection changes.
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
<TreeView
|
|
208
|
+
defaultItems={initialItems}
|
|
209
|
+
onCheckedChange={(items) => {
|
|
210
|
+
console.log("Selected items:", items);
|
|
211
|
+
}}
|
|
212
|
+
/>
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Title change callback
|
|
216
|
+
|
|
217
|
+
Use `onTitleChange` to track inline title edits. It receives the updated node and its new title.
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
<TreeView
|
|
221
|
+
defaultItems={initialItems}
|
|
222
|
+
onTitleChange={(node, title) => {
|
|
223
|
+
console.log("Updated node:", node);
|
|
224
|
+
console.log("New title:", title);
|
|
225
|
+
}}
|
|
226
|
+
/>
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Delete callback
|
|
230
|
+
|
|
231
|
+
Use `onDelete` to be notified whenever a category is deleted. It receives the deleted node.
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
<TreeView
|
|
235
|
+
defaultItems={initialItems}
|
|
236
|
+
onDelete={(node) => {
|
|
237
|
+
console.log("Deleted category:", node);
|
|
238
|
+
}}
|
|
239
|
+
/>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## Loading state
|
|
243
|
+
|
|
244
|
+
Set `loading` to `true` to display the built-in loading skeleton while your categories are being loaded. It defaults to `false`.
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
<TreeView
|
|
248
|
+
defaultItems={initialItems}
|
|
249
|
+
loading={isLoading}
|
|
250
|
+
/>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Empty state message
|
|
254
|
+
|
|
255
|
+
Use `emptyMessage` to customize the content displayed when there are no categories. It accepts any `ReactNode`, so you can provide text, icons, or custom components. The default message is `"No categories found"`.
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
<TreeView
|
|
259
|
+
defaultItems={[]}
|
|
260
|
+
emptyMessage={
|
|
261
|
+
<div>
|
|
262
|
+
<span>No categories yet.</span>
|
|
263
|
+
<button onClick={() => console.log("Add category")}>
|
|
264
|
+
Create your first category
|
|
265
|
+
</button>
|
|
266
|
+
</div>
|
|
267
|
+
}
|
|
268
|
+
/>
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Development
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
npm install
|
|
275
|
+
npm run typecheck
|
|
276
|
+
npm run build
|
|
277
|
+
npm run pack:check
|
|
278
|
+
```
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import "./styles.css";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
export type TreeNode = {
|
|
4
|
+
key: string;
|
|
5
|
+
title: string;
|
|
6
|
+
children?: TreeNode[];
|
|
7
|
+
image?: string;
|
|
8
|
+
imagePath?: string;
|
|
9
|
+
};
|
|
10
|
+
export type TreeItem = {
|
|
11
|
+
_id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
};
|
|
14
|
+
export type TreeViewMode = "editable" | "selectable";
|
|
15
|
+
export type TreeViewDirection = "ltr" | "rtl";
|
|
16
|
+
export type TreeViewTheme = "light" | "dark";
|
|
17
|
+
export type TreeViewLabels = {
|
|
18
|
+
addCategory: string;
|
|
19
|
+
addChild: string;
|
|
20
|
+
delete: string;
|
|
21
|
+
expand: string;
|
|
22
|
+
collapse: string;
|
|
23
|
+
select: string;
|
|
24
|
+
edit: string;
|
|
25
|
+
dragHandle: string;
|
|
26
|
+
loading: string;
|
|
27
|
+
empty: string;
|
|
28
|
+
dragAndDropGuide: string;
|
|
29
|
+
before: string;
|
|
30
|
+
inside: string;
|
|
31
|
+
after: string;
|
|
32
|
+
defaultCategoryTitle: string;
|
|
33
|
+
};
|
|
34
|
+
export type TreeViewIcons = {
|
|
35
|
+
expand?: ReactNode;
|
|
36
|
+
collapse?: ReactNode;
|
|
37
|
+
dragHandle?: ReactNode;
|
|
38
|
+
folder?: ReactNode;
|
|
39
|
+
openFolder?: ReactNode;
|
|
40
|
+
add?: ReactNode;
|
|
41
|
+
delete?: ReactNode;
|
|
42
|
+
check?: ReactNode;
|
|
43
|
+
empty?: ReactNode;
|
|
44
|
+
};
|
|
45
|
+
export type TreeViewDropIndicatorColors = {
|
|
46
|
+
before: string;
|
|
47
|
+
inside: string;
|
|
48
|
+
after: string;
|
|
49
|
+
};
|
|
50
|
+
export type TreeViewProps = {
|
|
51
|
+
/** Controlled tree data. Use `defaultItems` for an uncontrolled tree. */
|
|
52
|
+
items?: TreeNode[];
|
|
53
|
+
/** Initial tree data for an uncontrolled tree. */
|
|
54
|
+
defaultItems?: TreeNode[];
|
|
55
|
+
/** Editable trees support add, delete, inline rename, and drag-and-drop. */
|
|
56
|
+
type?: TreeViewMode;
|
|
57
|
+
defaultChecked?: TreeItem[];
|
|
58
|
+
checkedItems?: TreeItem[];
|
|
59
|
+
onChange?: (items: TreeNode[]) => void;
|
|
60
|
+
onCheckedChange?: (items: TreeItem[]) => void;
|
|
61
|
+
onTitleChange?: (node: TreeNode, title: string) => void;
|
|
62
|
+
onDelete?: (node: TreeNode) => void;
|
|
63
|
+
height?: number;
|
|
64
|
+
className?: string;
|
|
65
|
+
/** CSS color used as the primary color in both themes. */
|
|
66
|
+
defaultColor?: string;
|
|
67
|
+
/** Visual theme for the complete tree view. Defaults to light. */
|
|
68
|
+
theme?: TreeViewTheme;
|
|
69
|
+
direction?: TreeViewDirection;
|
|
70
|
+
icons?: TreeViewIcons;
|
|
71
|
+
labels?: Partial<TreeViewLabels>;
|
|
72
|
+
dropIndicatorColors?: Partial<TreeViewDropIndicatorColors>;
|
|
73
|
+
loading?: boolean;
|
|
74
|
+
emptyMessage?: ReactNode;
|
|
75
|
+
disabled?: boolean;
|
|
76
|
+
};
|
|
77
|
+
export declare function TreeView({ items, defaultItems, type, defaultChecked, checkedItems, onChange, onCheckedChange, onTitleChange, onDelete, height, className, defaultColor, theme, direction, icons, labels, dropIndicatorColors, loading, emptyMessage, disabled, }: TreeViewProps): import("react").JSX.Element;
|
|
78
|
+
export default TreeView;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const r=require("react/jsx-runtime"),d=require("react"),Ne=typeof window>"u"?d.useEffect:d.useLayoutEffect,He={addCategory:"Add category",addChild:"Add child to {title}",delete:"Delete {title}",expand:"Expand category",collapse:"Collapse category",select:"Select {title}",edit:"Edit {title}",dragHandle:"Drag to reorder",loading:"Loading categories",empty:"No categories found",dragAndDropGuide:"Drag and drop guide",before:"Before",inside:"Inside",after:"After",defaultCategoryTitle:"New category"},$e={before:"#3b82f6",inside:"#f59e0b",after:"#10b981"};function K(e,s){return s===void 0?e:e.replace(/\{title\}/g,s)}const Be=180,ye={duration:Be,easing:"cubic-bezier(0.2, 0.7, 0.2, 1)",fill:"forwards"};function pe(e){const s=Number.parseFloat(e);return Number.isFinite(s)?s:0}function be(e){const s=e.style.height,i=e.style.overflow,o=e.firstElementChild;e.style.height="auto",e.style.overflow="visible",e.getBoundingClientRect();const u=getComputedStyle(e),l=o?getComputedStyle(o):void 0,h=pe(u.paddingBlockStart)+pe(u.paddingBlockEnd),$=o instanceof HTMLElement?o.getBoundingClientRect().height:0,B=l?pe(l.marginBlockStart)+pe(l.marginBlockEnd):0,z=e.getBoundingClientRect().height,O=Math.max(e.scrollHeight,z,$+B,h);return e.style.height=s,e.style.overflow=i,Math.ceil(O)}function Ae(){const[e,s]=d.useState(!1);return d.useEffect(()=>{if(typeof window>"u"||typeof window.matchMedia!="function")return;const i=window.matchMedia("(prefers-reduced-motion: reduce)"),o=()=>s(i.matches);return o(),i.addEventListener?i.addEventListener("change",o):i.addListener(o),()=>{i.removeEventListener?i.removeEventListener("change",o):i.removeListener(o)}},[]),e}function F({children:e,size:s=16,className:i,label:o}){return r.jsx("svg",{"aria-hidden":o?void 0:!0,"aria-label":o,className:i,fill:"none",height:s,role:o?"img":void 0,viewBox:"0 0 24 24",width:s,xmlns:"http://www.w3.org/2000/svg",children:e})}function T({children:e,className:s}){return r.jsx("span",{className:`treeview-icon${s?` ${s}`:""}`,children:e})}function Se({direction:e,...s}){const i=e==="down"?"m6 9 6 6 6-6":e==="left"?"m14 6-6 6 6 6":"m10 6 6 6-6 6";return r.jsx(F,{...s,children:r.jsx("path",{d:i,stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2"})})}function Re(e){return r.jsx(F,{...e,children:r.jsx("path",{d:"M12 5v14M5 12h14",stroke:"currentColor",strokeLinecap:"round",strokeWidth:"2"})})}function Pe(e){return r.jsx(F,{...e,children:r.jsx("path",{d:"M5 7h14M10 11v6M14 11v6M9 7V5h6v2m-9 0 1 13h10l1-13",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"1.8"})})}function Te(e){return r.jsx(F,{...e,children:r.jsx("path",{d:"M9 5h.01M15 5h.01M9 12h.01M15 12h.01M9 19h.01M15 19h.01",stroke:"currentColor",strokeLinecap:"round",strokeWidth:"3"})})}function ze(e){return r.jsx(F,{...e,children:r.jsx("path",{d:"m5 12 4 4L19 6",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2"})})}function Ce({open:e,...s}){return r.jsxs(F,{...s,children:[r.jsx("path",{d:"M3.5 7.5h6l1.6 2H20a1 1 0 0 1 1 1v7.2a1.8 1.8 0 0 1-1.8 1.8H4.8A1.8 1.8 0 0 1 3 17.7V8.2a.7.7 0 0 1 .5-.7Z",fill:e?"currentColor":"none",opacity:e?.2:1,stroke:"currentColor",strokeLinejoin:"round",strokeWidth:"1.7"}),e&&r.jsx("path",{d:"M4 10h16.5",stroke:"currentColor",strokeLinecap:"round",strokeWidth:"1.7"})]})}function Oe(e){return r.jsxs(F,{...e,children:[r.jsx("path",{d:"M3.5 7.5h6l1.6 2H20a1 1 0 0 1 1 1v7.2a1.8 1.8 0 0 1-1.8 1.8H4.8A1.8 1.8 0 0 1 3 17.7V8.2a.7.7 0 0 1 .5-.7Z",fill:"currentColor",opacity:".12",stroke:"currentColor",strokeLinejoin:"round",strokeWidth:"1.7"}),r.jsx("path",{d:"M7 14h10",stroke:"currentColor",strokeLinecap:"round",strokeWidth:"1.7"})]})}function Ee(){var e,s;return((s=(e=globalThis.crypto)==null?void 0:e.randomUUID)==null?void 0:s.call(e))??`tree-node-${Date.now()}-${Math.random().toString(36).slice(2)}`}function X(e,s){for(const i of e){if(i.key===s)return i;const o=i.children?X(i.children,s):void 0;if(o)return o}}function me(e,s){var i;return!!((i=e.children)!=null&&i.some(o=>o.key===s||me(o,s)))}function we(e,s,i){return e.map(o=>o.key===s?i(o):o.children?{...o,children:we(o.children,s,i)}:o)}function je(e,s){let i;const o=[];for(const u of e){if(u.key===s){i=u;continue}if(u.children){const[l,h]=je(u.children,s);h&&(i=h),o.push({...u,children:l})}else o.push(u)}return[o,i]}function Me(e,s,i=[]){for(const o of e){if(o.key===s)return i;const u=o.children?Me(o.children,s,[...i,o.key]):[];if(u.length)return u}return[]}function ke(e,s=new Set){return e.forEach(i=>{var o;(o=i.children)!=null&&o.length&&(s.add(i.key),ke(i.children,s))}),s}function De(e,s,i,o){const u=[];for(const l of e){if(l.key===s){o==="before"&&u.push(i,l),o==="inside"&&u.push({...l,children:[...l.children??[],i]}),o==="after"&&u.push(l,i);continue}u.push(l.children?{...l,children:De(l.children,s,i,o)}:l)}return u}function Le({node:e,level:s,mode:i,direction:o,prefersReducedMotion:u,icons:l,labels:h,checkedItems:$,expandedKeys:B,editingKey:z,editValue:O,draggedKey:Y,dragOverKey:ee,dragPosition:G,disabled:j,onToggle:te,onCheck:re,onStartEdit:ne,onEditChange:ie,onCommitEdit:y,onCancelEdit:se,onDragStart:oe,onDragEnd:ce,onAddChild:ae,onDeleteNode:p,onDragOver:D,onDragLeave:le,onDrop:x}){var he,fe,ge;const m=!!((he=e.children)!=null&&he.length),f=m&&B.has(e.key),[k,N]=d.useState(()=>m&&f),[A,L]=d.useState(()=>f?"open":"closing"),b=d.useRef(k),S=d.useRef(!0),q=d.useRef(null),g=d.useRef(null),R=d.useRef(0),U=d.useRef(f),W=d.useRef(!1),I=d.useRef(0),H=z===e.key,de=$.some(t=>t._id===e.key),ve=ee===e.key,J=d.useRef(null);d.useEffect(()=>{var t,C;H&&((t=J.current)==null||t.focus(),(C=J.current)==null||C.select())},[H]),Ne(()=>{var a;const t=q.current;if(S.current){S.current=!1,U.current=f,t&&m&&f&&(t.style.height="",t.style.overflow="",L("open"));return}const C=U.current!==f;if(U.current=f,m&&f&&!b.current){W.current=!0,b.current=!0,N(!0);return}if(!m){(a=g.current)==null||a.cancel(),g.current=null,R.current+=1,W.current=!1,t&&(t.style.height="",t.style.overflow=""),b.current=!1,k&&N(!1);return}if(!t)return;const Q=f&&(C||W.current);if(W.current=!1,!Q&&!C)return;const M=g.current,P=t.getBoundingClientRect().height;M==null||M.cancel(),g.current=null;const _=++R.current,V=()=>{t.style.height="",t.style.overflow=""};if(u||typeof t.animate!="function"){V(),f?L("open"):(b.current=!1,N(!1));return}t.style.overflow="hidden";const n=f&&!M?0:P;if(t.style.height=`${n}px`,t.getBoundingClientRect(),f){const v=be(t);I.current=v,L("opening");const w=t.animate([{height:`${n}px`},{height:`${v}px`}],ye);g.current=w,w.finished.then(()=>{_!==R.current||g.current!==w||(g.current=null,w.cancel(),V(),L("open"))}).catch(()=>{});return}L("closing");const c=t.animate([{height:`${P}px`},{height:"0px"}],ye);g.current=c,c.finished.then(()=>{_!==R.current||g.current!==c||(g.current=null,c.cancel(),V(),b.current=!1,N(!1))}).catch(()=>{})},[k,m,f,u]),Ne(()=>{const t=q.current,C=t==null?void 0:t.firstElementChild;if(!t||!C||typeof ResizeObserver>"u")return;const Q=new ResizeObserver(()=>{if(A!=="opening"||!f)return;const M=g.current;if(!M)return;const P=be(t);if(Math.abs(P-I.current)<1)return;const _=t.getBoundingClientRect().height;M.cancel(),g.current=null;const V=++R.current;t.style.overflow="hidden",t.style.height=`${_}px`,t.getBoundingClientRect(),I.current=P;const n=t.animate([{height:`${_}px`},{height:`${P}px`}],ye);g.current=n,n.finished.then(()=>{V!==R.current||g.current!==n||(g.current=null,n.cancel(),t.style.height="",t.style.overflow="",L("open"))}).catch(()=>{})});return Q.observe(C),()=>Q.disconnect()},[A,k,f]),d.useEffect(()=>()=>{var t;R.current+=1,(t=g.current)==null||t.cancel(),g.current=null},[]);const Z=t=>t.stopPropagation(),ue=k&&A==="open"&&f;return r.jsxs("div",{className:"treeview-node",children:[r.jsxs("div",{className:`treeview-row${Y===e.key?" is-dragging":""}${ve&&G?` is-drag-${G}`:""}`,draggable:i==="editable"&&!j&&!H,onDragStart:t=>oe(t,e.key),onDragEnd:ce,onDragOver:t=>D(t,e.key),onDragLeave:le,onDrop:t=>x(t,e.key),style:{paddingInlineStart:`calc(${s} * var(--treeview-indent) + 0.5rem)`},children:[m?r.jsx("button",{"aria-label":f?h.collapse:h.expand,className:"treeview-expander",disabled:j,onClick:t=>{t.stopPropagation(),te(e.key)},title:f?h.collapse:h.expand,type:"button",children:r.jsx(T,{children:f?l.collapse??r.jsx(Se,{direction:"down",size:14}):l.expand??r.jsx(Se,{direction:o==="rtl"?"left":"right",size:14})})}):r.jsx("span",{className:"treeview-expander-spacer"}),i==="editable"&&r.jsx("span",{"aria-label":h.dragHandle,className:"treeview-drag-handle",title:h.dragHandle,children:r.jsx(T,{children:l.dragHandle??r.jsx(Te,{size:16})})}),i==="selectable"&&r.jsxs("label",{className:"treeview-checkbox-wrap",onClick:Z,children:[r.jsx("input",{"aria-label":K(h.select,e.title),checked:de,className:"treeview-checkbox",disabled:j,onChange:t=>re(e,t.target.checked),type:"checkbox"}),r.jsx("span",{"aria-hidden":"true",className:"treeview-checkmark",children:r.jsx(T,{children:l.check??r.jsx(ze,{size:12})})})]}),r.jsx("span",{"aria-hidden":"true",className:`treeview-folder${f?" is-open":""}`,children:r.jsx(T,{children:f?l.openFolder??r.jsx(Ce,{open:!0,size:18}):l.folder??r.jsx(Ce,{open:!1,size:18})})}),H?r.jsx("input",{"aria-label":K(h.edit,e.title),className:"treeview-title-input",disabled:j,onBlur:y,onChange:t=>ie(t.target.value),onClick:Z,onKeyDown:t=>{t.key==="Enter"&&y(),t.key==="Escape"&&se()},ref:J,value:O}):r.jsx("button",{"aria-label":i==="editable"?K(h.edit,e.title):e.title,className:`treeview-title${i==="editable"?" is-editable":""}`,disabled:j,onClick:t=>{t.stopPropagation(),i==="editable"&&ne(e)},type:"button",children:e.title}),i==="editable"&&r.jsxs("div",{className:"treeview-actions",children:[r.jsx("button",{"aria-label":K(h.addChild,e.title),className:"treeview-action treeview-action-add",disabled:j,onClick:t=>{t.stopPropagation(),ae(e)},onMouseDown:Z,title:K(h.addChild,e.title),type:"button",children:r.jsx(T,{children:l.add??r.jsx(Re,{size:13})})}),r.jsx("button",{"aria-label":K(h.delete,e.title),className:"treeview-action treeview-action-delete",disabled:j,onClick:t=>{t.stopPropagation(),p(e)},onMouseDown:Z,title:K(h.delete,e.title),type:"button",children:r.jsx(T,{children:l.delete??r.jsx(Pe,{size:13})})})]}),m&&r.jsx("span",{className:"treeview-child-count",children:(fe=e.children)==null?void 0:fe.length})]}),m&&k&&r.jsx("div",{"aria-hidden":ue?void 0:!0,className:"treeview-children",ref:t=>{q.current=t,t==null||t.toggleAttribute("inert",!ue)},children:r.jsx("div",{className:"treeview-children-content",children:(ge=e.children)==null?void 0:ge.map(t=>r.jsx(Le,{node:t,level:s+1,mode:i,direction:o,prefersReducedMotion:u,icons:l,labels:h,checkedItems:$,expandedKeys:B,editingKey:z,editValue:O,draggedKey:Y,dragOverKey:ee,dragPosition:G,disabled:j,onToggle:te,onCheck:re,onStartEdit:ne,onEditChange:ie,onCommitEdit:y,onCancelEdit:se,onDragStart:oe,onDragEnd:ce,onAddChild:ae,onDeleteNode:p,onDragOver:D,onDragLeave:le,onDrop:x},t.key))})})]})}function Ie({items:e,defaultItems:s=[],type:i="editable",defaultChecked:o=[],checkedItems:u,onChange:l,onCheckedChange:h,onTitleChange:$,onDelete:B,height:z,className:O,defaultColor:Y,theme:ee="light",direction:G="ltr",icons:j={},labels:te={},dropIndicatorColors:re={},loading:ne=!1,emptyMessage:ie,disabled:y=!1}){const[se,oe]=d.useState(s),[ce,ae]=d.useState(o),p=e??se,D=u??ce,le=Ae(),x={...He,...te},m={...$e,...re},[f,k]=d.useState(()=>ke(p)),[N,A]=d.useState(null),[L,b]=d.useState(""),[S,q]=d.useState(null),[g,R]=d.useState(null),[U,W]=d.useState(null),I=n=>{e===void 0&&oe(n),de(n),l==null||l(n)},H=n=>{u===void 0&&ae(n),h==null||h(n)},de=n=>{const c=ke(n);k(a=>{let v=!1;const w=new Set;return a.forEach(E=>{c.has(E)?w.add(E):v=!0}),v?w:a})};d.useEffect(()=>{de(p)},[p]);const ve=n=>k(c=>{const a=new Set(c);return a.has(n)?a.delete(n):a.add(n),a}),J=n=>{if(y)return;const c={key:Ee(),title:x.defaultCategoryTitle,children:[]};I(we(p,n.key,a=>({...a,children:[...a.children??[],c]}))),k(a=>new Set(a).add(n.key))},Z=()=>{y||I([...p,{key:Ee(),title:x.defaultCategoryTitle,children:[]}])},ue=n=>{!y&&i==="editable"&&(A(n.key),b(n.title))},he=()=>{if(!N)return;const n=X(p,N);if(!n)return;const c=L.trim()||n.title;if(c!==n.title){const a={...n,title:c};I(we(p,N,v=>({...v,title:c}))),H(D.map(v=>v._id===N?{...v,title:c}:v)),$==null||$(a,c)}A(null),b("")},fe=n=>{if(y)return;const[c]=je(p,n.key);I(c),H(D.filter(a=>a._id!==n.key)),B==null||B(n),N===n.key&&(A(null),b(""))},ge=(n,c)=>{const a=Me(p,n.key);if(c){const v=new Set(D.map(E=>E._id)),w=[...a,n.key].filter(E=>!v.has(E)).map(E=>{const xe=X(p,E);return{_id:E,title:(xe==null?void 0:xe.title)??""}});H([...D,...w])}else{const v=new Set([...a,n.key]);H(D.filter(w=>!v.has(w._id)))}},t=()=>{R(null),W(null)},C=()=>{q(null),t()},Q=(n,c)=>{y||i!=="editable"||(q(c),n.dataTransfer.effectAllowed="move",n.dataTransfer.setData("text/plain",c),t())},M=n=>{const c=n.currentTarget.getBoundingClientRect(),a=n.clientY-c.top;return a<c.height*.3?"before":a>c.height*.7?"after":"inside"},P=(n,c)=>{if(y||i!=="editable"||!S)return;if(S===c){t();return}const a=X(p,S);if(!a||me(a,c)){t();return}n.preventDefault(),n.stopPropagation(),R(c),W(M(n))},_=(n,c)=>{n.preventDefault(),n.stopPropagation();try{if(y||i!=="editable"||!S||S===c)return;const a=X(p,S),v=U??M(n);if(!a||me(a,c))return;const[w]=je(p,S);I(De(w,c,a,v)),v==="inside"&&k(E=>new Set(E).add(c))}finally{C()}},V={"--treeview-drop-before":m.before,"--treeview-drop-inside":m.inside,"--treeview-drop-after":m.after,...Y===void 0?{}:{"--treeview-primary":Y}};return r.jsxs("div",{className:`treeview-root${O?` ${O}`:""}`,"data-disabled":y||void 0,"data-theme":ee,dir:G,style:V,children:[i==="editable"&&r.jsxs("div",{className:"treeview-toolbar",children:[r.jsxs("button",{"aria-label":x.addCategory,className:"treeview-add-root",disabled:y,onClick:Z,title:x.addCategory,type:"button",children:[r.jsx(T,{children:j.add??r.jsx(Re,{size:16})})," ",x.addCategory]}),r.jsxs("div",{className:"treeview-guide","aria-label":x.dragAndDropGuide,children:[r.jsxs("span",{children:[r.jsx("i",{className:"treeview-guide-dot is-before"}),x.before]}),r.jsxs("span",{children:[r.jsx("i",{className:"treeview-guide-dot is-inside"}),x.inside]}),r.jsxs("span",{children:[r.jsx("i",{className:"treeview-guide-dot is-after"}),x.after]})]})]}),r.jsx("div",{className:"treeview-shell",style:z===void 0?void 0:{maxHeight:z},children:ne?r.jsx("div",{className:"treeview-loading","aria-busy":"true","aria-label":x.loading,children:Array.from({length:6},(n,c)=>r.jsx("div",{className:"treeview-skeleton-row"},c))}):p.length===0?r.jsxs("div",{className:"treeview-empty",children:[r.jsx("span",{className:"treeview-empty-icon",children:r.jsx(T,{children:j.empty??r.jsx(Oe,{size:38})})}),r.jsx("p",{children:ie??x.empty})]}):r.jsx("div",{className:"treeview-list",children:p.map(n=>r.jsx(Le,{node:n,level:0,mode:i,direction:G,prefersReducedMotion:le,icons:j,labels:x,checkedItems:D,expandedKeys:f,editingKey:N,editValue:L,draggedKey:S,dragOverKey:g,dragPosition:U,disabled:y,onToggle:ve,onCheck:ge,onStartEdit:ue,onEditChange:b,onCommitEdit:he,onCancelEdit:()=>{A(null),b("")},onDragStart:Q,onDragEnd:C,onAddChild:J,onDeleteNode:fe,onDragOver:P,onDragLeave:t,onDrop:_},n.key))})})]})}exports.TreeView=Ie;exports.default=Ie;
|
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as y, jsx as n } from "react/jsx-runtime";
|
|
3
|
+
import { useState as D, useEffect as re, useRef as A, useLayoutEffect as Ae } from "react";
|
|
4
|
+
const Ee = typeof window > "u" ? re : Ae, Pe = {
|
|
5
|
+
addCategory: "Add category",
|
|
6
|
+
addChild: "Add child to {title}",
|
|
7
|
+
delete: "Delete {title}",
|
|
8
|
+
expand: "Expand category",
|
|
9
|
+
collapse: "Collapse category",
|
|
10
|
+
select: "Select {title}",
|
|
11
|
+
edit: "Edit {title}",
|
|
12
|
+
dragHandle: "Drag to reorder",
|
|
13
|
+
loading: "Loading categories",
|
|
14
|
+
empty: "No categories found",
|
|
15
|
+
dragAndDropGuide: "Drag and drop guide",
|
|
16
|
+
before: "Before",
|
|
17
|
+
inside: "Inside",
|
|
18
|
+
after: "After",
|
|
19
|
+
defaultCategoryTitle: "New category"
|
|
20
|
+
}, ze = {
|
|
21
|
+
before: "#3b82f6",
|
|
22
|
+
inside: "#f59e0b",
|
|
23
|
+
after: "#10b981"
|
|
24
|
+
};
|
|
25
|
+
function G(e, o) {
|
|
26
|
+
return o === void 0 ? e : e.replace(/\{title\}/g, o);
|
|
27
|
+
}
|
|
28
|
+
const Te = 180, Ne = {
|
|
29
|
+
duration: Te,
|
|
30
|
+
easing: "cubic-bezier(0.2, 0.7, 0.2, 1)",
|
|
31
|
+
fill: "forwards"
|
|
32
|
+
};
|
|
33
|
+
function ye(e) {
|
|
34
|
+
const o = Number.parseFloat(e);
|
|
35
|
+
return Number.isFinite(o) ? o : 0;
|
|
36
|
+
}
|
|
37
|
+
function Se(e) {
|
|
38
|
+
const o = e.style.height, i = e.style.overflow, c = e.firstElementChild;
|
|
39
|
+
e.style.height = "auto", e.style.overflow = "visible", e.getBoundingClientRect();
|
|
40
|
+
const d = getComputedStyle(e), s = c ? getComputedStyle(c) : void 0, u = ye(d.paddingBlockStart) + ye(d.paddingBlockEnd), P = c instanceof HTMLElement ? c.getBoundingClientRect().height : 0, z = s ? ye(s.marginBlockStart) + ye(s.marginBlockEnd) : 0, j = e.getBoundingClientRect().height, V = Math.max(e.scrollHeight, j, P + z, u);
|
|
41
|
+
return e.style.height = o, e.style.overflow = i, Math.ceil(V);
|
|
42
|
+
}
|
|
43
|
+
function We() {
|
|
44
|
+
const [e, o] = D(!1);
|
|
45
|
+
return re(() => {
|
|
46
|
+
if (typeof window > "u" || typeof window.matchMedia != "function") return;
|
|
47
|
+
const i = window.matchMedia("(prefers-reduced-motion: reduce)"), c = () => o(i.matches);
|
|
48
|
+
return c(), i.addEventListener ? i.addEventListener("change", c) : i.addListener(c), () => {
|
|
49
|
+
i.removeEventListener ? i.removeEventListener("change", c) : i.removeListener(c);
|
|
50
|
+
};
|
|
51
|
+
}, []), e;
|
|
52
|
+
}
|
|
53
|
+
function U({ children: e, size: o = 16, className: i, label: c }) {
|
|
54
|
+
return /* @__PURE__ */ n("svg", { "aria-hidden": c ? void 0 : !0, "aria-label": c, className: i, fill: "none", height: o, role: c ? "img" : void 0, viewBox: "0 0 24 24", width: o, xmlns: "http://www.w3.org/2000/svg", children: e });
|
|
55
|
+
}
|
|
56
|
+
function O({ children: e, className: o }) {
|
|
57
|
+
return /* @__PURE__ */ n("span", { className: `treeview-icon${o ? ` ${o}` : ""}`, children: e });
|
|
58
|
+
}
|
|
59
|
+
function De({ direction: e, ...o }) {
|
|
60
|
+
return /* @__PURE__ */ n(U, { ...o, children: /* @__PURE__ */ n("path", { d: e === "down" ? "m6 9 6 6 6-6" : e === "left" ? "m14 6-6 6 6 6" : "m10 6 6 6-6 6", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2" }) });
|
|
61
|
+
}
|
|
62
|
+
function Ie(e) {
|
|
63
|
+
return /* @__PURE__ */ n(U, { ...e, children: /* @__PURE__ */ n("path", { d: "M12 5v14M5 12h14", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "2" }) });
|
|
64
|
+
}
|
|
65
|
+
function Oe(e) {
|
|
66
|
+
return /* @__PURE__ */ n(U, { ...e, children: /* @__PURE__ */ n("path", { d: "M5 7h14M10 11v6M14 11v6M9 7V5h6v2m-9 0 1 13h10l1-13", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "1.8" }) });
|
|
67
|
+
}
|
|
68
|
+
function je(e) {
|
|
69
|
+
return /* @__PURE__ */ n(U, { ...e, children: /* @__PURE__ */ n("path", { d: "M9 5h.01M15 5h.01M9 12h.01M15 12h.01M9 19h.01M15 19h.01", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "3" }) });
|
|
70
|
+
}
|
|
71
|
+
function Ve(e) {
|
|
72
|
+
return /* @__PURE__ */ n(U, { ...e, children: /* @__PURE__ */ n("path", { d: "m5 12 4 4L19 6", stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2" }) });
|
|
73
|
+
}
|
|
74
|
+
function Le({ open: e, ...o }) {
|
|
75
|
+
return /* @__PURE__ */ y(U, { ...o, children: [
|
|
76
|
+
/* @__PURE__ */ n("path", { d: "M3.5 7.5h6l1.6 2H20a1 1 0 0 1 1 1v7.2a1.8 1.8 0 0 1-1.8 1.8H4.8A1.8 1.8 0 0 1 3 17.7V8.2a.7.7 0 0 1 .5-.7Z", fill: e ? "currentColor" : "none", opacity: e ? 0.2 : 1, stroke: "currentColor", strokeLinejoin: "round", strokeWidth: "1.7" }),
|
|
77
|
+
e && /* @__PURE__ */ n("path", { d: "M4 10h16.5", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.7" })
|
|
78
|
+
] });
|
|
79
|
+
}
|
|
80
|
+
function _e(e) {
|
|
81
|
+
return /* @__PURE__ */ y(U, { ...e, children: [
|
|
82
|
+
/* @__PURE__ */ n("path", { d: "M3.5 7.5h6l1.6 2H20a1 1 0 0 1 1 1v7.2a1.8 1.8 0 0 1-1.8 1.8H4.8A1.8 1.8 0 0 1 3 17.7V8.2a.7.7 0 0 1 .5-.7Z", fill: "currentColor", opacity: ".12", stroke: "currentColor", strokeLinejoin: "round", strokeWidth: "1.7" }),
|
|
83
|
+
/* @__PURE__ */ n("path", { d: "M7 14h10", stroke: "currentColor", strokeLinecap: "round", strokeWidth: "1.7" })
|
|
84
|
+
] });
|
|
85
|
+
}
|
|
86
|
+
function Re() {
|
|
87
|
+
var e, o;
|
|
88
|
+
return ((o = (e = globalThis.crypto) == null ? void 0 : e.randomUUID) == null ? void 0 : o.call(e)) ?? `tree-node-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
89
|
+
}
|
|
90
|
+
function te(e, o) {
|
|
91
|
+
for (const i of e) {
|
|
92
|
+
if (i.key === o) return i;
|
|
93
|
+
const c = i.children ? te(i.children, o) : void 0;
|
|
94
|
+
if (c) return c;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function be(e, o) {
|
|
98
|
+
var i;
|
|
99
|
+
return !!((i = e.children) != null && i.some((c) => c.key === o || be(c, o)));
|
|
100
|
+
}
|
|
101
|
+
function Ce(e, o, i) {
|
|
102
|
+
return e.map((c) => c.key === o ? i(c) : c.children ? { ...c, children: Ce(c.children, o, i) } : c);
|
|
103
|
+
}
|
|
104
|
+
function xe(e, o) {
|
|
105
|
+
let i;
|
|
106
|
+
const c = [];
|
|
107
|
+
for (const d of e) {
|
|
108
|
+
if (d.key === o) {
|
|
109
|
+
i = d;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (d.children) {
|
|
113
|
+
const [s, u] = xe(d.children, o);
|
|
114
|
+
u && (i = u), c.push({ ...d, children: s });
|
|
115
|
+
} else
|
|
116
|
+
c.push(d);
|
|
117
|
+
}
|
|
118
|
+
return [c, i];
|
|
119
|
+
}
|
|
120
|
+
function He(e, o, i = []) {
|
|
121
|
+
for (const c of e) {
|
|
122
|
+
if (c.key === o) return i;
|
|
123
|
+
const d = c.children ? He(c.children, o, [...i, c.key]) : [];
|
|
124
|
+
if (d.length) return d;
|
|
125
|
+
}
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
function Me(e, o = /* @__PURE__ */ new Set()) {
|
|
129
|
+
return e.forEach((i) => {
|
|
130
|
+
var c;
|
|
131
|
+
(c = i.children) != null && c.length && (o.add(i.key), Me(i.children, o));
|
|
132
|
+
}), o;
|
|
133
|
+
}
|
|
134
|
+
function $e(e, o, i, c) {
|
|
135
|
+
const d = [];
|
|
136
|
+
for (const s of e) {
|
|
137
|
+
if (s.key === o) {
|
|
138
|
+
c === "before" && d.push(i, s), c === "inside" && d.push({ ...s, children: [...s.children ?? [], i] }), c === "after" && d.push(s, i);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
d.push(s.children ? { ...s, children: $e(s.children, o, i, c) } : s);
|
|
142
|
+
}
|
|
143
|
+
return d;
|
|
144
|
+
}
|
|
145
|
+
function Be({
|
|
146
|
+
node: e,
|
|
147
|
+
level: o,
|
|
148
|
+
mode: i,
|
|
149
|
+
direction: c,
|
|
150
|
+
prefersReducedMotion: d,
|
|
151
|
+
icons: s,
|
|
152
|
+
labels: u,
|
|
153
|
+
checkedItems: P,
|
|
154
|
+
expandedKeys: z,
|
|
155
|
+
editingKey: j,
|
|
156
|
+
editValue: V,
|
|
157
|
+
draggedKey: X,
|
|
158
|
+
dragOverKey: ne,
|
|
159
|
+
dragPosition: Z,
|
|
160
|
+
disabled: N,
|
|
161
|
+
onToggle: ie,
|
|
162
|
+
onCheck: oe,
|
|
163
|
+
onStartEdit: ce,
|
|
164
|
+
onEditChange: ae,
|
|
165
|
+
onCommitEdit: m,
|
|
166
|
+
onCancelEdit: le,
|
|
167
|
+
onDragStart: se,
|
|
168
|
+
onDragEnd: de,
|
|
169
|
+
onAddChild: ue,
|
|
170
|
+
onDeleteNode: p,
|
|
171
|
+
onDragOver: I,
|
|
172
|
+
onDragLeave: he,
|
|
173
|
+
onDrop: v
|
|
174
|
+
}) {
|
|
175
|
+
var ge, ve, me;
|
|
176
|
+
const w = !!((ge = e.children) != null && ge.length), h = w && z.has(e.key), [b, C] = D(() => w && h), [T, H] = D(() => h ? "open" : "closing"), x = A(b), M = A(!0), Q = A(null), f = A(null), L = A(0), Y = A(h), _ = A(!1), $ = A(0), B = j === e.key, fe = P.some((t) => t._id === e.key), we = ne === e.key, ee = A(null);
|
|
177
|
+
re(() => {
|
|
178
|
+
var t, E;
|
|
179
|
+
B && ((t = ee.current) == null || t.focus(), (E = ee.current) == null || E.select());
|
|
180
|
+
}, [B]), Ee(() => {
|
|
181
|
+
var l;
|
|
182
|
+
const t = Q.current;
|
|
183
|
+
if (M.current) {
|
|
184
|
+
M.current = !1, Y.current = h, t && w && h && (t.style.height = "", t.style.overflow = "", H("open"));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const E = Y.current !== h;
|
|
188
|
+
if (Y.current = h, w && h && !x.current) {
|
|
189
|
+
_.current = !0, x.current = !0, C(!0);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (!w) {
|
|
193
|
+
(l = f.current) == null || l.cancel(), f.current = null, L.current += 1, _.current = !1, t && (t.style.height = "", t.style.overflow = ""), x.current = !1, b && C(!1);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!t) return;
|
|
197
|
+
const J = h && (E || _.current);
|
|
198
|
+
if (_.current = !1, !J && !E) return;
|
|
199
|
+
const R = f.current, W = t.getBoundingClientRect().height;
|
|
200
|
+
R == null || R.cancel(), f.current = null;
|
|
201
|
+
const K = ++L.current, F = () => {
|
|
202
|
+
t.style.height = "", t.style.overflow = "";
|
|
203
|
+
};
|
|
204
|
+
if (d || typeof t.animate != "function") {
|
|
205
|
+
F(), h ? H("open") : (x.current = !1, C(!1));
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
t.style.overflow = "hidden";
|
|
209
|
+
const r = h && !R ? 0 : W;
|
|
210
|
+
if (t.style.height = `${r}px`, t.getBoundingClientRect(), h) {
|
|
211
|
+
const g = Se(t);
|
|
212
|
+
$.current = g, H("opening");
|
|
213
|
+
const k = t.animate(
|
|
214
|
+
[{ height: `${r}px` }, { height: `${g}px` }],
|
|
215
|
+
Ne
|
|
216
|
+
);
|
|
217
|
+
f.current = k, k.finished.then(() => {
|
|
218
|
+
K !== L.current || f.current !== k || (f.current = null, k.cancel(), F(), H("open"));
|
|
219
|
+
}).catch(() => {
|
|
220
|
+
});
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
H("closing");
|
|
224
|
+
const a = t.animate(
|
|
225
|
+
[{ height: `${W}px` }, { height: "0px" }],
|
|
226
|
+
Ne
|
|
227
|
+
);
|
|
228
|
+
f.current = a, a.finished.then(() => {
|
|
229
|
+
K !== L.current || f.current !== a || (f.current = null, a.cancel(), F(), x.current = !1, C(!1));
|
|
230
|
+
}).catch(() => {
|
|
231
|
+
});
|
|
232
|
+
}, [b, w, h, d]), Ee(() => {
|
|
233
|
+
const t = Q.current, E = t == null ? void 0 : t.firstElementChild;
|
|
234
|
+
if (!t || !E || typeof ResizeObserver > "u") return;
|
|
235
|
+
const J = new ResizeObserver(() => {
|
|
236
|
+
if (T !== "opening" || !h) return;
|
|
237
|
+
const R = f.current;
|
|
238
|
+
if (!R) return;
|
|
239
|
+
const W = Se(t);
|
|
240
|
+
if (Math.abs(W - $.current) < 1) return;
|
|
241
|
+
const K = t.getBoundingClientRect().height;
|
|
242
|
+
R.cancel(), f.current = null;
|
|
243
|
+
const F = ++L.current;
|
|
244
|
+
t.style.overflow = "hidden", t.style.height = `${K}px`, t.getBoundingClientRect(), $.current = W;
|
|
245
|
+
const r = t.animate(
|
|
246
|
+
[{ height: `${K}px` }, { height: `${W}px` }],
|
|
247
|
+
Ne
|
|
248
|
+
);
|
|
249
|
+
f.current = r, r.finished.then(() => {
|
|
250
|
+
F !== L.current || f.current !== r || (f.current = null, r.cancel(), t.style.height = "", t.style.overflow = "", H("open"));
|
|
251
|
+
}).catch(() => {
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
return J.observe(E), () => J.disconnect();
|
|
255
|
+
}, [T, b, h]), re(() => () => {
|
|
256
|
+
var t;
|
|
257
|
+
L.current += 1, (t = f.current) == null || t.cancel(), f.current = null;
|
|
258
|
+
}, []);
|
|
259
|
+
const q = (t) => t.stopPropagation(), pe = b && T === "open" && h;
|
|
260
|
+
return /* @__PURE__ */ y(
|
|
261
|
+
"div",
|
|
262
|
+
{
|
|
263
|
+
className: "treeview-node",
|
|
264
|
+
children: [
|
|
265
|
+
/* @__PURE__ */ y(
|
|
266
|
+
"div",
|
|
267
|
+
{
|
|
268
|
+
className: `treeview-row${X === e.key ? " is-dragging" : ""}${we && Z ? ` is-drag-${Z}` : ""}`,
|
|
269
|
+
draggable: i === "editable" && !N && !B,
|
|
270
|
+
onDragStart: (t) => se(t, e.key),
|
|
271
|
+
onDragEnd: de,
|
|
272
|
+
onDragOver: (t) => I(t, e.key),
|
|
273
|
+
onDragLeave: he,
|
|
274
|
+
onDrop: (t) => v(t, e.key),
|
|
275
|
+
style: { paddingInlineStart: `calc(${o} * var(--treeview-indent) + 0.5rem)` },
|
|
276
|
+
children: [
|
|
277
|
+
w ? /* @__PURE__ */ n("button", { "aria-label": h ? u.collapse : u.expand, className: "treeview-expander", disabled: N, onClick: (t) => {
|
|
278
|
+
t.stopPropagation(), ie(e.key);
|
|
279
|
+
}, title: h ? u.collapse : u.expand, type: "button", children: /* @__PURE__ */ n(O, { children: h ? s.collapse ?? /* @__PURE__ */ n(De, { direction: "down", size: 14 }) : s.expand ?? /* @__PURE__ */ n(De, { direction: c === "rtl" ? "left" : "right", size: 14 }) }) }) : /* @__PURE__ */ n("span", { className: "treeview-expander-spacer" }),
|
|
280
|
+
i === "editable" && /* @__PURE__ */ n("span", { "aria-label": u.dragHandle, className: "treeview-drag-handle", title: u.dragHandle, children: /* @__PURE__ */ n(O, { children: s.dragHandle ?? /* @__PURE__ */ n(je, { size: 16 }) }) }),
|
|
281
|
+
i === "selectable" && /* @__PURE__ */ y("label", { className: "treeview-checkbox-wrap", onClick: q, children: [
|
|
282
|
+
/* @__PURE__ */ n("input", { "aria-label": G(u.select, e.title), checked: fe, className: "treeview-checkbox", disabled: N, onChange: (t) => oe(e, t.target.checked), type: "checkbox" }),
|
|
283
|
+
/* @__PURE__ */ n("span", { "aria-hidden": "true", className: "treeview-checkmark", children: /* @__PURE__ */ n(O, { children: s.check ?? /* @__PURE__ */ n(Ve, { size: 12 }) }) })
|
|
284
|
+
] }),
|
|
285
|
+
/* @__PURE__ */ n("span", { "aria-hidden": "true", className: `treeview-folder${h ? " is-open" : ""}`, children: /* @__PURE__ */ n(O, { children: h ? s.openFolder ?? /* @__PURE__ */ n(Le, { open: !0, size: 18 }) : s.folder ?? /* @__PURE__ */ n(Le, { open: !1, size: 18 }) }) }),
|
|
286
|
+
B ? /* @__PURE__ */ n(
|
|
287
|
+
"input",
|
|
288
|
+
{
|
|
289
|
+
"aria-label": G(u.edit, e.title),
|
|
290
|
+
className: "treeview-title-input",
|
|
291
|
+
disabled: N,
|
|
292
|
+
onBlur: m,
|
|
293
|
+
onChange: (t) => ae(t.target.value),
|
|
294
|
+
onClick: q,
|
|
295
|
+
onKeyDown: (t) => {
|
|
296
|
+
t.key === "Enter" && m(), t.key === "Escape" && le();
|
|
297
|
+
},
|
|
298
|
+
ref: ee,
|
|
299
|
+
value: V
|
|
300
|
+
}
|
|
301
|
+
) : /* @__PURE__ */ n("button", { "aria-label": i === "editable" ? G(u.edit, e.title) : e.title, className: `treeview-title${i === "editable" ? " is-editable" : ""}`, disabled: N, onClick: (t) => {
|
|
302
|
+
t.stopPropagation(), i === "editable" && ce(e);
|
|
303
|
+
}, type: "button", children: e.title }),
|
|
304
|
+
i === "editable" && /* @__PURE__ */ y("div", { className: "treeview-actions", children: [
|
|
305
|
+
/* @__PURE__ */ n("button", { "aria-label": G(u.addChild, e.title), className: "treeview-action treeview-action-add", disabled: N, onClick: (t) => {
|
|
306
|
+
t.stopPropagation(), ue(e);
|
|
307
|
+
}, onMouseDown: q, title: G(u.addChild, e.title), type: "button", children: /* @__PURE__ */ n(O, { children: s.add ?? /* @__PURE__ */ n(Ie, { size: 13 }) }) }),
|
|
308
|
+
/* @__PURE__ */ n("button", { "aria-label": G(u.delete, e.title), className: "treeview-action treeview-action-delete", disabled: N, onClick: (t) => {
|
|
309
|
+
t.stopPropagation(), p(e);
|
|
310
|
+
}, onMouseDown: q, title: G(u.delete, e.title), type: "button", children: /* @__PURE__ */ n(O, { children: s.delete ?? /* @__PURE__ */ n(Oe, { size: 13 }) }) })
|
|
311
|
+
] }),
|
|
312
|
+
w && /* @__PURE__ */ n("span", { className: "treeview-child-count", children: (ve = e.children) == null ? void 0 : ve.length })
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
),
|
|
316
|
+
w && b && /* @__PURE__ */ n(
|
|
317
|
+
"div",
|
|
318
|
+
{
|
|
319
|
+
"aria-hidden": pe ? void 0 : !0,
|
|
320
|
+
className: "treeview-children",
|
|
321
|
+
ref: (t) => {
|
|
322
|
+
Q.current = t, t == null || t.toggleAttribute("inert", !pe);
|
|
323
|
+
},
|
|
324
|
+
children: /* @__PURE__ */ n("div", { className: "treeview-children-content", children: (me = e.children) == null ? void 0 : me.map((t) => /* @__PURE__ */ n(
|
|
325
|
+
Be,
|
|
326
|
+
{
|
|
327
|
+
node: t,
|
|
328
|
+
level: o + 1,
|
|
329
|
+
mode: i,
|
|
330
|
+
direction: c,
|
|
331
|
+
prefersReducedMotion: d,
|
|
332
|
+
icons: s,
|
|
333
|
+
labels: u,
|
|
334
|
+
checkedItems: P,
|
|
335
|
+
expandedKeys: z,
|
|
336
|
+
editingKey: j,
|
|
337
|
+
editValue: V,
|
|
338
|
+
draggedKey: X,
|
|
339
|
+
dragOverKey: ne,
|
|
340
|
+
dragPosition: Z,
|
|
341
|
+
disabled: N,
|
|
342
|
+
onToggle: ie,
|
|
343
|
+
onCheck: oe,
|
|
344
|
+
onStartEdit: ce,
|
|
345
|
+
onEditChange: ae,
|
|
346
|
+
onCommitEdit: m,
|
|
347
|
+
onCancelEdit: le,
|
|
348
|
+
onDragStart: se,
|
|
349
|
+
onDragEnd: de,
|
|
350
|
+
onAddChild: ue,
|
|
351
|
+
onDeleteNode: p,
|
|
352
|
+
onDragOver: I,
|
|
353
|
+
onDragLeave: he,
|
|
354
|
+
onDrop: v
|
|
355
|
+
},
|
|
356
|
+
t.key
|
|
357
|
+
)) })
|
|
358
|
+
}
|
|
359
|
+
)
|
|
360
|
+
]
|
|
361
|
+
}
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
function Ge({
|
|
365
|
+
items: e,
|
|
366
|
+
defaultItems: o = [],
|
|
367
|
+
type: i = "editable",
|
|
368
|
+
defaultChecked: c = [],
|
|
369
|
+
checkedItems: d,
|
|
370
|
+
onChange: s,
|
|
371
|
+
onCheckedChange: u,
|
|
372
|
+
onTitleChange: P,
|
|
373
|
+
onDelete: z,
|
|
374
|
+
height: j,
|
|
375
|
+
className: V,
|
|
376
|
+
defaultColor: X,
|
|
377
|
+
theme: ne = "light",
|
|
378
|
+
direction: Z = "ltr",
|
|
379
|
+
icons: N = {},
|
|
380
|
+
labels: ie = {},
|
|
381
|
+
dropIndicatorColors: oe = {},
|
|
382
|
+
loading: ce = !1,
|
|
383
|
+
emptyMessage: ae,
|
|
384
|
+
disabled: m = !1
|
|
385
|
+
}) {
|
|
386
|
+
const [le, se] = D(o), [de, ue] = D(c), p = e ?? le, I = d ?? de, he = We(), v = { ...Pe, ...ie }, w = { ...ze, ...oe }, [h, b] = D(() => Me(p)), [C, T] = D(null), [H, x] = D(""), [M, Q] = D(null), [f, L] = D(null), [Y, _] = D(null), $ = (r) => {
|
|
387
|
+
e === void 0 && se(r), fe(r), s == null || s(r);
|
|
388
|
+
}, B = (r) => {
|
|
389
|
+
d === void 0 && ue(r), u == null || u(r);
|
|
390
|
+
}, fe = (r) => {
|
|
391
|
+
const a = Me(r);
|
|
392
|
+
b((l) => {
|
|
393
|
+
let g = !1;
|
|
394
|
+
const k = /* @__PURE__ */ new Set();
|
|
395
|
+
return l.forEach((S) => {
|
|
396
|
+
a.has(S) ? k.add(S) : g = !0;
|
|
397
|
+
}), g ? k : l;
|
|
398
|
+
});
|
|
399
|
+
};
|
|
400
|
+
re(() => {
|
|
401
|
+
fe(p);
|
|
402
|
+
}, [p]);
|
|
403
|
+
const we = (r) => b((a) => {
|
|
404
|
+
const l = new Set(a);
|
|
405
|
+
return l.has(r) ? l.delete(r) : l.add(r), l;
|
|
406
|
+
}), ee = (r) => {
|
|
407
|
+
if (m) return;
|
|
408
|
+
const a = { key: Re(), title: v.defaultCategoryTitle, children: [] };
|
|
409
|
+
$(Ce(p, r.key, (l) => ({ ...l, children: [...l.children ?? [], a] }))), b((l) => new Set(l).add(r.key));
|
|
410
|
+
}, q = () => {
|
|
411
|
+
m || $([...p, { key: Re(), title: v.defaultCategoryTitle, children: [] }]);
|
|
412
|
+
}, pe = (r) => {
|
|
413
|
+
!m && i === "editable" && (T(r.key), x(r.title));
|
|
414
|
+
}, ge = () => {
|
|
415
|
+
if (!C) return;
|
|
416
|
+
const r = te(p, C);
|
|
417
|
+
if (!r) return;
|
|
418
|
+
const a = H.trim() || r.title;
|
|
419
|
+
if (a !== r.title) {
|
|
420
|
+
const l = { ...r, title: a };
|
|
421
|
+
$(Ce(p, C, (g) => ({ ...g, title: a }))), B(I.map((g) => g._id === C ? { ...g, title: a } : g)), P == null || P(l, a);
|
|
422
|
+
}
|
|
423
|
+
T(null), x("");
|
|
424
|
+
}, ve = (r) => {
|
|
425
|
+
if (m) return;
|
|
426
|
+
const [a] = xe(p, r.key);
|
|
427
|
+
$(a), B(I.filter((l) => l._id !== r.key)), z == null || z(r), C === r.key && (T(null), x(""));
|
|
428
|
+
}, me = (r, a) => {
|
|
429
|
+
const l = He(p, r.key);
|
|
430
|
+
if (a) {
|
|
431
|
+
const g = new Set(I.map((S) => S._id)), k = [...l, r.key].filter((S) => !g.has(S)).map((S) => {
|
|
432
|
+
const ke = te(p, S);
|
|
433
|
+
return { _id: S, title: (ke == null ? void 0 : ke.title) ?? "" };
|
|
434
|
+
});
|
|
435
|
+
B([...I, ...k]);
|
|
436
|
+
} else {
|
|
437
|
+
const g = /* @__PURE__ */ new Set([...l, r.key]);
|
|
438
|
+
B(I.filter((k) => !g.has(k._id)));
|
|
439
|
+
}
|
|
440
|
+
}, t = () => {
|
|
441
|
+
L(null), _(null);
|
|
442
|
+
}, E = () => {
|
|
443
|
+
Q(null), t();
|
|
444
|
+
}, J = (r, a) => {
|
|
445
|
+
m || i !== "editable" || (Q(a), r.dataTransfer.effectAllowed = "move", r.dataTransfer.setData("text/plain", a), t());
|
|
446
|
+
}, R = (r) => {
|
|
447
|
+
const a = r.currentTarget.getBoundingClientRect(), l = r.clientY - a.top;
|
|
448
|
+
return l < a.height * 0.3 ? "before" : l > a.height * 0.7 ? "after" : "inside";
|
|
449
|
+
}, W = (r, a) => {
|
|
450
|
+
if (m || i !== "editable" || !M) return;
|
|
451
|
+
if (M === a) {
|
|
452
|
+
t();
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
const l = te(p, M);
|
|
456
|
+
if (!l || be(l, a)) {
|
|
457
|
+
t();
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
r.preventDefault(), r.stopPropagation(), L(a), _(R(r));
|
|
461
|
+
}, K = (r, a) => {
|
|
462
|
+
r.preventDefault(), r.stopPropagation();
|
|
463
|
+
try {
|
|
464
|
+
if (m || i !== "editable" || !M || M === a) return;
|
|
465
|
+
const l = te(p, M), g = Y ?? R(r);
|
|
466
|
+
if (!l || be(l, a)) return;
|
|
467
|
+
const [k] = xe(p, M);
|
|
468
|
+
$($e(k, a, l, g)), g === "inside" && b((S) => new Set(S).add(a));
|
|
469
|
+
} finally {
|
|
470
|
+
E();
|
|
471
|
+
}
|
|
472
|
+
}, F = {
|
|
473
|
+
"--treeview-drop-before": w.before,
|
|
474
|
+
"--treeview-drop-inside": w.inside,
|
|
475
|
+
"--treeview-drop-after": w.after,
|
|
476
|
+
...X === void 0 ? {} : { "--treeview-primary": X }
|
|
477
|
+
};
|
|
478
|
+
return /* @__PURE__ */ y("div", { className: `treeview-root${V ? ` ${V}` : ""}`, "data-disabled": m || void 0, "data-theme": ne, dir: Z, style: F, children: [
|
|
479
|
+
i === "editable" && /* @__PURE__ */ y("div", { className: "treeview-toolbar", children: [
|
|
480
|
+
/* @__PURE__ */ y("button", { "aria-label": v.addCategory, className: "treeview-add-root", disabled: m, onClick: q, title: v.addCategory, type: "button", children: [
|
|
481
|
+
/* @__PURE__ */ n(O, { children: N.add ?? /* @__PURE__ */ n(Ie, { size: 16 }) }),
|
|
482
|
+
" ",
|
|
483
|
+
v.addCategory
|
|
484
|
+
] }),
|
|
485
|
+
/* @__PURE__ */ y("div", { className: "treeview-guide", "aria-label": v.dragAndDropGuide, children: [
|
|
486
|
+
/* @__PURE__ */ y("span", { children: [
|
|
487
|
+
/* @__PURE__ */ n("i", { className: "treeview-guide-dot is-before" }),
|
|
488
|
+
v.before
|
|
489
|
+
] }),
|
|
490
|
+
/* @__PURE__ */ y("span", { children: [
|
|
491
|
+
/* @__PURE__ */ n("i", { className: "treeview-guide-dot is-inside" }),
|
|
492
|
+
v.inside
|
|
493
|
+
] }),
|
|
494
|
+
/* @__PURE__ */ y("span", { children: [
|
|
495
|
+
/* @__PURE__ */ n("i", { className: "treeview-guide-dot is-after" }),
|
|
496
|
+
v.after
|
|
497
|
+
] })
|
|
498
|
+
] })
|
|
499
|
+
] }),
|
|
500
|
+
/* @__PURE__ */ n("div", { className: "treeview-shell", style: j === void 0 ? void 0 : { maxHeight: j }, children: ce ? /* @__PURE__ */ n("div", { className: "treeview-loading", "aria-busy": "true", "aria-label": v.loading, children: Array.from({ length: 6 }, (r, a) => /* @__PURE__ */ n("div", { className: "treeview-skeleton-row" }, a)) }) : p.length === 0 ? /* @__PURE__ */ y("div", { className: "treeview-empty", children: [
|
|
501
|
+
/* @__PURE__ */ n("span", { className: "treeview-empty-icon", children: /* @__PURE__ */ n(O, { children: N.empty ?? /* @__PURE__ */ n(_e, { size: 38 }) }) }),
|
|
502
|
+
/* @__PURE__ */ n("p", { children: ae ?? v.empty })
|
|
503
|
+
] }) : /* @__PURE__ */ n("div", { className: "treeview-list", children: p.map((r) => /* @__PURE__ */ n(
|
|
504
|
+
Be,
|
|
505
|
+
{
|
|
506
|
+
node: r,
|
|
507
|
+
level: 0,
|
|
508
|
+
mode: i,
|
|
509
|
+
direction: Z,
|
|
510
|
+
prefersReducedMotion: he,
|
|
511
|
+
icons: N,
|
|
512
|
+
labels: v,
|
|
513
|
+
checkedItems: I,
|
|
514
|
+
expandedKeys: h,
|
|
515
|
+
editingKey: C,
|
|
516
|
+
editValue: H,
|
|
517
|
+
draggedKey: M,
|
|
518
|
+
dragOverKey: f,
|
|
519
|
+
dragPosition: Y,
|
|
520
|
+
disabled: m,
|
|
521
|
+
onToggle: we,
|
|
522
|
+
onCheck: me,
|
|
523
|
+
onStartEdit: pe,
|
|
524
|
+
onEditChange: x,
|
|
525
|
+
onCommitEdit: ge,
|
|
526
|
+
onCancelEdit: () => {
|
|
527
|
+
T(null), x("");
|
|
528
|
+
},
|
|
529
|
+
onDragStart: J,
|
|
530
|
+
onDragEnd: E,
|
|
531
|
+
onAddChild: ee,
|
|
532
|
+
onDeleteNode: ve,
|
|
533
|
+
onDragOver: W,
|
|
534
|
+
onDragLeave: t,
|
|
535
|
+
onDrop: K
|
|
536
|
+
},
|
|
537
|
+
r.key
|
|
538
|
+
)) }) })
|
|
539
|
+
] });
|
|
540
|
+
}
|
|
541
|
+
export {
|
|
542
|
+
Ge as TreeView,
|
|
543
|
+
Ge as default
|
|
544
|
+
};
|
package/dist/styles.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.treeview-root{--treeview-border: #e5e7eb;--treeview-surface: #ffffff;--treeview-surface-muted: #f8fafc;--treeview-text: #172033;--treeview-muted: #718096;--treeview-primary: #171717;--treeview-primary-soft: color-mix(in srgb, var(--treeview-primary) 10%, transparent);--treeview-danger: #dc2626;--treeview-shadow: rgba(15, 23, 42, .08);--treeview-drop-before: #3b82f6;--treeview-drop-inside: #f59e0b;--treeview-drop-after: #10b981;--treeview-toolbar-background: var(--treeview-surface);--treeview-shell-background: color-mix(in srgb, var(--treeview-surface) 92%, transparent);--treeview-checkmark: #ffffff;--treeview-indent: 32px;box-sizing:border-box;width:100%;color:var(--treeview-text);color-scheme:light;font-family:inherit;font-size:.875rem;transition:color .16s ease}.treeview-root[data-theme=dark]{--treeview-border: rgba(255, 255, 255, .11);--treeview-surface: #111111;--treeview-surface-muted: rgba(255, 255, 255, .045);--treeview-text: #f0f2f7;--treeview-muted: #9ba3b5;--treeview-primary: #ffffff;--treeview-primary-soft: color-mix(in srgb, var(--treeview-primary) 14%, transparent);--treeview-shadow: rgba(0, 0, 0, .2);--treeview-toolbar-background: #000000;--treeview-shell-background: #111111;--treeview-checkmark: #171717;color-scheme:dark}.treeview-root *,.treeview-root *:before,.treeview-root *:after{box-sizing:border-box}.treeview-icon{display:inline-flex;flex:0 0 auto;align-items:center;justify-content:center;line-height:0}.treeview-icon>svg{display:block;max-width:100%;max-height:100%}.treeview-expander .treeview-icon,.treeview-drag-handle .treeview-icon{width:1rem;height:1rem}.treeview-folder .treeview-icon{width:1.125rem;height:1.125rem}.treeview-checkmark .treeview-icon{width:.75rem;height:.75rem}.treeview-action .treeview-icon{width:.8125rem;height:.8125rem}.treeview-add-root .treeview-icon{width:1rem;height:1rem}.treeview-empty-icon .treeview-icon{width:2.375rem;height:2.375rem}.treeview-toolbar{display:flex;min-height:62px;align-items:center;justify-content:space-between;gap:1rem;padding:.75rem 1rem;border:1px solid var(--treeview-border);border-bottom:0;border-radius:1rem 1rem 0 0;background:var(--treeview-toolbar-background);transition:background-color .16s ease,border-color .16s ease,color .16s ease}.treeview-add-root,.treeview-action,.treeview-expander,.treeview-title{border:0;font:inherit}.treeview-add-root{display:inline-flex;align-items:center;gap:.5rem;min-height:2.25rem;padding:.5rem .85rem;border:1px solid color-mix(in srgb,var(--treeview-primary) 22%,transparent);border-radius:.75rem;color:var(--treeview-primary);background:color-mix(in srgb,var(--treeview-primary) 10%,transparent);cursor:pointer;font-size:.8125rem;font-weight:700;transition:background-color .16s ease,border-color .16s ease,color .16s ease,transform .16s ease}.treeview-add-root:hover:not(:disabled){background:color-mix(in srgb,var(--treeview-primary) 16%,transparent);transform:translateY(-1px)}.treeview-add-root:disabled,.treeview-action:disabled,.treeview-expander:disabled,.treeview-title:disabled{cursor:not-allowed;opacity:.55}.treeview-guide{display:flex;flex-wrap:wrap;align-items:center;gap:.75rem;color:var(--treeview-muted);font-size:.6875rem}.treeview-guide span{display:inline-flex;align-items:center;gap:.35rem;white-space:nowrap}.treeview-guide-dot{width:.45rem;height:.45rem;border-radius:999px;box-shadow:0 0 7px currentColor}.treeview-guide-dot.is-before{color:var(--treeview-drop-before);background:currentColor}.treeview-guide-dot.is-inside{color:var(--treeview-drop-inside);background:currentColor}.treeview-guide-dot.is-after{color:var(--treeview-drop-after);background:currentColor}.treeview-shell{width:100%;overflow:auto;border:1px solid var(--treeview-border);border-radius:1rem;background:var(--treeview-shell-background);box-shadow:0 12px 35px var(--treeview-shadow);transition:background-color .16s ease,border-color .16s ease,box-shadow .16s ease}.treeview-toolbar+.treeview-shell{border-radius:0 0 1rem 1rem}.treeview-list{display:flex;min-height:4rem;flex-direction:column;gap:.25rem;padding:.5rem}.treeview-node{min-width:0}.treeview-row{position:relative;z-index:1;display:flex;min-height:2.75rem;align-items:center;gap:.45rem;margin:0;padding:.35rem .5rem;border:1px solid transparent;border-radius:.75rem;cursor:default;transition:background-color .16s ease,border-color .16s ease,color .16s ease,opacity .16s ease}.treeview-row:hover{border-color:var(--treeview-border);background:var(--treeview-surface-muted)}.treeview-row.is-dragging{opacity:.3}.treeview-row.is-drag-before{border-color:color-mix(in srgb,var(--treeview-drop-before) 32%,transparent);background:color-mix(in srgb,var(--treeview-drop-before) 9%,transparent)}.treeview-row.is-drag-before:before,.treeview-row.is-drag-after:after{position:absolute;inset-inline:.5rem;height:3px;border-radius:99px;content:"";box-shadow:0 0 12px currentColor}.treeview-row.is-drag-before:before{top:-1px;color:var(--treeview-drop-before);background:currentColor}.treeview-row.is-drag-after{border-color:color-mix(in srgb,var(--treeview-drop-after) 32%,transparent);background:color-mix(in srgb,var(--treeview-drop-after) 9%,transparent)}.treeview-row.is-drag-after:after{bottom:-1px;color:var(--treeview-drop-after);background:currentColor}.treeview-row.is-drag-inside{border-color:color-mix(in srgb,var(--treeview-drop-inside) 42%,transparent);background:color-mix(in srgb,var(--treeview-drop-inside) 11%,transparent);box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--treeview-drop-inside) 16%,transparent)}.treeview-expander,.treeview-expander-spacer{display:inline-flex;width:1.75rem;height:1.75rem;flex:0 0 auto;align-items:center;justify-content:center;border-radius:.55rem}.treeview-expander{color:var(--treeview-muted);background:transparent;cursor:pointer;transition:background-color .16s ease,color .16s ease}.treeview-expander:hover:not(:disabled){color:var(--treeview-primary);background:var(--treeview-primary-soft)}.treeview-drag-handle{display:inline-flex;width:1.5rem;height:1.5rem;flex:0 0 auto;align-items:center;justify-content:center;color:var(--treeview-muted);cursor:grab;opacity:.45;transition:opacity .16s ease,color .16s ease}.treeview-row:hover .treeview-drag-handle{color:var(--treeview-primary);opacity:1}.treeview-drag-handle:active{cursor:grabbing}.treeview-folder{display:inline-flex;flex:0 0 auto;color:#e5a93d}.treeview-folder.is-open{color:var(--treeview-primary)}.treeview-checkbox-wrap{position:relative;display:inline-flex;flex:0 0 auto;align-items:center;cursor:pointer}.treeview-checkbox{width:1rem;height:1rem;margin:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:1px solid var(--treeview-border);border-radius:.35rem;background:var(--treeview-surface);cursor:pointer;transition:background-color .16s ease,border-color .16s ease}.treeview-checkbox:checked{border-color:var(--treeview-primary);background:var(--treeview-primary)}.treeview-checkmark{position:absolute;top:0;right:0;bottom:0;left:0;display:grid;place-items:center;color:var(--treeview-checkmark);pointer-events:none;transform:scale(0);transition:transform .16s ease}.treeview-checkbox:checked+.treeview-checkmark{transform:scale(1)}.treeview-title{min-width:0;flex:1;overflow:hidden;padding:.3rem 0;color:var(--treeview-text);background:transparent;text-align:start;text-overflow:ellipsis;white-space:nowrap;font-size:.875rem;font-weight:600;line-height:1.5rem;transition:color .16s ease,background-color .16s ease}.treeview-title.is-editable{cursor:text}.treeview-title.is-editable:hover{color:var(--treeview-primary)}.treeview-title-input{min-width:5rem;flex:1;height:2rem;padding:.25rem .5rem;border:1px solid var(--treeview-primary);border-radius:.45rem;outline:2px solid color-mix(in srgb,var(--treeview-primary) 18%,transparent);color:var(--treeview-text);background:var(--treeview-surface);font:inherit}.treeview-actions{display:flex;flex:0 0 auto;align-items:center;gap:.25rem}.treeview-action{display:inline-flex;width:1.5rem;height:1.5rem;align-items:center;justify-content:center;border:1px solid transparent;border-radius:.5rem;cursor:pointer;opacity:0;transition:opacity .16s ease,background-color .16s ease,transform .16s ease}.treeview-row:hover .treeview-action,.treeview-action:focus-visible{opacity:1}.treeview-action:hover:not(:disabled){transform:scale(1.06)}.treeview-action-add{border-color:color-mix(in srgb,var(--treeview-primary) 20%,transparent);color:var(--treeview-primary);background:var(--treeview-primary-soft)}.treeview-action-delete{border-color:#dc262633;color:var(--treeview-danger);background:#dc262614}.treeview-child-count{flex:0 0 auto;padding:.1rem .5rem;border-radius:999px;color:var(--treeview-muted);background:var(--treeview-surface-muted);font-size:.625rem;font-weight:600}.treeview-children{margin-block-start:.25rem;overflow:visible}.treeview-children-content{display:flex;min-block-size:0;flex-direction:column;gap:.25rem;overflow:visible}.treeview-children[aria-hidden=true]{pointer-events:none;-webkit-user-select:none;user-select:none}.treeview-loading{display:flex;min-height:11rem;flex-direction:column;gap:.5rem;padding:.75rem}.treeview-skeleton-row{height:2.75rem;border-radius:.75rem;background:linear-gradient(90deg,var(--treeview-surface-muted) 20%,var(--treeview-primary-soft) 50%,var(--treeview-surface-muted) 80%);background-size:220% 100%;animation:treeview-shimmer 1.4s ease-in-out infinite}.treeview-empty{display:flex;min-height:11rem;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;padding:1rem;color:var(--treeview-muted);text-align:center}.treeview-empty-icon{color:var(--treeview-muted);opacity:.35}.treeview-empty p{margin:0;font-size:.8125rem}@keyframes treeview-shimmer{0%{background-position:100% 0}to{background-position:-100% 0}}@media(max-width:640px){.treeview-root{--treeview-indent: 26px}.treeview-toolbar{align-items:flex-start;flex-direction:column}.treeview-guide{gap:.55rem}}@media(prefers-reduced-motion:reduce){.treeview-root *{transition:none!important;animation:none!important}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,67 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-tree-node-view",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight, standalone React tree view with selection, inline editing, and drag-and-drop reordering.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/react-tree-node-view.cjs",
|
|
7
|
+
"module": "./dist/react-tree-node-view.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/react-tree-node-view.js",
|
|
13
|
+
"require": "./dist/react-tree-node-view.cjs"
|
|
14
|
+
},
|
|
15
|
+
"./styles.css": "./dist/styles.css",
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"package.json"
|
|
23
|
+
],
|
|
24
|
+
"sideEffects": [
|
|
25
|
+
"**/*.css"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "vite build && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
29
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
30
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
31
|
+
"pack:check": "npm pack --dry-run"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"react",
|
|
35
|
+
"tree-view",
|
|
36
|
+
"treeview",
|
|
37
|
+
"tree-component",
|
|
38
|
+
"nested-list",
|
|
39
|
+
"drag-and-drop",
|
|
40
|
+
"typescript",
|
|
41
|
+
"inline-editing",
|
|
42
|
+
"category-tree"
|
|
43
|
+
],
|
|
44
|
+
"homepage": "https://github.com/hossein-nj/react-tree-node-view#readme",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/hossein-nj/react-tree-node-view.git"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/hossein-nj/react-tree-node-view/issues"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"react": ">=18.2.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/react": "^18.3.12",
|
|
57
|
+
"typescript": "^5.6.3",
|
|
58
|
+
"vite": "^6.0.5"
|
|
59
|
+
},
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
},
|
|
63
|
+
"license": "MIT",
|
|
64
|
+
"engines": {
|
|
65
|
+
"node": ">=18.18.0"
|
|
66
|
+
}
|
|
67
|
+
}
|