@rovula/ui 0.0.49 → 0.0.51

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.
@@ -5,10 +5,14 @@ import { TreeData, TreeProps } from "./type";
5
5
  const Tree: FC<TreeProps> = ({
6
6
  classes,
7
7
  data,
8
- defaultExpandedId = [],
9
- defaultCheckedId = [],
8
+ defaultExpandedId,
9
+ defaultCheckedId,
10
10
  checkedId,
11
11
  loadingId,
12
+ lineSize,
13
+ horizontalLineWidth,
14
+ expandButtonSize,
15
+ spacing,
12
16
  renderIcon,
13
17
  renderRightSection,
14
18
  renderElement,
@@ -40,7 +44,7 @@ const Tree: FC<TreeProps> = ({
40
44
  };
41
45
 
42
46
  useEffect(() => {
43
- if (defaultExpandAll) {
47
+ if (data.length && defaultExpandAll) {
44
48
  const allExpanded: Record<string, boolean> = {};
45
49
  traverseTree(data, (node) => {
46
50
  allExpanded[node.id] = true;
@@ -56,20 +60,23 @@ const Tree: FC<TreeProps> = ({
56
60
  }, [data, defaultExpandedId, defaultExpandAll]);
57
61
 
58
62
  useEffect(() => {
59
- if (defaultCheckAll) {
63
+ if (data.length && defaultCheckAll) {
60
64
  const allChecked: Record<string, boolean> = {};
61
65
  traverseTree(data, (node) => {
62
66
  allChecked[node.id] = true;
63
67
  });
64
68
  setCheckedState(allChecked);
65
- } else if (!checkedId && defaultCheckedId?.length) {
69
+ } else if (
70
+ Object.keys(checkedState)?.length === 0 &&
71
+ defaultCheckedId?.length
72
+ ) {
66
73
  const initialCheckedState = defaultCheckedId.reduce((acc, id) => {
67
74
  acc[id] = true;
68
75
  return acc;
69
76
  }, {} as Record<string, boolean>);
70
77
  setCheckedState(initialCheckedState);
71
78
  }
72
- }, [data, defaultCheckedId, checkedId, defaultCheckAll]);
79
+ }, [data, defaultCheckedId, defaultCheckAll]);
73
80
 
74
81
  const handleExpandChange = useCallback(
75
82
  (id: string, expanded: boolean) => {
@@ -182,6 +189,10 @@ const Tree: FC<TreeProps> = ({
182
189
  enableSeparatorLine={enableSeparatorLine}
183
190
  disabled={disabled}
184
191
  showIcon={showIcon}
192
+ lineSize={lineSize}
193
+ horizontalLineWidth={horizontalLineWidth}
194
+ expandButtonSize={expandButtonSize}
195
+ spacing={spacing}
185
196
  {...item}
186
197
  />
187
198
  ))}
@@ -26,6 +26,10 @@ const TreeItem: FC<TreeItemProps> = ({
26
26
  renderElement,
27
27
  renderTitle,
28
28
  renderRightSection,
29
+ lineSize = 2,
30
+ horizontalLineWidth = 4,
31
+ expandButtonSize = 30,
32
+ spacing = 2,
29
33
  }) => {
30
34
  const isLoading = useMemo(() => checkIsLoading?.(id), [checkIsLoading, id]);
31
35
  const isChecked = useMemo(() => checkIsChecked(id), [checkIsChecked, id]);
@@ -40,40 +44,41 @@ const TreeItem: FC<TreeItemProps> = ({
40
44
  onExpandChange?.(id, !isExpanded);
41
45
  }, [id, isExpanded, onExpandChange]);
42
46
 
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
- };
47
+ const styles = useMemo(
48
+ () => ({
49
+ branch: {
50
+ width: lineSize,
51
+ borderBottomLeftRadius: isLastItem ? lineSize / 2 : 0,
52
+ },
53
+ horizontalLine: {
54
+ height: lineSize,
55
+ width:
56
+ lineSize +
57
+ horizontalLineWidth +
58
+ (shouldExpandButton ? 0 : expandButtonSize + spacing),
59
+ marginLeft: -lineSize,
60
+ borderBottomLeftRadius: lineSize / 2,
61
+ },
62
+ expandButton: {
63
+ width: expandButtonSize,
64
+ height: expandButtonSize,
65
+ },
66
+ childPadding: {
67
+ paddingLeft: isFirstLevel
68
+ ? expandButtonSize / 2 - lineSize / 2
69
+ : expandButtonSize / 2 + horizontalLineWidth - lineSize / 2,
70
+ },
71
+ }),
72
+ [
73
+ lineSize,
74
+ horizontalLineWidth,
75
+ expandButtonSize,
76
+ spacing,
77
+ isFirstLevel,
78
+ isLastItem,
79
+ shouldExpandButton,
80
+ ]
81
+ );
77
82
 
78
83
  useEffect(() => {
79
84
  if (isExpanded && !isLoading && !hasChildren) {
@@ -124,95 +129,147 @@ const TreeItem: FC<TreeItemProps> = ({
124
129
 
125
130
  return elementWrapper(
126
131
  <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
132
  <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"} />
133
+ <div className={cn("flex flex-row flex-1", classes?.rowWrapperClasses)}>
134
+ <div
135
+ className={cn(
136
+ "flex flex-col h-full",
137
+ classes?.columnWrapperClasses
138
+ )}
139
+ >
140
+ {!isFirstLevel && (
141
+ <div
142
+ className={cn(
143
+ "flex w-[2px] h-1/2 bg-grey-150",
144
+ classes?.branch
161
145
  )}
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
- />
146
+ style={styles.branch}
147
+ />
148
+ )}
149
+ {!isFirstLevel &&
150
+ !isLastItem &&
151
+ ((isExpanded && (hasChildren || isLoading)) || !isExpanded) && (
152
+ <div
153
+ className={cn(
154
+ "flex w-[2px] h-1/2 bg-grey-150",
155
+ classes?.branch
156
+ )}
157
+ style={styles.branch}
158
+ />
159
+ )}
160
+ </div>
161
+
174
162
  <div
175
163
  className={cn(
176
- "ml-2 gap-1 flex flex-1 items-center text-foreground",
177
- classes?.item
164
+ "flex flex-1 items-center py-2 min-h-10",
165
+ classes?.itemContainer
178
166
  )}
179
- onClick={handleOnClickItem}
180
167
  >
181
- {showIcon ? customIcon || defaultIcon : null}
168
+ {!isFirstLevel && (
169
+ <div
170
+ className={cn("bg-grey-150", classes?.horizontalLine)}
171
+ style={styles.horizontalLine}
172
+ />
173
+ )}
174
+ {isFirstLevel && !shouldExpandButton && (
175
+ <div
176
+ className={cn("flex mr-[2px]", classes?.expandButton)}
177
+ style={styles.expandButton}
178
+ />
179
+ )}
180
+ {shouldExpandButton && (
181
+ <div
182
+ className={cn("flex mr-[2px]", classes?.expandButton)}
183
+ style={styles.expandButton}
184
+ onClick={!isLoading && handleExpandToggle}
185
+ >
186
+ <ActionButton variant="icon" size="sm">
187
+ {isLoading ? (
188
+ <Loading />
189
+ ) : (
190
+ <Icon
191
+ name={isExpanded ? "chevron-down" : "chevron-right"}
192
+ />
193
+ )}
194
+ </ActionButton>
195
+ </div>
196
+ )}
197
+ <Checkbox
198
+ id={id}
199
+ className={cn("size-[16pt]", classes?.checkbox)}
200
+ checked={isChecked}
201
+ disabled={disabled}
202
+ onCheckedChange={(newChecked) =>
203
+ onCheckedChange?.(id, newChecked as boolean)
204
+ }
205
+ />
182
206
  <div
183
207
  className={cn(
184
- "flex flex-1 cursor-pointer text-subtitle5 text-ellipsis",
185
- classes?.title
208
+ "ml-2 gap-1 flex flex-1 items-center text-foreground",
209
+ classes?.item
186
210
  )}
211
+ onClick={handleOnClickItem}
187
212
  >
188
- {titleContent}
213
+ {showIcon ? customIcon || defaultIcon : null}
214
+ <div
215
+ className={cn(
216
+ "flex flex-1 cursor-pointer text-subtitle5 text-ellipsis",
217
+ classes?.title
218
+ )}
219
+ >
220
+ {titleContent}
221
+ </div>
189
222
  </div>
223
+ {rightIcon}
190
224
  </div>
191
- {rightIcon}
192
225
  </div>
226
+
193
227
  {isExpanded && hasChildren && (
194
228
  <div
195
- className={cn("flex flex-col", classes?.childrenWrapper)}
196
- style={styles.childPadding}
229
+ className={cn(
230
+ "flex flex-row overflow-hidden max-h-screen",
231
+ classes?.expandedChildrenWrapper
232
+ )}
197
233
  >
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}
234
+ {!isFirstLevel && !isLastItem && (
235
+ <div
236
+ className={cn(
237
+ "flex w-[2px] h-full bg-grey-150",
238
+ classes?.branch
239
+ )}
240
+ style={styles.branch}
214
241
  />
215
- ))}
242
+ )}
243
+ <div
244
+ className={cn(
245
+ "flex flex-col overflow-hidden max-h-screen",
246
+ classes?.expandedChildrenWrapperInner
247
+ )}
248
+ style={styles.childPadding}
249
+ >
250
+ {children?.map((child, idx) => (
251
+ <TreeItem
252
+ key={child.id}
253
+ classes={classes}
254
+ isLastItem={idx === children.length - 1}
255
+ checkIsExpanded={checkIsExpanded}
256
+ checkIsChecked={checkIsChecked}
257
+ checkIsLoading={checkIsLoading}
258
+ onExpandChange={onExpandChange}
259
+ onCheckedChange={onCheckedChange}
260
+ renderIcon={renderIcon}
261
+ renderElement={renderElement}
262
+ renderTitle={renderTitle}
263
+ disabled={disabled}
264
+ showIcon={showIcon}
265
+ lineSize={lineSize}
266
+ horizontalLineWidth={horizontalLineWidth}
267
+ expandButtonSize={expandButtonSize}
268
+ spacing={spacing}
269
+ {...child}
270
+ />
271
+ ))}
272
+ </div>
216
273
  </div>
217
274
  )}
218
275
  {enableSeparatorLine && isFirstLevel && !isLastItem && (
@@ -21,6 +21,10 @@ export interface TreeItemProps extends TreeData {
21
21
  showIcon?: boolean;
22
22
  showExpandButton?: boolean;
23
23
  enableSeparatorLine?: boolean;
24
+ lineSize?: number;
25
+ horizontalLineWidth?: number;
26
+ expandButtonSize?: number;
27
+ spacing?: number;
24
28
  checkIsExpanded: (id: string) => boolean;
25
29
  checkIsChecked: (id: string) => boolean;
26
30
  checkIsLoading?: (id: string) => void;
@@ -51,17 +55,20 @@ export interface TreeItemProps extends TreeData {
51
55
  selected: boolean;
52
56
  }) => ReactNode;
53
57
  classes?: Partial<{
54
- elementWrapper: string;
55
- branch: string;
56
- itemWrapper: string;
57
- itemContainer: string;
58
- horizontalLine: string;
59
- expandButton: string;
60
- separatorLine: string;
61
- checkbox: string;
62
- item: string;
63
- title: string;
64
- childrenWrapper: string;
58
+ elementWrapper?: string;
59
+ branch?: string;
60
+ itemWrapper?: string;
61
+ itemContainer?: string;
62
+ horizontalLine?: string;
63
+ expandButton?: string;
64
+ separatorLine?: string;
65
+ checkbox?: string;
66
+ item?: string;
67
+ title?: string;
68
+ expandedChildrenWrapper?: string;
69
+ expandedChildrenWrapperInner?: string;
70
+ rowWrapperClasses?: string;
71
+ columnWrapperClasses?: string;
65
72
  }>;
66
73
  }
67
74
 
@@ -76,6 +83,10 @@ export interface TreeProps
76
83
  | "disabled"
77
84
  | "enableSeparatorLine"
78
85
  | "classes"
86
+ | "lineSize"
87
+ | "horizontalLineWidth"
88
+ | "expandButtonSize"
89
+ | "spacing"
79
90
  > {
80
91
  data: TreeData[];
81
92
  defaultExpandedId?: string[];