@usefui/components 1.5.2 → 1.6.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/index.d.mts +246 -3
  3. package/dist/index.d.ts +246 -3
  4. package/dist/index.js +740 -307
  5. package/dist/index.mjs +708 -288
  6. package/package.json +12 -12
  7. package/src/__tests__/MessageBubble.test.tsx +179 -0
  8. package/src/__tests__/Shimmer.test.tsx +122 -0
  9. package/src/__tests__/Tree.test.tsx +275 -0
  10. package/src/accordion/hooks/index.tsx +3 -1
  11. package/src/badge/index.tsx +2 -3
  12. package/src/checkbox/hooks/index.tsx +5 -1
  13. package/src/collapsible/hooks/index.tsx +3 -1
  14. package/src/dialog/hooks/index.tsx +5 -1
  15. package/src/dropdown/hooks/index.tsx +3 -1
  16. package/src/dropdown/index.tsx +9 -9
  17. package/src/field/hooks/index.tsx +5 -1
  18. package/src/field/styles/index.ts +1 -0
  19. package/src/index.ts +6 -0
  20. package/src/message-bubble/MessageBubble.stories.tsx +91 -0
  21. package/src/message-bubble/hooks/index.tsx +41 -0
  22. package/src/message-bubble/index.tsx +153 -0
  23. package/src/message-bubble/styles/index.ts +61 -0
  24. package/src/otp-field/hooks/index.tsx +3 -1
  25. package/src/otp-field/index.tsx +5 -3
  26. package/src/sheet/hooks/index.tsx +5 -1
  27. package/src/shimmer/Shimmer.stories.tsx +95 -0
  28. package/src/shimmer/index.tsx +64 -0
  29. package/src/shimmer/styles/index.ts +33 -0
  30. package/src/switch/hooks/index.tsx +5 -1
  31. package/src/tabs/hooks/index.tsx +5 -1
  32. package/src/text-area/Textarea.stories.tsx +7 -2
  33. package/src/text-area/index.tsx +30 -14
  34. package/src/text-area/styles/index.ts +32 -72
  35. package/src/toolbar/hooks/index.tsx +5 -1
  36. package/src/tooltip/index.tsx +4 -3
  37. package/src/tree/Tree.stories.tsx +139 -0
  38. package/src/tree/hooks/tree-node-provider.tsx +50 -0
  39. package/src/tree/hooks/tree-provider.tsx +75 -0
  40. package/src/tree/index.tsx +231 -0
  41. package/src/tree/styles/index.ts +23 -0
  42. package/tsconfig.build.json +20 -0
  43. package/tsconfig.json +1 -3
