@rovula/ui 0.0.47 → 0.0.49

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.
Files changed (45) hide show
  1. package/dist/cjs/bundle.css +32 -4
  2. package/dist/cjs/bundle.js +3 -3
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/Switch/Switch.stories.d.ts +1 -6
  5. package/dist/cjs/types/components/Tree/Tree.d.ts +4 -0
  6. package/dist/cjs/types/components/Tree/Tree.stories.d.ts +12 -0
  7. package/dist/cjs/types/components/Tree/TreeItem.d.ts +4 -0
  8. package/dist/cjs/types/components/Tree/index.d.ts +4 -0
  9. package/dist/cjs/types/components/Tree/type.d.ts +76 -0
  10. package/dist/cjs/types/index.d.ts +1 -0
  11. package/dist/components/Switch/Switch.js +2 -2
  12. package/dist/components/Switch/Switch.stories.js +2 -7
  13. package/dist/components/Tree/Tree.js +104 -0
  14. package/dist/components/Tree/Tree.stories.js +162 -0
  15. package/dist/components/Tree/TreeItem.js +81 -0
  16. package/dist/components/Tree/index.js +4 -0
  17. package/dist/components/Tree/type.js +1 -0
  18. package/dist/esm/bundle.css +32 -4
  19. package/dist/esm/bundle.js +1 -1
  20. package/dist/esm/bundle.js.map +1 -1
  21. package/dist/esm/types/components/Switch/Switch.stories.d.ts +1 -6
  22. package/dist/esm/types/components/Tree/Tree.d.ts +4 -0
  23. package/dist/esm/types/components/Tree/Tree.stories.d.ts +12 -0
  24. package/dist/esm/types/components/Tree/TreeItem.d.ts +4 -0
  25. package/dist/esm/types/components/Tree/index.d.ts +4 -0
  26. package/dist/esm/types/components/Tree/type.d.ts +76 -0
  27. package/dist/esm/types/index.d.ts +1 -0
  28. package/dist/index.d.ts +82 -2
  29. package/dist/index.js +1 -0
  30. package/dist/src/theme/global.css +75 -14
  31. package/dist/theme/themes/SKL/color.css +10 -10
  32. package/dist/theme/themes/xspector/baseline.css +1 -0
  33. package/dist/theme/themes/xspector/components/switch.css +30 -0
  34. package/package.json +1 -1
  35. package/src/components/Switch/Switch.stories.tsx +2 -7
  36. package/src/components/Switch/Switch.tsx +2 -2
  37. package/src/components/Tree/Tree.stories.tsx +288 -0
  38. package/src/components/Tree/Tree.tsx +192 -0
  39. package/src/components/Tree/TreeItem.tsx +231 -0
  40. package/src/components/Tree/index.ts +5 -0
  41. package/src/components/Tree/type.ts +90 -0
  42. package/src/index.ts +1 -0
  43. package/src/theme/themes/SKL/color.css +10 -10
  44. package/src/theme/themes/xspector/baseline.css +1 -0
  45. package/src/theme/themes/xspector/components/switch.css +30 -0
