@usefui/components 1.5.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +233 -0
  2. package/LICENSE +21 -0
  3. package/README.md +0 -0
  4. package/babel.config.js +12 -0
  5. package/dist/index.d.mts +1299 -0
  6. package/dist/index.d.ts +1299 -0
  7. package/dist/index.js +3701 -0
  8. package/dist/index.mjs +3586 -0
  9. package/package.json +44 -0
  10. package/src/__tests__/Accordion.test.tsx +106 -0
  11. package/src/__tests__/Avatar.test.tsx +89 -0
  12. package/src/__tests__/Badge.test.tsx +58 -0
  13. package/src/__tests__/Button.test.tsx +88 -0
  14. package/src/__tests__/Checkbox.test.tsx +106 -0
  15. package/src/__tests__/Collapsible.test.tsx +79 -0
  16. package/src/__tests__/Dialog.test.tsx +109 -0
  17. package/src/__tests__/Dropdown.test.tsx +159 -0
  18. package/src/__tests__/Field.test.tsx +100 -0
  19. package/src/__tests__/OTPField.test.tsx +199 -0
  20. package/src/__tests__/Overlay.test.tsx +70 -0
  21. package/src/__tests__/Page.test.tsx +98 -0
  22. package/src/__tests__/Portal.test.tsx +28 -0
  23. package/src/__tests__/Sheet.test.tsx +125 -0
  24. package/src/__tests__/Switch.test.tsx +90 -0
  25. package/src/__tests__/Tabs.test.tsx +129 -0
  26. package/src/__tests__/Toggle.test.tsx +67 -0
  27. package/src/__tests__/Toolbar.test.tsx +147 -0
  28. package/src/__tests__/Tooltip.test.tsx +88 -0
  29. package/src/accordion/Accordion.stories.tsx +89 -0
  30. package/src/accordion/hooks/index.tsx +39 -0
  31. package/src/accordion/index.tsx +170 -0
  32. package/src/avatar/Avatar.stories.tsx +62 -0
  33. package/src/avatar/index.tsx +90 -0
  34. package/src/avatar/styles/index.ts +79 -0
  35. package/src/badge/Badge.stories.tsx +60 -0
  36. package/src/badge/index.tsx +58 -0
  37. package/src/badge/styles/index.ts +109 -0
  38. package/src/button/Button.stories.tsx +47 -0
  39. package/src/button/index.tsx +79 -0
  40. package/src/button/styles/index.ts +180 -0
  41. package/src/checkbox/Checkbox.stories.tsx +100 -0
  42. package/src/checkbox/hooks/index.tsx +40 -0
  43. package/src/checkbox/index.tsx +147 -0
  44. package/src/checkbox/styles/index.ts +139 -0
  45. package/src/collapsible/Collapsible.stories.tsx +95 -0
  46. package/src/collapsible/hooks/index.tsx +50 -0
  47. package/src/collapsible/index.tsx +137 -0
  48. package/src/dialog/Dialog.stories.tsx +73 -0
  49. package/src/dialog/hooks/index.tsx +35 -0
  50. package/src/dialog/index.tsx +221 -0
  51. package/src/dialog/styles/index.ts +72 -0
  52. package/src/divider/index.ts +10 -0
  53. package/src/dropdown/Dropdown.stories.tsx +100 -0
  54. package/src/dropdown/hooks/index.tsx +64 -0
  55. package/src/dropdown/index.tsx +316 -0
  56. package/src/dropdown/styles/index.ts +90 -0
  57. package/src/field/Field.stories.tsx +146 -0
  58. package/src/field/hooks/index.tsx +28 -0
  59. package/src/field/index.tsx +183 -0
  60. package/src/field/styles/index.ts +166 -0
  61. package/src/index.ts +33 -0
  62. package/src/otp-field/OTPField.stories.tsx +50 -0
  63. package/src/otp-field/hooks/index.tsx +13 -0
  64. package/src/otp-field/index.tsx +234 -0
  65. package/src/otp-field/styles/index.ts +33 -0
  66. package/src/otp-field/types/index.ts +23 -0
  67. package/src/overlay/Overlay.stories.tsx +59 -0
  68. package/src/overlay/index.tsx +58 -0
  69. package/src/overlay/styles/index.ts +26 -0
  70. package/src/page/Page.stories.tsx +85 -0
  71. package/src/page/index.tsx +265 -0
  72. package/src/page/styles/index.ts +59 -0
  73. package/src/portal/Portal.stories.tsx +27 -0
  74. package/src/portal/index.tsx +36 -0
  75. package/src/scrollarea/Scrollarea.stories.tsx +99 -0
  76. package/src/scrollarea/index.tsx +27 -0
  77. package/src/scrollarea/styles/index.ts +71 -0
  78. package/src/sheet/Sheet.stories.tsx +86 -0
  79. package/src/sheet/hooks/index.tsx +47 -0
  80. package/src/sheet/index.tsx +190 -0
  81. package/src/sheet/styles/index.ts +69 -0
  82. package/src/switch/Switch.stories.tsx +96 -0
  83. package/src/switch/hooks/index.tsx +33 -0
  84. package/src/switch/index.tsx +122 -0
  85. package/src/switch/styles/index.ts +118 -0
  86. package/src/table/index.tsx +138 -0
  87. package/src/table/styles/index.ts +48 -0
  88. package/src/tabs/Tabs.stories.tsx +87 -0
  89. package/src/tabs/hooks/index.tsx +35 -0
  90. package/src/tabs/index.tsx +161 -0
  91. package/src/tabs/styles/index.ts +9 -0
  92. package/src/toggle/Toggle.stories.tsx +118 -0
  93. package/src/toggle/index.tsx +55 -0
  94. package/src/toggle/styles/index.ts +0 -0
  95. package/src/toolbar/Toolbar.stories.tsx +89 -0
  96. package/src/toolbar/hooks/index.tsx +35 -0
  97. package/src/toolbar/index.tsx +243 -0
  98. package/src/toolbar/styles/index.ts +129 -0
  99. package/src/tooltip/Tooltip.stories.tsx +60 -0
  100. package/src/tooltip/index.tsx +177 -0
  101. package/src/tooltip/styles/index.ts +38 -0
  102. package/src/utils/index.ts +2 -0
  103. package/tsconfig.json +18 -0
  104. package/vitest.config.ts +16 -0
