@clevertask/react-sortable-tree 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 CleverTask
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,183 @@
1
+ # @clevertask/react-sortable-tree
2
+
3
+ A customizable React component for rendering and managing tree structures with drag-and-drop functionality.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Usage](#usage)
9
+ - [Props](#props)
10
+ - [Types](#types)
11
+ - [TreeItem](#treeitem)
12
+ - [TreeItems](#treeitems)
13
+ - [OptimizedTreeStructure](#optimizedtreestructure)
14
+ - [Helper Functions](#helper-functions)
15
+ - [createOptimizedTreeStructure](#createoptimizedtreestructure)
16
+ - [removeItemById](#removeitembyid)
17
+ - [setTreeItemProperties](#settreeitemproperties)
18
+ - [Roadmap](#roadmap)
19
+ - [Release Process](#release-process)
20
+ - [License](#license)
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install @clevertask/react-sortable-tree
26
+ ```
27
+
28
+ ## Usage
29
+
30
+ ```tsx
31
+ import '@clevertask/react-sortable-tree/dist/style.css';
32
+ import { SortableTree, createOptimizedTreeStructure } from '@clevertask/react-sortable-tree';
33
+
34
+ function App() {
35
+ const [treeStructure, setTreeStructure] = useState(createOptimizedTreeStructure([
36
+ { id: '1', label: 'Item 1', children: [] },
37
+ { id: '2', label: 'Item 2', children: [
38
+ { id: '3', label: 'Item 2.1', children: [] }
39
+ ] },
40
+ ]));
41
+
42
+ return (
43
+ <SortableTree
44
+ items={treeStructure.items}
45
+ onItemsChange={setTreeStructure}
46
+ isCollapsible
47
+ isRemovable
48
+ allowNestedItemAddition
49
+ // ... other props
50
+ />
51
+ );
52
+ }
53
+ ```
54
+
55
+ ## Props
56
+
57
+ | Prop | Type | Default | Description |
58
+ |------|------|---------|-------------|
59
+ | `items` | `TreeItems` | Required | The array of tree items to be rendered. |
60
+ | `onItemsChange` | `React.Dispatch<React.SetStateAction<OptimizedTreeStructure>>` | Required | Callback function called when the tree structure changes. |
61
+ | `indentationWidth` | `number` | `undefined` | The indentation width for children elements. |
62
+ | `isCollapsible` | `boolean` | `false` | Determines if tree items can be collapsed/expanded. |
63
+ | `onLazyLoadChildren` | `(id: UniqueIdentifier, isExpanding: boolean) => Promise<void>` | `undefined` | Callback for lazy loading child items when a parent is expanded. Useful for getting child items from an API endpoint |
64
+ | `showDropIndicator` | `boolean` | `false` | Determines if a drop indicator should be shown when dragging items. |
65
+ | `isRemovable` | `boolean` | `false` | Determines if items can be removed from the tree. |
66
+ | `onRemoveItem` | `(id: UniqueIdentifier) => void` | `undefined` | Callback function called when an item is removed from the tree. |
67
+ | `allowNestedItemAddition` | `boolean` | `false` | Determines if new items can be added as children to existing items. |
68
+ | `onAddItem` | `(parentId: UniqueIdentifier \| null) => void` | `undefined` | Callback function called when a new item is added to the tree. |
69
+ | `onDragEnd` | `(result: DropResult) => void` | `undefined` | Callback function called when a drag operation ends. |
70
+ | `onItemClick` | `(id: UniqueIdentifier) => void` | `undefined` | Callback function called when an item in the tree is clicked. |
71
+
72
+ ## Types
73
+
74
+ ### TreeItem
75
+
76
+ ```typescript
77
+ type TreeItem = {
78
+ id: UniqueIdentifier;
79
+ label: string;
80
+ children: TreeItem[];
81
+ collapsed?: boolean;
82
+ canFetchChildren?: boolean;
83
+ disableDragging?: boolean;
84
+ [key: string]: any;
85
+ };
86
+ ```
87
+
88
+ ### TreeItems
89
+
90
+ ```typescript
91
+ type TreeItems = TreeItem[];
92
+ ```
93
+
94
+ ### OptimizedTreeStructure
95
+
96
+ ```typescript
97
+ interface OptimizedTreeStructure {
98
+ items: TreeItems;
99
+ itemMap: Map<UniqueIdentifier, TreeItem>;
100
+ }
101
+ ```
102
+
103
+ The `OptimizedTreeStructure` is used internally to improve performance for large trees. Use the `createOptimizedTreeStructure` function to create this structure from a `TreeItems` array.
104
+
105
+ ## Helper Functions
106
+
107
+ ### createOptimizedTreeStructure
108
+
109
+ ```typescript
110
+ function createOptimizedTreeStructure(items: TreeItems): OptimizedTreeStructure
111
+ ```
112
+
113
+ ### removeItemById
114
+
115
+ ```typescript
116
+ function removeItemById(structure: OptimizedTreeStructure, id: UniqueIdentifier): OptimizedTreeStructure
117
+ ```
118
+
119
+ This function removes an item from the tree structure by its ID. It returns a new `OptimizedTreeStructure` with the item removed from both the `items` array and the `itemMap`. It also handles removing the item from nested children.
120
+
121
+ Usage example:
122
+
123
+ ```typescript
124
+ const updatedStructure = removeItemById(currentStructure, '123');
125
+ setTreeStructure(updatedStructure);
126
+ ```
127
+
128
+ ### setTreeItemProperties
129
+
130
+ ```typescript
131
+ function setTreeItemProperties(
132
+ structure: OptimizedTreeStructure,
133
+ id: UniqueIdentifier,
134
+ setter: (value: TreeItem) => Partial<TreeItem>
135
+ ): OptimizedTreeStructure
136
+ ```
137
+
138
+ This function updates the properties of a specific tree item. It takes a setter function that receives the current item and returns an object with the properties to be updated. It returns a new `OptimizedTreeStructure` with the updated item in both the `items` array and the `itemMap`.
139
+
140
+ Usage example:
141
+
142
+ ```typescript
143
+ setTreeStructure((treeStructure) => {
144
+ return setTreeItemProperties(
145
+ treeStructure,
146
+ '123',
147
+ (item) => ({ label: 'New Label', collapsed: !item.collapsed })
148
+ );
149
+ });
150
+ ```
151
+
152
+ These helper functions are designed to work with the `OptimizedTreeStructure` to ensure efficient updates to the tree. They maintain both the tree hierarchy in the `items` array and the fast lookup `O(1)` capability of the `itemMap`.
153
+
154
+ Use this function to create an `OptimizedTreeStructure` from a `TreeItems` array. This is useful when initializing your state.
155
+
156
+ ## Roadmap
157
+
158
+ We're constantly working to improve @clevertask/react-sortable-tree. Here are some features we're planning to implement:
159
+
160
+ - **Virtualization**: Improve performance for large trees by only rendering visible nodes.
161
+ - **Custom item rendering**: Allow users to provide custom components for rendering tree items.
162
+ - **Selection and Multi-selection**: Add support for selecting one or multiple items in the tree.
163
+ - **Drag multiple items**: Enable dragging and dropping multiple selected items at once.
164
+ - **API Example**: Provide a comprehensive example illustrating real-world usage with a backend API.
165
+ - **E2E tests**: It will ensure this component's working as expected.
166
+
167
+ We're excited about these upcoming features and welcome any feedback or contributions from the community. If you have any suggestions or would like to contribute to any of these features, please open an issue or submit a pull request on our GitHub repository.
168
+
169
+ ## Release Process
170
+
171
+ This package is automatically published to npm when a new release is created on GitHub. To create a new release:
172
+
173
+ 1. Update the version in `package.json` according to semantic versioning rules.
174
+ 2. Commit the version change: `git commit -am "Bump version to x.x.x"`
175
+ 3. Create a new tag: `git tag vx.x.x`
176
+ 4. Push the changes and the tag: `git push && git push --tags`
177
+ 5. Go to the GitHub repository and create a new release, selecting the tag you just created.
178
+
179
+ The GitHub Action will automatically build, test, and publish the new version to npm.
180
+
181
+ ## License
182
+
183
+ MIT
@@ -0,0 +1,4 @@
1
+ import { SortableTreeProps } from './types';
2
+ declare function PrivateSortableTree({ items, onItemsChange: setItems, isCollapsible, onLazyLoadChildren, showDropIndicator, indentationWidth, isRemovable, onRemoveItem, allowNestedItemAddition, onAddItem, onDragEnd, onItemClick, }: SortableTreeProps): import("react/jsx-runtime").JSX.Element;
3
+ export declare const SortableTree: import('react').MemoExoticComponent<typeof PrivateSortableTree>;
4
+ export {};
@@ -0,0 +1,9 @@
1
+ import { default as React, CSSProperties } from 'react';
2
+ export interface Props extends React.HTMLAttributes<HTMLButtonElement> {
3
+ active?: {
4
+ fill: string;
5
+ background: string;
6
+ };
7
+ cursor?: CSSProperties['cursor'];
8
+ }
9
+ export declare const Action: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,2 @@
1
+ export { Action } from './Action';
2
+ export type { Props as ActionProps } from './Action';
@@ -0,0 +1,2 @@
1
+ import { ActionProps } from '../Action';
2
+ export declare function Add(props: ActionProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export { Add } from './Add';
@@ -0,0 +1,2 @@
1
+ import { ActionProps } from '../Action';
2
+ export declare const Handle: import('react').ForwardRefExoticComponent<ActionProps & import('react').RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1 @@
1
+ export { Handle } from './Handle';
@@ -0,0 +1,2 @@
1
+ import { ActionProps } from '../Action';
2
+ export declare function Remove(props: ActionProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ export { Remove } from './Remove';
@@ -0,0 +1,8 @@
1
+ import { UniqueIdentifier } from '@dnd-kit/core';
2
+ import { Props as TreeItemProps } from './TreeItem';
3
+ interface Props extends TreeItemProps {
4
+ id: UniqueIdentifier;
5
+ }
6
+ declare function PrivateSortableTreeItem({ id, depth, ...props }: Props): import("react/jsx-runtime").JSX.Element;
7
+ export declare const SortableTreeItem: import('react').MemoExoticComponent<typeof PrivateSortableTreeItem>;
8
+ export {};
@@ -0,0 +1,22 @@
1
+ import { default as React, HTMLAttributes } from 'react';
2
+ export interface Props extends Omit<HTMLAttributes<HTMLLIElement>, "id"> {
3
+ childCount?: number;
4
+ clone?: boolean;
5
+ collapsed?: boolean;
6
+ depth: number;
7
+ disableInteraction?: boolean;
8
+ disableSelection?: boolean;
9
+ disableDragging?: boolean;
10
+ ghost?: boolean;
11
+ handleProps?: any;
12
+ indicator?: boolean;
13
+ indentationWidth: number;
14
+ value: string;
15
+ onCollapse?(): void;
16
+ onRemove?(): void;
17
+ onAdd?(): void;
18
+ onLabelClick?(): void;
19
+ wrapperRef?(node: HTMLLIElement): void;
20
+ }
21
+ export declare const _TreeItem: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLDivElement>>;
22
+ export declare const TreeItem: React.MemoExoticComponent<React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLDivElement>>>;
@@ -0,0 +1,2 @@
1
+ export { TreeItem } from './TreeItem';
2
+ export { SortableTreeItem } from './SortableTreeItem';
@@ -0,0 +1 @@
1
+ export { TreeItem, SortableTreeItem } from './TreeItem';
@@ -0,0 +1,3 @@
1
+ export * from './types';
2
+ export { SortableTree } from './SortableTree';
3
+ export { setTreeItemProperties, removeItemById, createOptimizedTreeStructure } from './utilities';
@@ -0,0 +1,3 @@
1
+ import { KeyboardCoordinateGetter } from '@dnd-kit/core';
2
+ import { SensorContext } from './types';
3
+ export declare const sortableTreeKeyboardCoordinates: (context: SensorContext, indicator: boolean, indentationWidth: number) => KeyboardCoordinateGetter;
@@ -0,0 +1,138 @@
1
+ import { MutableRefObject } from 'react';
2
+ import { UniqueIdentifier } from '@dnd-kit/core';
3
+ export interface OptimizedTreeStructure {
4
+ items: TreeItems;
5
+ itemMap: Map<UniqueIdentifier, TreeItem>;
6
+ }
7
+ /**
8
+ * Represents an item in the tree structure.
9
+ */
10
+ export type TreeItem = {
11
+ /**
12
+ * Unique identifier for the item. Can be a string or number.
13
+ */
14
+ id: UniqueIdentifier;
15
+ /**
16
+ * The text label displayed for the item in the tree.
17
+ */
18
+ label: string;
19
+ /**
20
+ * An array of child TreeItems. If empty, the item is a leaf node.
21
+ */
22
+ children: TreeItem[];
23
+ /**
24
+ * Determines whether the item's children are initially collapsed.
25
+ * @default false
26
+ */
27
+ collapsed?: boolean;
28
+ /**
29
+ * Indicates whether this item can lazy-load its children.
30
+ * When true, children will be fetched on expansion if the children array is empty.
31
+ * This is useful for optimizing initial render performance with large trees.
32
+ * @default false
33
+ */
34
+ canFetchChildren?: boolean;
35
+ /**
36
+ * When true, prevents this item from being dragged.
37
+ * Useful for creating fixed or header items in the tree.
38
+ * @default false
39
+ */
40
+ disableDragging?: boolean;
41
+ /**
42
+ * Any additional custom properties can be added here.
43
+ */
44
+ [key: string]: any;
45
+ };
46
+ export type TreeItems = TreeItem[];
47
+ export interface FlattenedItem extends TreeItem {
48
+ parentId: UniqueIdentifier | null;
49
+ depth: number;
50
+ index: number;
51
+ }
52
+ export type SensorContext = MutableRefObject<{
53
+ items: FlattenedItem[];
54
+ offset: number;
55
+ }>;
56
+ /**
57
+ * Props for the SortableTree component.
58
+ */
59
+ export interface SortableTreeProps {
60
+ /**
61
+ * A control that lets you add the indentation width for children elements
62
+ */
63
+ indentationWidth?: number;
64
+ /**
65
+ * The array of tree items to be rendered.
66
+ */
67
+ items: TreeItems;
68
+ /**
69
+ * Callback function called when the tree structure changes.
70
+ * @param items - The updated array of tree items.
71
+ */
72
+ onItemsChange: React.Dispatch<React.SetStateAction<OptimizedTreeStructure>>;
73
+ /**
74
+ * Determines if tree items can be collapsed/expanded.
75
+ * @default false
76
+ */
77
+ isCollapsible?: boolean;
78
+ /**
79
+ * Callback function for lazy loading child items when a parent is expanded.
80
+ * It only works if the item has the `canFetchChildren` property
81
+ * @param id - The id of the parent item being expanded.
82
+ * @param isExpanding - True if the item is being expanded, false if collapsing.
83
+ */
84
+ onLazyLoadChildren?: (id: UniqueIdentifier, isExpanding: boolean) => Promise<void>;
85
+ /**
86
+ * Determines if a drop indicator should be shown when dragging items.
87
+ * @default false
88
+ */
89
+ showDropIndicator?: boolean;
90
+ /**
91
+ * Determines if items can be removed from the tree.
92
+ * @default false
93
+ */
94
+ isRemovable?: boolean;
95
+ /**
96
+ * Callback function called when an item is removed from the tree.
97
+ * @param id - The id of the item being removed.
98
+ */
99
+ onRemoveItem?: (id: UniqueIdentifier) => void;
100
+ /**
101
+ * Determines if new items can be added as children to existing items.
102
+ * @default false
103
+ */
104
+ allowNestedItemAddition?: boolean;
105
+ /**
106
+ * Callback function called when a new item is added to the tree.
107
+ * @param parentId - The id of the parent item, or null if adding a root item.
108
+ */
109
+ onAddItem?: (parentId: UniqueIdentifier | null) => void;
110
+ /**
111
+ * Callback function called when a drag operation ends.
112
+ * @param result - An object containing information about the drag operation.
113
+ */
114
+ onDragEnd?: (result: DropResult) => void;
115
+ /**
116
+ * Callback function called when an item in the tree is clicked.
117
+ * @param id - The id of the clicked item.
118
+ */
119
+ onItemClick?: (id: UniqueIdentifier) => void;
120
+ }
121
+ /**
122
+ * Represents the result of a drag operation in the tree.
123
+ */
124
+ export type DropResult = {
125
+ /**
126
+ * The item that was dragged.
127
+ */
128
+ movedItem: TreeItem;
129
+ /**
130
+ * The new parent of the dragged item, or null if it's now a root item.
131
+ */
132
+ parent: UniqueIdentifier | null;
133
+ /**
134
+ * The new index of the dragged item within its parent's children array.
135
+ */
136
+ index: number;
137
+ } | null;
138
+ export type { UniqueIdentifier } from '@dnd-kit/core';
@@ -0,0 +1,18 @@
1
+ import { UniqueIdentifier } from '@dnd-kit/core';
2
+ import { FlattenedItem, OptimizedTreeStructure, TreeItem, TreeItems } from './types';
3
+ export declare const iOS: boolean;
4
+ export declare function getProjection(items: FlattenedItem[], activeId: UniqueIdentifier, overId: UniqueIdentifier, dragOffset: number, indentationWidth: number): {
5
+ depth: number;
6
+ maxDepth: number;
7
+ minDepth: number;
8
+ parentId: UniqueIdentifier | null;
9
+ };
10
+ export declare function flattenTree(items: TreeItems): FlattenedItem[];
11
+ export declare function buildTree(flattenedItems: FlattenedItem[]): TreeItems;
12
+ export declare function findItem(items: TreeItem[], itemId: UniqueIdentifier): TreeItem | undefined;
13
+ export declare function findItemDeep(items: TreeItems, itemId: UniqueIdentifier): TreeItem | undefined;
14
+ export declare function removeItemById(structure: OptimizedTreeStructure, id: UniqueIdentifier): OptimizedTreeStructure;
15
+ export declare function setTreeItemProperties(structure: OptimizedTreeStructure, id: UniqueIdentifier, setter: (value: TreeItem) => Partial<TreeItem>): OptimizedTreeStructure;
16
+ export declare function createOptimizedTreeStructure(items: TreeItems): OptimizedTreeStructure;
17
+ export declare function getChildCount(items: TreeItems, id: UniqueIdentifier): number;
18
+ export declare function removeChildrenOf(items: FlattenedItem[], ids: UniqueIdentifier[]): FlattenedItem[];
@@ -0,0 +1 @@
1
+ export * from './SortableTree';
package/dist/main.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};