@@ -0,0 +1,288 @@
1
+ import React, { useState } from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import Tree from "./Tree";
4
+ import { ActionButton, Icon } from "@/index";
5
+
6
+ const exampleData = [
7
+ {
8
+ id: "1",
9
+ title: "Parent Folder 1",
10
+ children: [
11
+ {
12
+ id: "1.1",
13
+ title: "Child Folder 1.1",
14
+ children: [
15
+ { id: "1.1.1", title: "Sub Folder 1.1.1" },
16
+ { id: "1.1.2", title: "Sub Folder 1.1.2" },
17
+ ],
18
+ },
19
+ { id: "1.2", title: "Child Folder 1.2" },
20
+ ],
21
+ },
22
+ {
23
+ id: "2",
24
+ title: "Parent Folder 2",
25
+ children: [{ id: "2.1", title: "Child Folder 2.1" }],
26
+ },
27
+ { id: "3", title: "Parent Folder 3" },
28
+ ];
29
+
30
+ const commonProps = {
31
+ defaultExpandedId: ["1", "1.1"],
32
+ defaultCheckedId: ["1.1"],
33
+ defaultExpandAll: true,
34
+ defaultCheckAll: true,
35
+ hierarchicalCheck: true,
36
+ disabled: false,
37
+ showIcon: true,
38
+ };
39
+
40
+ // Storybook metadata
41
+ const meta: Meta<typeof Tree> = {
42
+ title: "Components/Tree",
43
+ component: Tree,
44
+ tags: ["autodocs"],
45
+ parameters: {
46
+ layout: "fullscreen",
47
+ },
48
+ decorators: [
49
+ (Story) => (
50
+ <div className="p-5 flex w-full bg-base-bg">
51
+ <Story />
52
+ </div>
53
+ ),
54
+ ],
55
+ };
56
+
57
+ export default meta;
58
+
59
+ // Default story
60
+ export const Default: StoryObj<typeof Tree> = {
61
+ args: {
62
+ data: exampleData,
63
+ ...commonProps,
64
+ showIcon: true,
65
+ },
66
+ render: (args) => {
67
+ return (
68
+ <div className="flex flex-row gap-4 w-full">
69
+ <Tree {...args} />
70
+ </div>
71
+ );
72
+ },
73
+ };
74
+
75
+ export const onClick: StoryObj<typeof Tree> = {
76
+ args: {
77
+ data: exampleData.map((item) => ({
78
+ ...item,
79
+ onClickItem: (id: string) => alert("Click item " + id),
80
+ })),
81
+ ...commonProps,
82
+ },
83
+ render: (args) => {
84
+ return (
85
+ <div className="flex flex-row gap-4 w-full">
86
+ <Tree {...args} />
87
+ </div>
88
+ );
89
+ },
90
+ };
91
+
92
+ export const CustomIcon: StoryObj<typeof Tree> = {
93
+ args: {
94
+ data: exampleData.map((item, idx) => ({
95
+ ...item,
96
+ ...(idx === 0 && {
97
+ icon: <Icon name="home" />,
98
+ }),
99
+ ...(idx === 1 && {
100
+ renderIcon: ({ expanded, selected }) => (
101
+ <Icon
102
+ name={expanded ? "home" : "home-modern"}
103
+ className={selected ? "fill-info" : "fill-error"}
104
+ />
105
+ ),
106
+ }),
107
+ })),
108
+ ...commonProps,
109
+ },
110
+ render: (args) => {
111
+ return (
112
+ <div className="flex flex-row gap-4 w-full">
113
+ <Tree
114
+ {...args}
115
+ renderIcon={({ expanded, selected }) => (
116
+ <Icon
117
+ name={expanded ? "bell" : "bell-slash"}
118
+ className={selected ? "fill-primary" : "fill-secondary"}
119
+ />
120
+ )}
121
+ />
122
+ </div>
123
+ );
124
+ },
125
+ };
126
+
127
+ export const renderRightSection: StoryObj<typeof Tree> = {
128
+ args: {
129
+ data: exampleData,
130
+ ...commonProps,
131
+ },
132
+ render: (args) => {
133
+ return (
134
+ <div className="flex flex-row gap-4 w-full">
135
+ <Tree
136
+ {...args}
137
+ renderRightSection={() => (
138
+ <ActionButton variant="icon" onClick={() => alert("Say hi!")}>
139
+ <Icon name="ellipsis-vertical" />
140
+ </ActionButton>
141
+ )}
142
+ />
143
+ </div>
144
+ );
145
+ },
146
+ };
147
+
148
+ export const ControlShowExpandButton: StoryObj<typeof Tree> = {
149
+ args: {
150
+ data: exampleData.map((item) => ({
151
+ ...item,
152
+ showExpandButton: true,
153
+ })),
154
+ },
155
+ render: (args) => {
156
+ return (
157
+ <div className="flex flex-row gap-4 w-full">
158
+ <Tree {...args} />
159
+ </div>
160
+ );
161
+ },
162
+ };
163
+
164
+ export const Diabled: StoryObj<typeof Tree> = {
165
+ args: {
166
+ data: exampleData,
167
+ ...commonProps,
168
+ disabled: true,
169
+ },
170
+ render: (args) => {
171
+ return (
172
+ <div className="flex flex-row gap-4 w-full">
173
+ <Tree
174
+ {...args}
175
+ renderRightSection={() => (
176
+ <ActionButton variant="icon" onClick={() => alert("Say hi!")}>
177
+ <Icon name="ellipsis-vertical" />
178
+ </ActionButton>
179
+ )}
180
+ />
181
+ </div>
182
+ );
183
+ },
184
+ };
185
+
186
+ export const DiabledEachItem: StoryObj<typeof Tree> = {
187
+ args: {
188
+ data: exampleData.map((item, i) => ({ ...item, disabled: i === 0 })),
189
+ ...commonProps,
190
+ disabled: undefined,
191
+ },
192
+ render: (args) => {
193
+ return (
194
+ <div className="flex flex-row gap-4 w-full">
195
+ <Tree
196
+ {...args}
197
+ renderRightSection={() => (
198
+ <ActionButton variant="icon" onClick={() => alert("Say hi!")}>
199
+ <Icon name="ellipsis-vertical" />
200
+ </ActionButton>
201
+ )}
202
+ />
203
+ </div>
204
+ );
205
+ },
206
+ };
207
+
208
+ export const ExpandLoadData: StoryObj<typeof Tree> = {
209
+ args: {},
210
+ render: (args) => {
211
+ const [data, setData] = useState([
212
+ {
213
+ id: "1",
214
+ title: "Parent Folder 1",
215
+ showExpandButton: true,
216
+ children: [],
217
+ },
218
+ {
219
+ id: "2",
220
+ title: "Parent Folder 2",
221
+ showExpandButton: true,
222
+ children: [],
223
+ },
224
+ {
225
+ id: "3",
226
+ title: "Parent Folder 3",
227
+ showExpandButton: true,
228
+ children: [],
229
+ },
230
+ ]);
231
+ const [loadingId, setLoadingId] = useState<string[]>([]);
232
+ const [loadedId, setLoadedId] = useState<string[]>([]);
233
+
234
+ const updateNode = (nodes: any[], id: string, newChildren: any[]): any[] =>
235
+ nodes.map((node) => {
236
+ if (node.id === id) {
237
+ return { ...node, children: newChildren };
238
+ }
239
+ if (node.children?.length) {
240
+ return {
241
+ ...node,
242
+ children: updateNode(node.children, id, newChildren),
243
+ };
244
+ }
245
+ return node;
246
+ });
247
+
248
+ const handleOnExpandChange = (id: string, isExpand: boolean) => {
249
+ // *Note can you other way for improve should load if need with other way without loadedId
250
+
251
+ if (isExpand && !loadingId.includes(id) && !loadedId.includes(id)) {
252
+ setLoadingId((prev) => [...prev, id]);
253
+ setTimeout(() => {
254
+ // Mock child data
255
+ const newChildren = [
256
+ {
257
+ id: Date.now() + "1",
258
+ title: `Child of ${id} - 1`,
259
+ children: [],
260
+ showExpandButton: true,
261
+ },
262
+ {
263
+ id: Date.now() + "2",
264
+ title: `Child of ${id} - 2`,
265
+ children: [],
266
+ showExpandButton: true,
267
+ },
268
+ ];
269
+
270
+ setData((prevData) => updateNode(prevData, id, newChildren));
271
+ setLoadingId((prev) => prev.filter((val) => val !== id));
272
+ setLoadedId((prev) => [...prev, id]);
273
+ }, 1500);
274
+ }
275
+ };
276
+
277
+ return (
278
+ <div className="flex flex-row gap-4 w-full">
279
+ <Tree
280
+ {...args}
281
+ data={data}
282
+ loadingId={loadingId}
283
+ onExpandChange={handleOnExpandChange}
284
+ />
285
+ </div>
286
+ );
287
+ },
288
+ };
@@ -0,0 +1,192 @@
1
+ import React, { FC, useCallback, useEffect, useState } from "react";
2
+ import TreeItem from "./TreeItem";
3
+ import { TreeData, TreeProps } from "./type";
4
+
5
+ const Tree: FC<TreeProps> = ({
6
+ classes,
7
+ data,
8
+ defaultExpandedId = [],
9
+ defaultCheckedId = [],
10
+ checkedId,
11
+ loadingId,
12
+ renderIcon,
13
+ renderRightSection,
14
+ renderElement,
15
+ renderTitle,
16
+ onExpandChange,
17
+ onCheckedChange,
18
+ defaultExpandAll = false,
19
+ defaultCheckAll = false,
20
+ hierarchicalCheck = false,
21
+ showIcon = true,
22
+ disabled,
23
+ enableSeparatorLine = true,
24
+ }) => {
25
+ const [checkedState, setCheckedState] = useState<Record<string, boolean>>({});
26
+ const [expandedState, setExpandedState] = useState<Record<string, boolean>>(
27
+ {}
28
+ );
29
+
30
+ const traverseTree = (
31
+ nodes: TreeData[],
32
+ callback: (node: TreeData) => void
33
+ ) => {
34
+ nodes.forEach((node) => {
35
+ callback(node);
36
+ if (node.children) {
37
+ traverseTree(node.children, callback);
38
+ }
39
+ });
40
+ };
41
+
42
+ useEffect(() => {
43
+ if (defaultExpandAll) {
44
+ const allExpanded: Record<string, boolean> = {};
45
+ traverseTree(data, (node) => {
46
+ allExpanded[node.id] = true;
47
+ });
48
+ setExpandedState(allExpanded);
49
+ } else if (defaultExpandedId?.length) {
50
+ const initialExpandedState = defaultExpandedId.reduce((acc, id) => {
51
+ acc[id] = true;
52
+ return acc;
53
+ }, {} as Record<string, boolean>);
54
+ setExpandedState(initialExpandedState);
55
+ }
56
+ }, [data, defaultExpandedId, defaultExpandAll]);
57
+
58
+ useEffect(() => {
59
+ if (defaultCheckAll) {
60
+ const allChecked: Record<string, boolean> = {};
61
+ traverseTree(data, (node) => {
62
+ allChecked[node.id] = true;
63
+ });
64
+ setCheckedState(allChecked);
65
+ } else if (!checkedId && defaultCheckedId?.length) {
66
+ const initialCheckedState = defaultCheckedId.reduce((acc, id) => {
67
+ acc[id] = true;
68
+ return acc;
69
+ }, {} as Record<string, boolean>);
70
+ setCheckedState(initialCheckedState);
71
+ }
72
+ }, [data, defaultCheckedId, checkedId, defaultCheckAll]);
73
+
74
+ const handleExpandChange = useCallback(
75
+ (id: string, expanded: boolean) => {
76
+ onExpandChange?.(id, expanded);
77
+ setExpandedState((prev) => ({ ...prev, [id]: expanded }));
78
+ },
79
+ [onExpandChange]
80
+ );
81
+
82
+ const handleCheckedChange = useCallback(
83
+ (id: string, checked: boolean) => {
84
+ let newState = { ...checkedState, [id]: checked };
85
+
86
+ if (hierarchicalCheck) {
87
+ const updateCheckedState = (
88
+ nodeId: string,
89
+ isChecked: boolean,
90
+ state: Record<string, boolean>
91
+ ) => {
92
+ state[nodeId] = isChecked;
93
+
94
+ // Update children recursively
95
+ const updateChildren = (parentId: string, isChecked: boolean) => {
96
+ traverseTree(data, (node) => {
97
+ if (node.id === parentId && node.children) {
98
+ node.children.forEach((child) => {
99
+ state[child.id] = isChecked;
100
+ updateChildren(child.id, isChecked);
101
+ });
102
+ }
103
+ });
104
+ };
105
+
106
+ // Update parents recursively
107
+ const updateParents = (
108
+ childId: string,
109
+ state: Record<string, boolean>
110
+ ) => {
111
+ traverseTree(data, (node) => {
112
+ if (node.children?.some((child) => child.id === childId)) {
113
+ const allChildrenChecked = node.children.every(
114
+ (child) => state[child.id]
115
+ );
116
+ state[node.id] = allChildrenChecked;
117
+ updateParents(node.id, state);
118
+ }
119
+ });
120
+ };
121
+
122
+ updateChildren(nodeId, isChecked);
123
+ updateParents(nodeId, state);
124
+
125
+ return state;
126
+ };
127
+
128
+ newState = updateCheckedState(id, checked, newState);
129
+ }
130
+
131
+ setCheckedState(newState);
132
+
133
+ if (onCheckedChange) {
134
+ const checkedIds = Object.keys(newState).filter((key) => newState[key]);
135
+ onCheckedChange(checkedIds);
136
+ }
137
+ },
138
+ [checkedState, data, onCheckedChange, hierarchicalCheck]
139
+ );
140
+
141
+ const checkIsExpanded = useCallback(
142
+ (id: string) => !!expandedState[id],
143
+ [expandedState]
144
+ );
145
+
146
+ const checkIsChecked = useCallback(
147
+ (id: string) => {
148
+ if (checkedId) {
149
+ return checkedId.includes(id);
150
+ }
151
+ return !!checkedState[id];
152
+ },
153
+ [checkedId, checkedState]
154
+ );
155
+
156
+ const checkIsLoading = useCallback(
157
+ (id: string) => {
158
+ if (loadingId) {
159
+ return loadingId.includes(id);
160
+ }
161
+ },
162
+ [loadingId]
163
+ );
164
+
165
+ return (
166
+ <div className="w-full">
167
+ {data.map((item, idx) => (
168
+ <TreeItem
169
+ key={item.id}
170
+ classes={classes}
171
+ isFirstLevel
172
+ isLastItem={idx === data.length - 1}
173
+ checkIsExpanded={checkIsExpanded}
174
+ checkIsChecked={checkIsChecked}
175
+ onExpandChange={handleExpandChange}
176
+ onCheckedChange={handleCheckedChange}
177
+ checkIsLoading={checkIsLoading}
178
+ renderIcon={renderIcon}
179
+ renderElement={renderElement}
180
+ renderTitle={renderTitle}
181
+ renderRightSection={renderRightSection}
182
+ enableSeparatorLine={enableSeparatorLine}
183
+ disabled={disabled}
184
+ showIcon={showIcon}
185
+ {...item}
186
+ />
187
+ ))}
188
+ </div>
189
+ );
190
+ };
191
+
192
+ export default Tree;
@@ -0,0 +1,231 @@
1
+ import { ActionButton, Checkbox, Loading } from "@/index";
2
+ import { cn } from "@/utils/cn";
3
+ import React, { FC, ReactNode, useCallback, useEffect, useMemo } from "react";
4
+ import Icon from "../Icon/Icon";
5
+ import { TreeItemProps } from "./type";
6
+
7
+ const TreeItem: FC<TreeItemProps> = ({
8
+ id,
9
+ title,
10
+ classes,
11
+ children,
12
+ isFirstLevel = false,
13
+ disabled,
14
+ icon,
15
+ showIcon,
16
+ showExpandButton,
17
+ enableSeparatorLine = true,
18
+ isLastItem,
19
+ checkIsExpanded,
20
+ checkIsChecked,
21
+ checkIsLoading,
22
+ onExpandChange,
23
+ onCheckedChange,
24
+ onClickItem,
25
+ renderIcon,
26
+ renderElement,
27
+ renderTitle,
28
+ renderRightSection,
29
+ }) => {
30
+ const isLoading = useMemo(() => checkIsLoading?.(id), [checkIsLoading, id]);
31
+ const isChecked = useMemo(() => checkIsChecked(id), [checkIsChecked, id]);
32
+ const isExpanded = useMemo(() => checkIsExpanded(id), [checkIsExpanded, id]);
33
+ const hasChildren = useMemo(() => !!children?.length, [children]);
34
+ const shouldExpandButton = useMemo(
35
+ () => (showExpandButton !== undefined ? showExpandButton : hasChildren),
36
+ [hasChildren, showExpandButton]
37
+ );
38
+
39
+ const handleExpandToggle = useCallback(() => {
40
+ onExpandChange?.(id, !isExpanded);
41
+ }, [id, isExpanded, onExpandChange]);
42
+
43
+ // TODO move to props
44
+ const lineSize = 2;
45
+ const horizontalLineWidth = 4;
46
+ const expandButtonSize = 30;
47
+ const spacing = 2;
48
+
49
+ const styles = {
50
+ branch: {
51
+ height: isLastItem
52
+ ? `calc(50% + ${lineSize}px)`
53
+ : `calc(100% + ${lineSize}px)`,
54
+ width: lineSize,
55
+ marginTop: -lineSize,
56
+ borderBottomLeftRadius: lineSize / 2,
57
+ },
58
+ horizontalLine: {
59
+ height: lineSize,
60
+ width:
61
+ lineSize +
62
+ horizontalLineWidth +
63
+ (shouldExpandButton ? 0 : expandButtonSize + spacing),
64
+ marginLeft: -lineSize + 0.1,
65
+ borderBottomLeftRadius: lineSize / 2,
66
+ },
67
+ expandButton: {
68
+ width: expandButtonSize,
69
+ height: expandButtonSize,
70
+ },
71
+ childPadding: {
72
+ paddingLeft: isFirstLevel
73
+ ? expandButtonSize / 2 - lineSize / 2
74
+ : expandButtonSize / 2 + horizontalLineWidth - lineSize / 2,
75
+ },
76
+ };
77
+
78
+ useEffect(() => {
79
+ if (isExpanded && !isLoading && !hasChildren) {
80
+ handleExpandToggle();
81
+ }
82
+ }, [isLoading, handleExpandToggle]);
83
+
84
+ const handleOnClickItem = useCallback(() => {
85
+ onClickItem?.(id);
86
+ }, [onClickItem, id]);
87
+
88
+ const defaultIcon = (
89
+ <Icon
90
+ name={isExpanded ? "folder-open" : "folder"}
91
+ className="fill-warning"
92
+ />
93
+ );
94
+
95
+ const customIcon =
96
+ icon ??
97
+ renderIcon?.({
98
+ id,
99
+ expanded: isExpanded,
100
+ selected: isChecked,
101
+ });
102
+
103
+ const rightIcon = renderRightSection?.({
104
+ id,
105
+ expanded: isExpanded,
106
+ selected: isChecked,
107
+ });
108
+
109
+ const titleContent = renderTitle
110
+ ? renderTitle({ id, title, expanded: isExpanded, selected: isChecked })
111
+ : title;
112
+
113
+ const elementWrapper = (content: ReactNode) =>
114
+ renderElement
115
+ ? renderElement({
116
+ id,
117
+ expanded: isExpanded,
118
+ selected: isChecked,
119
+ children: content,
120
+ styles,
121
+ onClick: handleOnClickItem,
122
+ })
123
+ : content;
124
+
125
+ return elementWrapper(
126
+ <div className={cn("flex flex-row w-full", classes?.elementWrapper)}>
127
+ <div
128
+ className={cn("bg-grey-150", { "h-1/2": isLastItem }, classes?.branch)}
129
+ style={styles.branch}
130
+ />
131
+ <div className={cn("flex flex-col w-full", classes?.itemWrapper)}>
132
+ <div
133
+ className={cn(
134
+ "flex items-center py-2 min-h-10",
135
+ classes?.itemContainer
136
+ )}
137
+ >
138
+ {!isFirstLevel && (
139
+ <div
140
+ className={cn("bg-grey-150", classes?.horizontalLine)}
141
+ style={styles.horizontalLine}
142
+ />
143
+ )}
144
+ {isFirstLevel && !shouldExpandButton && (
145
+ <div
146
+ className={cn("flex mr-[2px]", classes?.expandButton)}
147
+ style={styles.expandButton}
148
+ />
149
+ )}
150
+ {shouldExpandButton && (
151
+ <div
152
+ className={cn("flex mr-[2px]", classes?.expandButton)}
153
+ style={styles.expandButton}
154
+ onClick={!isLoading && handleExpandToggle}
155
+ >
156
+ <ActionButton variant="icon" size="sm">
157
+ {isLoading ? (
158
+ <Loading />
159
+ ) : (
160
+ <Icon name={isExpanded ? "chevron-down" : "chevron-right"} />
161
+ )}
162
+ </ActionButton>
163
+ </div>
164
+ )}
165
+ <Checkbox
166
+ id={id}
167
+ className={cn("size-[16pt]", classes?.checkbox)}
168
+ checked={isChecked}
169
+ disabled={disabled}
170
+ onCheckedChange={(newChecked) =>
171
+ onCheckedChange?.(id, newChecked as boolean)
172
+ }
173
+ />
174
+ <div
175
+ className={cn(
176
+ "ml-2 gap-1 flex flex-1 items-center text-foreground",
177
+ classes?.item
178
+ )}
179
+ onClick={handleOnClickItem}
180
+ >
181
+ {showIcon ? customIcon || defaultIcon : null}
182
+ <div
183
+ className={cn(
184
+ "flex flex-1 cursor-pointer text-subtitle5 text-ellipsis",
185
+ classes?.title
186
+ )}
187
+ >
188
+ {titleContent}
189
+ </div>
190
+ </div>
191
+ {rightIcon}
192
+ </div>
193
+ {isExpanded && hasChildren && (
194
+ <div
195
+ className={cn("flex flex-col", classes?.childrenWrapper)}
196
+ style={styles.childPadding}
197
+ >
198
+ {children?.map((child, idx) => (
199
+ <TreeItem
200
+ key={child.id}
201
+ classes={classes}
202
+ isLastItem={idx === children.length - 1}
203
+ checkIsExpanded={checkIsExpanded}
204
+ checkIsChecked={checkIsChecked}
205
+ checkIsLoading={checkIsLoading}
206
+ onExpandChange={onExpandChange}
207
+ onCheckedChange={onCheckedChange}
208
+ renderIcon={renderIcon}
209
+ renderElement={renderElement}
210
+ renderTitle={renderTitle}
211
+ disabled={disabled}
212
+ showIcon={showIcon}
213
+ {...child}
214
+ />
215
+ ))}
216
+ </div>
217
+ )}
218
+ {enableSeparatorLine && isFirstLevel && !isLastItem && (
219
+ <div
220
+ className={cn(
221
+ "bg-grey-150 w-full h-[2px] rounded",
222
+ classes?.separatorLine
223
+ )}
224
+ />
225
+ )}
226
+ </div>
227
+ </div>
228
+ );
229
+ };
230
+
231
+ export default TreeItem;
@@ -0,0 +1,5 @@
1
+ import Tree from "./Tree";
2
+ import TreeItem from "./TreeItem";
3
+
4
+ export * from "./type";
5
+ export { Tree, TreeItem };