@@ -0,0 +1,161 @@
1
+ "use client";
2
+
3
+ import React, { Children } from "react";
4
+ import { TabsProvider, useTabs } from "./hooks";
5
+ import { Button, IButtonProperties } from "../button";
6
+ import { TabWrapper } from "./styles";
7
+ import { IReactChildren, ComponentVariantEnum } from "../../../../types";
8
+
9
+ export interface ITabsProperties extends React.ComponentProps<"div"> {
10
+ value?: string;
11
+ defaultOpen?: string;
12
+ }
13
+
14
+ export interface ITabsComposition {
15
+ Root: typeof TabsRoot;
16
+ Trigger: typeof TabsTrigger;
17
+ Content: typeof TabsContent;
18
+ }
19
+
20
+ /**
21
+ * Tabs are components for a set of tab panels that allows the user to switch between them.
22
+ *
23
+ * **Best practices:**
24
+ *
25
+ * - Ensure that the tabs can be navigated and activated using the keyboard.
26
+ * - Ensure that the focus is managed correctly when switching between tabs.
27
+ * - Ensure that the active tab is visibly indicated and that its content is visible and focusable.
28
+ *
29
+ * @param {ITabsProperties} props - The props for the Tabs component.
30
+ * @param {string} props.defaultOpen - The value of the tab panel that should be open by default.
31
+ * @param {ReactNode} props.children - The content to be rendered inside the tabs component.
32
+ * @returns {ReactElement} The Tabs component.
33
+ */
34
+ const Tabs = (props: ITabsProperties) => {
35
+ const { defaultOpen, children, ...restProps } = props;
36
+ const { methods } = useTabs();
37
+ const { applyValue } = methods;
38
+
39
+ const childArray = Children.toArray(children);
40
+ const firstChild = childArray[0];
41
+
42
+ React.useEffect(() => {
43
+ if (React.isValidElement<{ value: string }>(firstChild)) {
44
+ if (childArray.length > 0 && applyValue)
45
+ applyValue(firstChild.props.value);
46
+ }
47
+ }, []);
48
+
49
+ React.useEffect(() => {
50
+ if (defaultOpen && applyValue) applyValue(defaultOpen);
51
+ }, []);
52
+
53
+ return (
54
+ <TabWrapper role="tablist" {...restProps}>
55
+ {children}
56
+ </TabWrapper>
57
+ );
58
+ };
59
+ Tabs.displayName = "Tabs";
60
+
61
+ const TabsRoot = ({ children }: IReactChildren) => {
62
+ return <TabsProvider>{children}</TabsProvider>;
63
+ };
64
+ TabsRoot.displayName = "Tabs.Root";
65
+
66
+ /**
67
+ * Tabs.Trigger is used to triggers the expansion and collapse of the associated Tabs.Content component.
68
+ *
69
+ * **Best practices:**
70
+ *
71
+ * - Use a clear and descriptive title for the trigger that accurately conveys the content of the associated tab panel.
72
+ * - Ensure that the trigger can be operated using only the keyboard.
73
+ * - Ensure that the focus is properly managed when the trigger is activated.
74
+ *
75
+ * @param {IButtonProperties} props - The props for the Tabs.Trigger component.
76
+ * @param {string} props.value - The value used to bind the Tabs.Trigger and Tabs.Content components.
77
+ * @param {ReactNode} props.children - The content to be rendered inside the tabs trigger.
78
+ * @returns {ReactElement} The Tabs.Trigger component.
79
+ */
80
+ const TabsTrigger = (props: IButtonProperties) => {
81
+ const { value, onClick, children, ...restProps } = props;
82
+ const { states, methods } = useTabs();
83
+ const { applyValue, getTabsId } = methods;
84
+
85
+ const hasSameValueAsContext = value === states.value;
86
+ const IdHandler = {
87
+ trigger: getTabsId && getTabsId({ value, type: "trigger" }),
88
+ content: getTabsId && getTabsId({ value, type: "content" }),
89
+ };
90
+
91
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
92
+ if (applyValue) applyValue(value);
93
+ if (onClick) onClick(event);
94
+ };
95
+
96
+ return (
97
+ <Button
98
+ type="button"
99
+ role="tab"
100
+ title={`${value}-tab`}
101
+ id={String(IdHandler.trigger)}
102
+ value={value}
103
+ onClick={handleClick}
104
+ aria-selected={hasSameValueAsContext}
105
+ data-controls={IdHandler.content}
106
+ data-state={hasSameValueAsContext ? "active" : "inactive"}
107
+ variant={props.variant ?? ComponentVariantEnum.Ghost}
108
+ {...restProps}
109
+ >
110
+ {children}
111
+ </Button>
112
+ );
113
+ };
114
+ TabsTrigger.displayName = "Tabs.Trigger";
115
+
116
+ /**
117
+ * Tabs.Content is used to contains the content of the associated Tabs.Trigger component.
118
+ *
119
+ * **Best practices:**
120
+ *
121
+ * - Ensure that the content is hidden when the associated tab panel is collapsed.
122
+ * - Ensure that the content is properly focused when the associated tab panel is expanded.
123
+ *
124
+ * @param {ITabsProperties} props - The props for the Tabs.Content component.
125
+ * @param {string} props.value - The value used to bind the Tabs.Content and Tabs.Trigger components.
126
+ * @param {ReactNode} props.children - The content to be rendered inside the tab.
127
+ * @returns {ReactElement} The Tabs.Content component.
128
+ */
129
+ const TabsContent = (props: ITabsProperties) => {
130
+ const { value, children, ...restProps } = props;
131
+ const { states, methods } = useTabs();
132
+ const { getTabsId } = methods;
133
+
134
+ const hasSameValueAsContext = value === states.value;
135
+ const IdHandler = {
136
+ trigger: getTabsId && getTabsId({ value, type: "trigger" }),
137
+ content: getTabsId && getTabsId({ value, type: "content" }),
138
+ };
139
+
140
+ return (
141
+ <div
142
+ tabIndex={0}
143
+ role="tabpanel"
144
+ title={`${value}-tabpanel`}
145
+ id={String(IdHandler.content)}
146
+ data-value={value}
147
+ data-state={hasSameValueAsContext ? "active" : "inactive"}
148
+ aria-labelledby={IdHandler.trigger ?? restProps["aria-labelledby"]}
149
+ {...restProps}
150
+ >
151
+ {hasSameValueAsContext && children}
152
+ </div>
153
+ );
154
+ };
155
+ TabsContent.displayName = "Tabs.Content";
156
+
157
+ Tabs.Root = TabsRoot;
158
+ Tabs.Trigger = TabsTrigger;
159
+ Tabs.Content = TabsContent;
160
+
161
+ export { TabsRoot, Tabs, TabsTrigger, TabsContent };
@@ -0,0 +1,9 @@
1
+ import styled from "styled-components";
2
+
3
+ export const TabWrapper = styled.div<any>`
4
+ button {
5
+ &[aria-selected="true"] {
6
+ color: var(--font-color) !important;
7
+ }
8
+ }
9
+ `;
@@ -0,0 +1,118 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+
4
+ import { Toggle } from "..";
5
+ import { ComponentVariantEnum } from "../../../../types";
6
+
7
+ const meta = {
8
+ title: "Components/Toggle",
9
+ component: Toggle,
10
+ tags: ["autodocs"],
11
+ decorators: [
12
+ (Story) => (
13
+ <div className="m-medium-30">
14
+ <Story />
15
+ </div>
16
+ ),
17
+ ],
18
+ } satisfies Meta<typeof Toggle>;
19
+ export default meta;
20
+
21
+ type Story = StoryObj<typeof meta>;
22
+ export const Default: Story = {
23
+ render: ({ ...args }) => {
24
+ const [checked, setChecked] = React.useState(false);
25
+ const variant = checked
26
+ ? ComponentVariantEnum.Secondary
27
+ : ComponentVariantEnum.Border;
28
+
29
+ return <Toggle variant={variant} onClick={() => setChecked(!checked)} />;
30
+ },
31
+ };
32
+ export const DefaultChecked: Story = {
33
+ render: ({ ...args }) => {
34
+ const [checked, setChecked] = React.useState(true);
35
+ const variant = checked
36
+ ? ComponentVariantEnum.Secondary
37
+ : ComponentVariantEnum.Border;
38
+
39
+ return (
40
+ <Toggle
41
+ defaultChecked
42
+ variant={variant}
43
+ onClick={() => setChecked(!checked)}
44
+ />
45
+ );
46
+ },
47
+ };
48
+ export const Group: Story = {
49
+ render: ({ ...args }) => {
50
+ const [checkedItems, setCheckedItems] = React.useState<object>({
51
+ primary: false,
52
+ secondary: true,
53
+ tertiary: false,
54
+ });
55
+
56
+ const getVariant = (label) => {
57
+ return Boolean(checkedItems[label])
58
+ ? ComponentVariantEnum.Secondary
59
+ : ComponentVariantEnum.Border;
60
+ };
61
+ const handleCheckedItem = (event: any) => {
62
+ checkedItems[event.target.name] = !checkedItems[event.target.name];
63
+ setCheckedItems({ ...checkedItems });
64
+ };
65
+
66
+ return (
67
+ <div className="flex justify-start align-center g-medium-30">
68
+ {[
69
+ Object.keys(checkedItems).map((item) => (
70
+ <Toggle
71
+ key={item}
72
+ name={item}
73
+ variant={getVariant(item)}
74
+ onClick={handleCheckedItem}
75
+ />
76
+ )),
77
+ ]}
78
+ </div>
79
+ );
80
+ },
81
+ };
82
+ export const Sizes: Story = {
83
+ render: ({ ...args }) => {
84
+ const [checkedItems, setCheckedItems] = React.useState<object>({
85
+ large: false,
86
+ medium: false,
87
+ small: false,
88
+ });
89
+
90
+ const getVariant = (label) => {
91
+ return Boolean(checkedItems[label])
92
+ ? ComponentVariantEnum.Secondary
93
+ : ComponentVariantEnum.Border;
94
+ };
95
+ const handleCheckedItem = (event: any) => {
96
+ checkedItems[event.target.name] = !checkedItems[event.target.name];
97
+ setCheckedItems({ ...checkedItems });
98
+ };
99
+
100
+ return (
101
+ <div className="flex justify-start align-center g-medium-30">
102
+ {[
103
+ Object.keys(checkedItems).map((item) => (
104
+ <Toggle
105
+ key={item}
106
+ name={item}
107
+ variant={getVariant(item)}
108
+ sizing={item}
109
+ onClick={handleCheckedItem}
110
+ >
111
+ {item}
112
+ </Toggle>
113
+ )),
114
+ ]}
115
+ </div>
116
+ );
117
+ },
118
+ };
@@ -0,0 +1,55 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { Button, IButtonProperties } from "../button";
5
+
6
+ export interface IToggleProperties extends IButtonProperties {
7
+ defaultChecked?: boolean;
8
+ }
9
+
10
+ /**
11
+ * Toggle are button that can be switched on or off.
12
+ *
13
+ * **Best practices:**
14
+ *
15
+ * - Define the checked state of toggles with different variants, labels.
16
+ * - The interaction must have predictable behavior.
17
+ *
18
+ * @param {IToggleProperties} props - The props for the Toggle component.
19
+ * @param {boolean} props.defaultChecked - The initial checked state of the toggle. Defaults to false.
20
+ * @param {ReactNode} props.children - The content to be rendered inside the toggle button.
21
+ * @returns {ReactElement} The Toggle component.
22
+ */
23
+ export const Toggle = (props: IToggleProperties) => {
24
+ const { defaultChecked, onClick, disabled, children, ...restProps } = props;
25
+ const [checked, setChecked] = React.useState<boolean>(
26
+ defaultChecked ?? false
27
+ );
28
+
29
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
30
+ if (onClick) onClick(event);
31
+ if (!disabled) setChecked((prevChecked) => !prevChecked);
32
+ };
33
+
34
+ React.useEffect(() => {
35
+ if (defaultChecked !== undefined) {
36
+ setChecked(Boolean(defaultChecked));
37
+ }
38
+ }, [defaultChecked]);
39
+
40
+ return (
41
+ <Button
42
+ role="switch"
43
+ onClick={handleClick}
44
+ value={String(checked)}
45
+ aria-checked={checked}
46
+ data-checked={Boolean(checked)}
47
+ data-disabled={Boolean(disabled)}
48
+ disabled={disabled}
49
+ {...restProps}
50
+ >
51
+ {children}
52
+ </Button>
53
+ );
54
+ };
55
+ Toggle.displayName = "Toggle";
File without changes
@@ -0,0 +1,89 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+
4
+ import { Toolbar } from "..";
5
+ import {
6
+ ComponentSizeEnum,
7
+ ComponentSideEnum,
8
+ ComponentHeightEnum,
9
+ } from "../../../../types";
10
+
11
+ const meta = {
12
+ title: "Components/Toolbar",
13
+ component: Toolbar,
14
+ tags: ["autodocs"],
15
+ } satisfies Meta<typeof Toolbar>;
16
+ export default meta;
17
+
18
+ type Story = StoryObj<typeof meta>;
19
+ export const Default = {
20
+ args: {
21
+ raw: false,
22
+ defaultOpen: false,
23
+ showoncollapse: false,
24
+ showfirstchild: false,
25
+ shortcut: false,
26
+ hotkey: ":",
27
+ bindkey: null,
28
+ },
29
+ argTypes: {
30
+ side: {
31
+ options: [
32
+ ComponentSideEnum.Top,
33
+ ComponentSideEnum.Right,
34
+ ComponentSideEnum.Bottom,
35
+ ComponentSideEnum.Left,
36
+ ],
37
+ control: { type: "radio" },
38
+ },
39
+ sizing: {
40
+ options: [
41
+ ComponentSizeEnum.Small,
42
+ ComponentSizeEnum.Medium,
43
+ ComponentSizeEnum.Large,
44
+ ],
45
+ control: { type: "radio" },
46
+ },
47
+ height: {
48
+ options: [ComponentHeightEnum.Fullscreen, ComponentHeightEnum.Auto],
49
+ control: { type: "radio" },
50
+ },
51
+ },
52
+ render: ({ ...args }) => (
53
+ <Toolbar.Root>
54
+ <Toolbar>
55
+ <Toolbar.Section>
56
+ <Toolbar.Item>🐻‍❄️</Toolbar.Item>
57
+ </Toolbar.Section>
58
+
59
+ <Toolbar.Trigger>&hArr;</Toolbar.Trigger>
60
+ </Toolbar>
61
+ </Toolbar.Root>
62
+ ),
63
+ };
64
+ export const DefaultOpen: Story = {
65
+ render: ({ ...args }) => (
66
+ <Toolbar.Root>
67
+ <Toolbar defaultOpen>
68
+ <Toolbar.Section>
69
+ <Toolbar.Item>🐻‍❄️</Toolbar.Item>
70
+ </Toolbar.Section>
71
+
72
+ <Toolbar.Trigger>&hArr;</Toolbar.Trigger>
73
+ </Toolbar>
74
+ </Toolbar.Root>
75
+ ),
76
+ };
77
+ export const Shortcut: Story = {
78
+ render: ({ ...args }) => (
79
+ <Toolbar.Root>
80
+ <Toolbar shortcut hotkey="t">
81
+ <Toolbar.Section>
82
+ <Toolbar.Item>🐻‍❄️</Toolbar.Item>
83
+ </Toolbar.Section>
84
+
85
+ <Toolbar.Trigger>&hArr;</Toolbar.Trigger>
86
+ </Toolbar>
87
+ </Toolbar.Root>
88
+ ),
89
+ };
@@ -0,0 +1,35 @@
1
+ import React, { useState, createContext, useContext } from "react";
2
+ import { IReactChildren, IComponentAPI } from "../../../../../types";
3
+
4
+ const defaultComponentAPI = {
5
+ id: "",
6
+ states: {},
7
+ methods: {},
8
+ };
9
+ const ToolbarContext = createContext<IComponentAPI>(defaultComponentAPI);
10
+ export const useToolbar = () => useContext(ToolbarContext);
11
+
12
+ export const ToolbarProvider = ({ children }: IReactChildren): JSX.Element => {
13
+ const context = useToolbarProvider();
14
+
15
+ return (
16
+ <ToolbarContext.Provider value={context}>
17
+ {children}
18
+ </ToolbarContext.Provider>
19
+ );
20
+ };
21
+
22
+ function useToolbarProvider(): IComponentAPI {
23
+ const [expanded, setExpanded] = useState<boolean>(false);
24
+ const toolbarId = React.useId();
25
+
26
+ return {
27
+ id: toolbarId,
28
+ states: {
29
+ expanded,
30
+ },
31
+ methods: {
32
+ toggleToolbar: (state: boolean) => setExpanded(state ?? !expanded),
33
+ },
34
+ };
35
+ }