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 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
- # Temporary Holding Version
1
+ # react-tree-node-view
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <div align="center">
4
+
5
+ ![React](https://img.shields.io/badge/React-%3E%3D18.2.0-61dafb?style=flat-square&logo=react&logoColor=20232a)
6
+ ![TypeScript](https://img.shields.io/badge/TypeScript-first-3178c6?style=flat-square&logo=typescript&logoColor=white)
7
+ ![NPM Version](https://img.shields.io/npm/v/react-tree-node-view?style=flat-square&logo=npm&color=cb3837)
8
+ ![Monthly Downloads](https://img.shields.io/npm/dm/react-tree-node-view?style=flat-square&logo=npm&color=blue)
9
+ ![Bundle Size](https://img.shields.io/bundlephobia/minzip/react-tree-node-view?style=flat-square&color=10b981)
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;
@@ -0,0 +1,2 @@
1
+ export { default, TreeView } from "./TreeView";
2
+ export type { TreeItem, TreeNode, TreeViewDirection, TreeViewDropIndicatorColors, TreeViewIcons, TreeViewLabels, TreeViewMode, TreeViewProps, TreeViewTheme, } from "./TreeView";
@@ -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
+ };
@@ -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": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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
+ }