@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,316 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { useClickOutside, useDisabledScroll } from "@usefui/hooks";
5
+ import { DropdownMenuProvider, useDropdownMenu } from "./hooks";
6
+ import { RootWrapper, ContentWrapper, ItemWrapper } from "./styles";
7
+ import { Button, IButtonProperties } from "../button";
8
+ import { applyDataState } from "../utils";
9
+ import {
10
+ IReactChildren,
11
+ IComponentStyling,
12
+ IComponentSize,
13
+ ComponentSideEnum,
14
+ } from "../../../../types";
15
+
16
+ export interface IDropdownContentProperties
17
+ extends IComponentStyling,
18
+ IComponentSize,
19
+ React.ComponentPropsWithRef<"ul"> {
20
+ defaultOpen?: boolean;
21
+ }
22
+ export interface IDropdownItemProperties
23
+ extends IComponentStyling,
24
+ React.ComponentProps<"li"> {
25
+ radio?: boolean;
26
+ disabled?: boolean;
27
+ onClick?: (
28
+ event: React.MouseEvent<HTMLLIElement> | React.KeyboardEvent<HTMLLIElement>
29
+ ) => void;
30
+ onKeyDown?: (event: React.KeyboardEvent<HTMLLIElement>) => void;
31
+ }
32
+
33
+ export interface IDropdownComposition {
34
+ Root: typeof DropdownMenuRoot;
35
+ Trigger: typeof DropdownMenuTrigger;
36
+ Content: typeof DropdownMenuContent;
37
+ Item: typeof DropdownMenuItem;
38
+ }
39
+
40
+ /**
41
+ * Dropdown are used to expand and collapse list of actions.
42
+ *
43
+ * **Best practices:**
44
+ *
45
+ * - Use semantic HTML elements to structure the dropdown menu content.
46
+ * - Ensure that the dropdown menu can be opened and closed using the keyboard.
47
+ * - Ensure that the dropdown menu is visibly focused when opened using the keyboard.
48
+ * - Ensure that the dropdown menu is dismissed when the user clicks outside of it or presses the Esc key.
49
+ *
50
+ * @param {React.ComponentProps<"div">} props - The props for the DropdownMenu component.
51
+ * @param {ReactNode} props.children - The content to be rendered inside the dropdown menu.
52
+ * @returns {ReactElement} The DropdownMenu component.
53
+ */
54
+ const DropdownMenu = ({ children }: React.ComponentProps<"div">) => {
55
+ const DropdownContentRef = React.useRef(null);
56
+ const { states, methods } = useDropdownMenu();
57
+ const { toggleOpen } = methods;
58
+
59
+ const handleClickOutside = () => {
60
+ if (states.open && toggleOpen) toggleOpen();
61
+ };
62
+
63
+ useClickOutside(DropdownContentRef, handleClickOutside);
64
+ useDisabledScroll(Boolean(states.open));
65
+ return <RootWrapper ref={DropdownContentRef}>{children}</RootWrapper>;
66
+ };
67
+ DropdownMenu.displayName = "DropdownMenu";
68
+
69
+ const DropdownMenuRoot = ({ children }: IReactChildren) => {
70
+ return <DropdownMenuProvider>{children}</DropdownMenuProvider>;
71
+ };
72
+ DropdownMenuRoot.displayName = "DropdownMenu.Root";
73
+
74
+ /**
75
+ * DropdownMenu.Trigger is used to triggers the expansion and collapse of the associated DropdownMenu.Content component.
76
+ *
77
+ * **Best practices:**
78
+ *
79
+ * - Use a clear and descriptive title for the trigger that accurately conveys the content of the associated dropdown section.
80
+ * - Ensure that the trigger can be operated using only the keyboard.
81
+ * - Ensure that the focus is properly managed when the trigger is activated.
82
+ *
83
+ * @param {IButtonProperties} props - The props for the DropdownMenu.Trigger component.components.
84
+ * @param {ReactNode} props.children - The content to be rendered inside the dropdown trigger.
85
+ * @returns {ReactElement} The DropdownMenu.Trigger component.
86
+ */
87
+ const DropdownMenuTrigger = (props: IButtonProperties) => {
88
+ const triggerRef = React.useRef<HTMLButtonElement>(null);
89
+ const triggerRect = () => triggerRef.current?.getBoundingClientRect();
90
+
91
+ const { variant = "ghost", onClick, children, ...restProps } = props;
92
+ const { id, states, methods } = useDropdownMenu();
93
+ const { toggleOpen, setTriggerProps } = methods;
94
+
95
+ const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
96
+ if (onClick) onClick(event);
97
+ if (toggleOpen) toggleOpen();
98
+ if (setTriggerProps)
99
+ setTriggerProps({
100
+ top: Number(triggerRect()?.top),
101
+ right: Number(triggerRect()?.right),
102
+ bottom: Number(triggerRect()?.bottom),
103
+ left: Number(triggerRect()?.left),
104
+ width: Number(triggerRect()?.width),
105
+ height: Number(triggerRect()?.height),
106
+ });
107
+ };
108
+
109
+ return (
110
+ <Button
111
+ ref={triggerRef}
112
+ id={id.split("|").at(0)}
113
+ onClick={handleClick}
114
+ aria-haspopup="menu"
115
+ data-state={applyDataState(Boolean(states.open))}
116
+ variant={variant}
117
+ {...restProps}
118
+ >
119
+ {children}
120
+ </Button>
121
+ );
122
+ };
123
+ DropdownMenuTrigger.displayName = "DropdownMenu.Trigger";
124
+
125
+ /**
126
+ * DropdownMenu.Content is used to contains the content of the associated DropdownMenu.Trigger component.
127
+ *
128
+ * **Best practices:**
129
+ *
130
+ * - Ensure that the content is hidden when the associated dropdown menu is collapsed.
131
+ * - Ensure that the content is properly focused when the associated dropdown menu is expanded.
132
+ *
133
+ * @param {IDropdownContentProperties} props - The props for the DropdownMenu.Content component.
134
+ * @param {boolean} props.raw - Define whether the component is styled or not.
135
+ * @param {ComponentSizeEnum} props.sizing - The size of the component.
136
+ * @param {boolean} props.defaultOpen - The initial open state of the dropdown menu. Defaults to false.
137
+ * @param {ReactNode} props.children - The content to be rendered inside the dropdown menu.
138
+ * @returns {ReactElement} The DropdownMenu.Content component.
139
+ */
140
+ const DropdownMenuContent = React.forwardRef<
141
+ HTMLUListElement,
142
+ IDropdownContentProperties
143
+ >((props, _) => {
144
+ const { raw, sizing = "medium", defaultOpen, children, ...restProps } = props;
145
+ const { id, states, methods } = useDropdownMenu();
146
+ const { toggleOpen, setContentProps } = methods;
147
+
148
+ const mounted = React.useRef(false);
149
+ const contentRef = React.useRef<HTMLDivElement>(null);
150
+
151
+ const contentRect = () => contentRef?.current?.getBoundingClientRect();
152
+ const bodyRect = (): DOMRect | undefined => {
153
+ if (typeof document !== "undefined") {
154
+ return document?.body?.getBoundingClientRect();
155
+ }
156
+ return undefined;
157
+ };
158
+
159
+ const positions = {
160
+ btt: `calc((${states?.triggerProps?.top}px - ${states?.contentProps?.height}px) - (var(--measurement-medium-10) * 2))`,
161
+ ttb: `calc((${states?.triggerProps?.top}px + ${states?.triggerProps?.height}px) + var(--measurement-medium-10))`,
162
+ ltr: `${states?.triggerProps?.left}px`,
163
+ rtl: `calc(${states?.triggerProps?.left}px - (${states?.contentProps?.width}px - ${states?.triggerProps?.width}px))`,
164
+ };
165
+ const dimensions = {
166
+ body_width: bodyRect()?.width ?? 0,
167
+ body_height: bodyRect()?.height ?? 0,
168
+ content_width: states.contentProps.width,
169
+ content_height: states.contentProps.height,
170
+ content_left: states.contentProps.left,
171
+ content_bottom: states.contentProps.bottom,
172
+ };
173
+
174
+ const hasEnoughHorizontalSpace =
175
+ dimensions.body_width - dimensions.content_left >
176
+ dimensions.content_width * 1.1;
177
+
178
+ const hasEnoughVerticalSpace =
179
+ dimensions.body_height - dimensions.content_bottom >
180
+ dimensions.content_height - dimensions.content_height * 0.9;
181
+
182
+ React.useEffect(() => {
183
+ if (defaultOpen && toggleOpen) toggleOpen();
184
+ // eslint-disable-next-line react-hooks/exhaustive-deps
185
+ }, []);
186
+
187
+ React.useEffect(() => {
188
+ mounted.current = true;
189
+
190
+ setContentProps &&
191
+ setContentProps({
192
+ top: Number(contentRect()?.top),
193
+ right: Number(contentRect()?.right),
194
+ bottom: Number(contentRect()?.bottom),
195
+ left: Number(contentRect()?.left),
196
+ width: Number(contentRect()?.width),
197
+ height: Number(contentRect()?.height),
198
+ });
199
+
200
+ return () => {
201
+ mounted.current = false;
202
+ };
203
+ // eslint-disable-next-line react-hooks/exhaustive-deps
204
+ }, [states.open]);
205
+
206
+ return (
207
+ <>
208
+ {states.open && (
209
+ <ContentWrapper
210
+ ref={contentRef}
211
+ id={id.split("|").at(-1)}
212
+ role="menu"
213
+ tabIndex={-1}
214
+ aria-labelledby={id.split("|").at(0)}
215
+ data-state={applyDataState(Boolean(states.open))}
216
+ data-sizing={sizing}
217
+ data-side={
218
+ hasEnoughHorizontalSpace
219
+ ? ComponentSideEnum.Left
220
+ : ComponentSideEnum.Right
221
+ }
222
+ data-align={
223
+ hasEnoughHorizontalSpace
224
+ ? ComponentSideEnum.Left
225
+ : ComponentSideEnum.Right
226
+ }
227
+ data-raw={Boolean(raw)}
228
+ style={{
229
+ top: hasEnoughVerticalSpace ? positions.ttb : positions.btt,
230
+ left: hasEnoughHorizontalSpace ? positions.ltr : positions.rtl,
231
+ }}
232
+ {...restProps}
233
+ >
234
+ {children}
235
+ </ContentWrapper>
236
+ )}
237
+ </>
238
+ );
239
+ });
240
+ DropdownMenuContent.displayName = "DropdownMenu.Content";
241
+
242
+ /**
243
+ * DropdownMenu.Item is used to handle action inside DropdownMenu.Content.
244
+ *
245
+ * @param {IDropdownItemProperties} props - The props for the DropdownMenu.Item component.
246
+ * @param {boolean} props.raw - Define whether the component is styled or not.
247
+ * @param {boolean} props.radio - Define whether the component is collapses the dropdown menu on click or not.
248
+ * @returns {ReactElement} The DropdownMenu.Content component.
249
+ */
250
+ const DropdownMenuItem = (props: IDropdownItemProperties) => {
251
+ const {
252
+ raw,
253
+ onClick,
254
+ radio = false,
255
+ disabled,
256
+ children,
257
+ ...restProps
258
+ } = props;
259
+ const { methods } = useDropdownMenu();
260
+ const { toggleOpen } = methods;
261
+
262
+ const EventsHandler = {
263
+ toggle: () => {
264
+ if (!radio && toggleOpen) toggleOpen();
265
+ },
266
+ click: (
267
+ event:
268
+ | React.MouseEvent<HTMLLIElement>
269
+ | React.KeyboardEvent<HTMLLIElement>
270
+ ) => {
271
+ if (onClick) onClick(event);
272
+ },
273
+ };
274
+
275
+ const handleClick = (event: React.MouseEvent<HTMLLIElement>) => {
276
+ if (!disabled) {
277
+ EventsHandler.click(event);
278
+ EventsHandler.toggle();
279
+ }
280
+ };
281
+ const handleKeydown = (event: React.KeyboardEvent<HTMLLIElement>) => {
282
+ if (["Space", "Enter"].includes(event.code || event.key) && !disabled) {
283
+ EventsHandler.click(event);
284
+ EventsHandler.toggle();
285
+ }
286
+ };
287
+
288
+ return (
289
+ <ItemWrapper
290
+ role="menuitem"
291
+ tabIndex={0}
292
+ onClick={handleClick}
293
+ onKeyDown={handleKeydown}
294
+ aria-disabled={disabled}
295
+ data-orientation="vertical"
296
+ data-raw={Boolean(raw)}
297
+ {...restProps}
298
+ >
299
+ {children}
300
+ </ItemWrapper>
301
+ );
302
+ };
303
+ DropdownMenuItem.displayName = "DropdownMenu.Item";
304
+
305
+ DropdownMenu.Root = DropdownMenuRoot;
306
+ DropdownMenu.Trigger = DropdownMenuTrigger;
307
+ DropdownMenu.Content = DropdownMenuContent;
308
+ DropdownMenu.Item = DropdownMenuItem;
309
+
310
+ export {
311
+ DropdownMenuRoot,
312
+ DropdownMenu,
313
+ DropdownMenuTrigger,
314
+ DropdownMenuContent,
315
+ DropdownMenuItem,
316
+ };
@@ -0,0 +1,90 @@
1
+ import styled, { css, keyframes } from "styled-components";
2
+
3
+ const FadeIn = keyframes`
4
+ 0% {
5
+ opacity: 0;
6
+ }
7
+ 100% {
8
+ opacity: 1;
9
+ }
10
+ `;
11
+ const ContentWrapperSizes = css`
12
+ --small: var(--measurement-large-60);
13
+ --medium: var(--measurement-large-80);
14
+ --large: var(--measurement-large-90);
15
+
16
+ max-height: var(--measurement-large-90);
17
+
18
+ &[data-sizing="small"] {
19
+ width: var(--small);
20
+ max-width: var(--small);
21
+ }
22
+
23
+ &[data-sizing="medium"] {
24
+ width: var(--medium);
25
+ max-width: var(--medium);
26
+ }
27
+
28
+ &[data-sizing="large"] {
29
+ width: var(--large);
30
+ max-width: var(--large);
31
+ }
32
+ `;
33
+
34
+ export const RootWrapper = styled.div`
35
+ position: relative;
36
+ `;
37
+ export const ContentWrapper = styled.ul<any>`
38
+ --small: var(--measurement-large-60);
39
+ --medium: var(--measurement-large-80);
40
+ --large: var(--measurement-large-90);
41
+
42
+ &[data-raw="false"] {
43
+ position: fixed;
44
+ margin: 0;
45
+
46
+ padding: var(--measurement-medium-30);
47
+ margin: var(--measurement-medium-10) 0;
48
+
49
+ background-color: var(--body-color);
50
+ border: var(--measurement-small-10) solid var(--font-color-alpha-10);
51
+ border-radius: var(--measurement-medium-30);
52
+
53
+ z-index: var(--depth-default-100);
54
+ animation-duration: 0.2s;
55
+ animation-name: ${FadeIn};
56
+ animation-fill-mode: backwards;
57
+
58
+ ${ContentWrapperSizes}
59
+ }
60
+ `;
61
+
62
+ export const ItemWrapper = styled.li<any>`
63
+ list-style: none;
64
+ padding: 0;
65
+ margin: 0;
66
+ user-select: none;
67
+
68
+ &[data-raw="false"] {
69
+ font-size: var(--fontsize-small-80);
70
+ padding: var(--measurement-medium-30);
71
+ border-radius: var(--measurement-medium-20);
72
+ text-align: left;
73
+ color: var(--font-color-alpha-60);
74
+ outline: none;
75
+ transition: all ease-in-out 0.2s;
76
+ cursor: pointer;
77
+
78
+ &:hover,
79
+ &:focus,
80
+ &:active {
81
+ color: var(--font-color);
82
+ background-color: var(--font-color-alpha-10);
83
+ }
84
+ }
85
+
86
+ &[aria-disabled="true"] {
87
+ cursor: not-allowed;
88
+ opacity: 0.6;
89
+ }
90
+ `;
@@ -0,0 +1,146 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+
4
+ import { Field } from "..";
5
+ import { ComponentVariantEnum, ComponentSizeEnum } from "../../../../types";
6
+
7
+ const meta = {
8
+ title: "Components/Field",
9
+ component: Field,
10
+ tags: ["autodocs"],
11
+ decorators: [
12
+ (Story) => (
13
+ <div className="m-medium-30">
14
+ <Story />
15
+ </div>
16
+ ),
17
+ ],
18
+ } satisfies Meta<typeof Field>;
19
+ export default meta;
20
+
21
+ type Story = StoryObj<typeof meta>;
22
+ export const Default: Story = {
23
+ args: {
24
+ raw: false,
25
+ optional: false,
26
+ hint: "",
27
+ error: "",
28
+ },
29
+ argTypes: {
30
+ variant: {
31
+ options: [
32
+ ComponentVariantEnum.Primary,
33
+ ComponentVariantEnum.Secondary,
34
+ ComponentVariantEnum.Ghost,
35
+ ],
36
+ control: { type: "radio" },
37
+ },
38
+ sizing: {
39
+ options: [
40
+ ComponentSizeEnum.Small,
41
+ ComponentSizeEnum.Medium,
42
+ ComponentSizeEnum.Large,
43
+ ],
44
+ control: { type: "radio" },
45
+ },
46
+ },
47
+ render: ({ ...args }) => {
48
+ return (
49
+ <Field.Root>
50
+ <Field />
51
+ </Field.Root>
52
+ );
53
+ },
54
+ };
55
+ export const Label = {
56
+ render: ({ ...args }) => {
57
+ return (
58
+ <Field.Root>
59
+ <Field.Wrapper>
60
+ <Field.Label>Label</Field.Label>
61
+ <Field />
62
+ </Field.Wrapper>
63
+ </Field.Root>
64
+ );
65
+ },
66
+ };
67
+ export const Description = {
68
+ render: ({ ...args }) => {
69
+ return (
70
+ <Field.Root>
71
+ <Field />
72
+ <Field.Meta>Meta</Field.Meta>
73
+ </Field.Root>
74
+ );
75
+ },
76
+ };
77
+ export const Hint = {
78
+ render: ({ ...args }) => {
79
+ return (
80
+ <Field.Root>
81
+ <Field hint="hint" />
82
+ </Field.Root>
83
+ );
84
+ },
85
+ };
86
+ export const Error = {
87
+ render: ({ ...args }) => {
88
+ return (
89
+ <Field.Root>
90
+ <Field error="error" />
91
+ </Field.Root>
92
+ );
93
+ },
94
+ };
95
+ export const Composed = {
96
+ render: ({ ...args }) => {
97
+ return (
98
+ <Field.Root>
99
+ <Field.Wrapper>
100
+ <Field.Label>Label</Field.Label>
101
+ <Field hint="hint" />
102
+ </Field.Wrapper>
103
+ <Field.Meta>Meta</Field.Meta>
104
+ </Field.Root>
105
+ );
106
+ },
107
+ };
108
+ export const ComposedError = {
109
+ render: ({ ...args }) => {
110
+ return (
111
+ <Field.Root>
112
+ <Field.Wrapper>
113
+ <Field.Label>Label</Field.Label>
114
+ <Field error="error" hint="hint" />
115
+ </Field.Wrapper>
116
+ <Field.Meta>Meta</Field.Meta>
117
+ </Field.Root>
118
+ );
119
+ },
120
+ };
121
+ export const Sizes = {
122
+ render: ({ ...args }) => {
123
+ return (
124
+ <div className="flex g-medium-30">
125
+ {["large", "medium", "small"].map((item) => (
126
+ <Field.Root key={item}>
127
+ <Field placeholder={item} sizing={item} />
128
+ </Field.Root>
129
+ ))}
130
+ </div>
131
+ );
132
+ },
133
+ };
134
+ export const Variants = {
135
+ render: ({ ...args }) => {
136
+ return (
137
+ <div className="flex g-medium-30">
138
+ {["primary", "secondary", "ghost"].map((item) => (
139
+ <Field.Root key={item}>
140
+ <Field placeholder={item} variant={item} />
141
+ </Field.Root>
142
+ ))}
143
+ </div>
144
+ );
145
+ },
146
+ };
@@ -0,0 +1,28 @@
1
+ import React, { createContext, useContext } from "react";
2
+ import { IReactChildren, IComponentAPI } from "../../../../../types";
3
+
4
+ const defaultComponentAPI = {
5
+ id: "",
6
+ states: {},
7
+ methods: {},
8
+ };
9
+ const FieldContext = createContext<IComponentAPI>(defaultComponentAPI);
10
+ export const useField = () => useContext(FieldContext);
11
+
12
+ export const FieldProvider = ({ children }: IReactChildren): JSX.Element => {
13
+ const context = useFieldProvider();
14
+
15
+ return (
16
+ <FieldContext.Provider value={context}>{children}</FieldContext.Provider>
17
+ );
18
+ };
19
+
20
+ function useFieldProvider(): IComponentAPI {
21
+ const fieldId = React.useId();
22
+
23
+ return {
24
+ id: fieldId,
25
+ states: {},
26
+ methods: {},
27
+ };
28
+ }