@@ -0,0 +1,231 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+
5
+ import { TreeProvider, useTree } from "./hooks/tree-provider";
6
+ import { TreeNodeProvider, useTreeNode } from "./hooks/tree-node-provider";
7
+
8
+ import { Button, IButtonProperties } from "../button";
9
+ import { TreeView, TreeItem, TreeNodeContent } from "./styles";
10
+
11
+ import {
12
+ IReactChildren,
13
+ IComponentSpacing,
14
+ ComponentVariantEnum,
15
+ } from "../../../../types";
16
+
17
+ export interface ITreeComposition {
18
+ Root: typeof TreeRoot;
19
+ Node: typeof TreeNode;
20
+ Trigger: typeof TreeTrigger;
21
+ Content: typeof TreeContent;
22
+ }
23
+
24
+ export interface ITreeProperties
25
+ extends IComponentSpacing, React.ComponentProps<"ul"> {}
26
+
27
+ export interface ITreeRootProperties extends IReactChildren {
28
+ defaultExpandedIds?: string[];
29
+ onSelectionChange?: (ids: string[]) => void;
30
+ }
31
+
32
+ export interface ITreeNodeProperties
33
+ extends IComponentSpacing, React.ComponentProps<"li"> {
34
+ nodeId: string;
35
+ level?: number;
36
+ isLast?: boolean;
37
+ }
38
+
39
+ export interface ITreeTriggerProperties extends Omit<
40
+ IButtonProperties,
41
+ "value"
42
+ > {
43
+ nodeId: string;
44
+ }
45
+
46
+ export interface ITreeContentProperties
47
+ extends IComponentSpacing, React.ComponentProps<"ul"> {
48
+ nodeId: string;
49
+ defaultOpen?: boolean;
50
+ }
51
+
52
+ /**
53
+ * Tree is used to display a hierarchical list of items.
54
+ *
55
+ * **Best practices:**
56
+ *
57
+ * - Use a clear and descriptive label for each tree node.
58
+ * - Ensure that the tree can be operated using only the keyboard.
59
+ * - Ensure that the focus is properly managed when nodes are expanded/collapsed.
60
+ *
61
+ * @param {ITreeProperties} props - The props for the Tree component.
62
+ * @param {ReactNode} props.children - The content to be rendered inside the tree.
63
+ * @returns {ReactElement} The Tree component.
64
+ */
65
+ const Tree = (props: ITreeProperties) => {
66
+ const { children, ...restProps } = props;
67
+ const { id } = useTree();
68
+
69
+ return (
70
+ <TreeView id={id} role="tree" {...restProps}>
71
+ {children}
72
+ </TreeView>
73
+ );
74
+ };
75
+ Tree.displayName = "Tree";
76
+
77
+ const TreeRoot = ({
78
+ children,
79
+ defaultExpandedIds,
80
+ onSelectionChange,
81
+ }: ITreeRootProperties) => {
82
+ return (
83
+ <TreeProvider
84
+ defaultExpandedIds={defaultExpandedIds}
85
+ onSelectionChange={onSelectionChange}
86
+ >
87
+ {children}
88
+ </TreeProvider>
89
+ );
90
+ };
91
+ TreeRoot.displayName = "Tree.Root";
92
+
93
+ /**
94
+ * Tree.Node is used to wrap each node of the tree.
95
+ *
96
+ * **Best practices:**
97
+ *
98
+ * - Provide a unique nodeId for each node.
99
+ * - Use the level prop to indicate the depth of the node in the hierarchy.
100
+ *
101
+ * @param {ITreeNodeProperties} props - The props for the Tree.Node component.
102
+ * @param {string} props.nodeId - The unique identifier for the node.
103
+ * @param {number} props.level - The depth level of the node. Defaults to 0.
104
+ * @param {boolean} props.isLast - Whether the node is the last in its siblings. Defaults to false.
105
+ * @param {ReactNode} props.children - The content to be rendered inside the node.
106
+ * @returns {ReactElement} The Tree.Node component.
107
+ */
108
+ const TreeNode = (props: ITreeNodeProperties) => {
109
+ const { nodeId, level = 0, isLast = false, children, ...restProps } = props;
110
+
111
+ return (
112
+ <TreeNodeProvider nodeId={nodeId} level={level} isLast={isLast}>
113
+ <TreeItem role="treeitem" aria-level={level + 1} {...restProps}>
114
+ {children}
115
+ </TreeItem>
116
+ </TreeNodeProvider>
117
+ );
118
+ };
119
+ TreeNode.displayName = "Tree.Node";
120
+
121
+ /**
122
+ * Tree.Trigger is used to trigger the expansion and collapse of the associated Tree.Content component.
123
+ *
124
+ * **Best practices:**
125
+ *
126
+ * - Use a clear and descriptive label for the trigger.
127
+ * - Ensure that the trigger can be operated using only the keyboard.
128
+ * - Ensure that the focus is properly managed when the trigger is activated.
129
+ *
130
+ * @param {ITreeTriggerProperties} props - The props for the Tree.Trigger component.
131
+ * @param {string} props.nodeId - The value used to bind the Tree.Trigger and Tree.Content components.
132
+ * @param {ReactNode} props.children - The content to be rendered inside the trigger.
133
+ * @returns {ReactElement} The Tree.Trigger component.
134
+ */
135
+ const TreeTrigger = (props: ITreeTriggerProperties) => {
136
+ const { nodeId, disabled, onClick, children, ...restProps } = props;
137
+
138
+ const { methods } = useTree();
139
+ const { getTreeId, toggleExpanded, toggleSelected } = methods;
140
+
141
+ const isExpanded = methods.isExpanded && methods.isExpanded(nodeId);
142
+ const isSelected = methods.isSelected && methods.isSelected(nodeId);
143
+
144
+ const IdHandler = {
145
+ trigger: getTreeId && getTreeId({ nodeId, type: "trigger" }),
146
+ content: getTreeId && getTreeId({ nodeId, type: "content" }),
147
+ };
148
+
149
+ const { states: nodeStates } = useTreeNode();
150
+ const level = nodeStates.level ?? 0;
151
+
152
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
153
+ if (!disabled) {
154
+ onClick && onClick(event);
155
+ toggleExpanded && toggleExpanded(nodeId);
156
+ toggleSelected && toggleSelected(nodeId);
157
+ }
158
+ };
159
+
160
+ return (
161
+ <Button
162
+ id={String(IdHandler.trigger)}
163
+ disabled={disabled ?? false}
164
+ onClick={handleClick}
165
+ data-state={isExpanded ? "expanded" : "collapsed"}
166
+ data-selected={isSelected || undefined}
167
+ variant={props.variant ?? ComponentVariantEnum.Ghost}
168
+ style={{ paddingLeft: `calc(${level} * 1rem + 0.5rem)` }}
169
+ rawicon
170
+ {...restProps}
171
+ >
172
+ {children}
173
+ </Button>
174
+ );
175
+ };
176
+ TreeTrigger.displayName = "Tree.Trigger";
177
+
178
+ /**
179
+ * Tree.Content is used to contain the children of the associated Tree.Trigger component.
180
+ *
181
+ * **Best practices:**
182
+ *
183
+ * - Ensure that the content is hidden when the associated node is collapsed.
184
+ * - Ensure that the content is properly focused when the associated node is expanded.
185
+ *
186
+ * @param {ITreeContentProperties} props - The props for the Tree.Content component.
187
+ * @param {string} props.nodeId - The value used to bind the Tree.Content and Tree.Trigger components.
188
+ * @param {boolean} props.defaultOpen - The initial open state of the content. Defaults to false.
189
+ * @param {ReactNode} props.children - The content to be rendered inside the node content.
190
+ * @returns {ReactElement} The Tree.Content component.
191
+ */
192
+ const TreeContent = (props: ITreeContentProperties) => {
193
+ const { nodeId, defaultOpen = false, children, ...restProps } = props;
194
+
195
+ const { methods } = useTree();
196
+ const { getTreeId, toggleExpanded } = methods;
197
+
198
+ const isExpanded = methods.isExpanded && methods.isExpanded(nodeId);
199
+
200
+ const IdHandler = {
201
+ trigger: getTreeId && getTreeId({ nodeId, type: "trigger" }),
202
+ content: getTreeId && getTreeId({ nodeId, type: "content" }),
203
+ };
204
+
205
+ React.useEffect(() => {
206
+ if (defaultOpen && !isExpanded && toggleExpanded) toggleExpanded(nodeId);
207
+ }, []);
208
+
209
+ if (isExpanded)
210
+ return (
211
+ <TreeNodeContent
212
+ role="group"
213
+ id={String(IdHandler.content)}
214
+ aria-labelledby={String(IdHandler.trigger)}
215
+ data-nodeId={nodeId}
216
+ {...restProps}
217
+ >
218
+ {children}
219
+ </TreeNodeContent>
220
+ );
221
+
222
+ return <React.Fragment />;
223
+ };
224
+ TreeContent.displayName = "Tree.Content";
225
+
226
+ Tree.Root = TreeRoot;
227
+ Tree.Node = TreeNode;
228
+ Tree.Trigger = TreeTrigger;
229
+ Tree.Content = TreeContent;
230
+
231
+ export { Tree, TreeRoot, TreeNode, TreeTrigger, TreeContent };
@@ -0,0 +1,23 @@
1
+ import styled from "styled-components";
2
+
3
+ export const TreeView = styled.ul<any>`
4
+ display: flex;
5
+ flex-direction: column;
6
+ list-style: none;
7
+ margin: 0;
8
+ padding: 0;
9
+ `;
10
+
11
+ export const TreeItem = styled.li<any>`
12
+ display: flex;
13
+ flex-direction: column;
14
+ list-style: none;
15
+ `;
16
+
17
+ export const TreeNodeContent = styled.ul<any>`
18
+ display: flex;
19
+ flex-direction: column;
20
+ list-style: none;
21
+ margin: 0;
22
+ padding: 0;
23
+ `;
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "noEmit": false,
5
+ "outDir": "./dist",
6
+ "declaration": true,
7
+ "declarationDir": "./dist",
8
+ "declarationMap": true,
9
+ "sourceMap": true
10
+ },
11
+ "exclude": [
12
+ "node_modules",
13
+ "**/*.test.ts",
14
+ "**/*.test.tsx",
15
+ "**/*.spec.ts",
16
+ "**/*.spec.tsx",
17
+ "**/*.stories.ts",
18
+ "**/*.stories.tsx"
19
+ ]
20
+ }
package/tsconfig.json CHANGED
@@ -1,9 +1,7 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "baseUrl": "./src",
4
- "outDir": "./dist",
5
3
  "jsx": "react",
6
- "target": "es2022",
4
+ "target": "ES2022",
7
5
  "module": "CommonJS",
8
6
  "esModuleInterop": true,
9
7
  "forceConsistentCasingInFileNames": true,