@rovula/ui 0.1.7 → 0.1.9

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 (196) hide show
  1. package/dist/cjs/bundle.css +273 -126
  2. package/dist/cjs/bundle.js +1545 -1545
  3. package/dist/cjs/bundle.js.map +1 -1
  4. package/dist/cjs/types/components/AlertDialog/AlertDialog.stories.d.ts +3 -0
  5. package/dist/cjs/types/components/Dialog/Dialog.d.ts +7 -1
  6. package/dist/cjs/types/components/Dialog/Dialog.stories.d.ts +3 -0
  7. package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +2 -0
  8. package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
  9. package/dist/cjs/types/components/Form/Field.d.ts +26 -0
  10. package/dist/cjs/types/components/Form/FieldMessage.d.ts +7 -0
  11. package/dist/cjs/types/components/Form/Form.d.ts +49 -11
  12. package/dist/cjs/types/components/Form/Form.stories.d.ts +23 -0
  13. package/dist/cjs/types/components/Form/ValidationHintList.d.ts +20 -0
  14. package/dist/cjs/types/components/Form/ValidationHintList.stories.d.ts +9 -0
  15. package/dist/cjs/types/components/Form/index.d.ts +10 -0
  16. package/dist/cjs/types/components/Form/useOptionBridge.d.ts +17 -0
  17. package/dist/cjs/types/components/OtpInput/OtpInput.d.ts +17 -0
  18. package/dist/cjs/types/components/OtpInput/OtpInput.stories.d.ts +15 -0
  19. package/dist/cjs/types/components/OtpInput/OtpInputGroup.d.ts +25 -0
  20. package/dist/cjs/types/components/OtpInput/index.d.ts +5 -0
  21. package/dist/cjs/types/components/TextInput/TextInput.styles.d.ts +3 -0
  22. package/dist/cjs/types/index.d.ts +5 -0
  23. package/dist/cjs/types/theme/ThemeColorCoverageRuntime.stories.d.ts +10 -0
  24. package/dist/cjs/types/utils/colors.d.ts +84 -0
  25. package/dist/components/ActionButton/ActionButton.stories.js +2 -2
  26. package/dist/components/ActionButton/ActionButton.styles.js +1 -1
  27. package/dist/components/AlertDialog/AlertDialog.js +6 -6
  28. package/dist/components/AlertDialog/AlertDialog.stories.js +3 -0
  29. package/dist/components/Avatar/Avatar.stories.js +1 -1
  30. package/dist/components/Avatar/Avatar.styles.js +1 -1
  31. package/dist/components/Avatar/AvatarBase.js +1 -1
  32. package/dist/components/Avatar/AvatarGroup.stories.js +1 -1
  33. package/dist/components/Button/Buttons.stories.js +2 -2
  34. package/dist/components/Calendar/Calendar.js +1 -1
  35. package/dist/components/Checkbox/Checkbox.js +1 -1
  36. package/dist/components/Checkbox/Checkbox.stories.js +17 -7
  37. package/dist/components/Collapsible/Collapsible.styles.js +1 -1
  38. package/dist/components/DataTable/DataTable.js +2 -2
  39. package/dist/components/Dialog/Dialog.js +12 -7
  40. package/dist/components/Dialog/Dialog.stories.js +90 -2
  41. package/dist/components/Dropdown/Dropdown.js +2 -2
  42. package/dist/components/DropdownMenu/DropdownMenu.js +3 -3
  43. package/dist/components/FocusedScrollView/FocusedScrollView.stories.js +6 -6
  44. package/dist/components/Form/Field.js +60 -0
  45. package/dist/components/Form/FieldMessage.js +24 -0
  46. package/dist/components/Form/Form.js +73 -41
  47. package/dist/components/Form/Form.stories.js +221 -0
  48. package/dist/components/Form/ValidationHintList.js +30 -0
  49. package/dist/components/Form/ValidationHintList.stories.js +50 -0
  50. package/dist/components/Form/index.js +5 -0
  51. package/dist/components/Form/useOptionBridge.js +27 -0
  52. package/dist/components/InputFilter/InputFilter.js +5 -4
  53. package/dist/components/InputFilter/InputFilter.stories.js +1 -1
  54. package/dist/components/InputFilter/InputFilter.styles.js +14 -1
  55. package/dist/components/Label/Label.styles.js +1 -1
  56. package/dist/components/Menu/Menu.js +2 -2
  57. package/dist/components/NumberInput/NumberInput.stories.js +1 -1
  58. package/dist/components/OtpInput/OtpInput.js +118 -0
  59. package/dist/components/OtpInput/OtpInput.stories.js +60 -0
  60. package/dist/components/OtpInput/OtpInputGroup.js +23 -0
  61. package/dist/components/OtpInput/index.js +3 -0
  62. package/dist/components/PasswordInput/PasswordInput.stories.js +1 -1
  63. package/dist/components/Popover/Popover.js +1 -1
  64. package/dist/components/RadioGroup/RadioGroup.js +1 -1
  65. package/dist/components/RadioGroup/RadioGroup.stories.js +2 -2
  66. package/dist/components/Search/Search.js +13 -1
  67. package/dist/components/Search/Search.stories.js +1 -1
  68. package/dist/components/Slider/Slider.js +1 -1
  69. package/dist/components/Slider/Slider.stories.js +5 -5
  70. package/dist/components/Switch/Switch.stories.js +2 -2
  71. package/dist/components/Table/Table.js +5 -5
  72. package/dist/components/Tabs/Tabs.js +12 -9
  73. package/dist/components/Tabs/Tabs.stories.js +1 -1
  74. package/dist/components/Text/Text.js +1 -1
  75. package/dist/components/Text/Text.stories.js +1 -1
  76. package/dist/components/TextArea/TextArea.stories.js +1 -1
  77. package/dist/components/TextArea/TextArea.styles.js +3 -3
  78. package/dist/components/TextInput/TextInput.js +3 -2
  79. package/dist/components/TextInput/TextInput.stories.js +3 -3
  80. package/dist/components/TextInput/TextInput.styles.js +41 -19
  81. package/dist/components/Toast/Toast.js +4 -2
  82. package/dist/components/Toast/Toast.stories.js +1 -1
  83. package/dist/components/Toast/Toast.styles.js +4 -4
  84. package/dist/components/Toast/Toaster.js +2 -2
  85. package/dist/components/Tree/Tree.stories.js +1 -1
  86. package/dist/components/Tree/TreeItem.js +1 -1
  87. package/dist/esm/bundle.css +273 -126
  88. package/dist/esm/bundle.js +1545 -1545
  89. package/dist/esm/bundle.js.map +1 -1
  90. package/dist/esm/types/components/AlertDialog/AlertDialog.stories.d.ts +3 -0
  91. package/dist/esm/types/components/Dialog/Dialog.d.ts +7 -1
  92. package/dist/esm/types/components/Dialog/Dialog.stories.d.ts +3 -0
  93. package/dist/esm/types/components/Dropdown/Dropdown.d.ts +2 -0
  94. package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
  95. package/dist/esm/types/components/Form/Field.d.ts +26 -0
  96. package/dist/esm/types/components/Form/FieldMessage.d.ts +7 -0
  97. package/dist/esm/types/components/Form/Form.d.ts +49 -11
  98. package/dist/esm/types/components/Form/Form.stories.d.ts +23 -0
  99. package/dist/esm/types/components/Form/ValidationHintList.d.ts +20 -0
  100. package/dist/esm/types/components/Form/ValidationHintList.stories.d.ts +9 -0
  101. package/dist/esm/types/components/Form/index.d.ts +10 -0
  102. package/dist/esm/types/components/Form/useOptionBridge.d.ts +17 -0
  103. package/dist/esm/types/components/OtpInput/OtpInput.d.ts +17 -0
  104. package/dist/esm/types/components/OtpInput/OtpInput.stories.d.ts +15 -0
  105. package/dist/esm/types/components/OtpInput/OtpInputGroup.d.ts +25 -0
  106. package/dist/esm/types/components/OtpInput/index.d.ts +5 -0
  107. package/dist/esm/types/components/TextInput/TextInput.styles.d.ts +3 -0
  108. package/dist/esm/types/index.d.ts +5 -0
  109. package/dist/esm/types/theme/ThemeColorCoverageRuntime.stories.d.ts +10 -0
  110. package/dist/esm/types/utils/colors.d.ts +84 -0
  111. package/dist/index.d.ts +248 -2
  112. package/dist/index.js +3 -0
  113. package/dist/src/theme/global.css +340 -151
  114. package/dist/theme/ThemeColorCoverageRuntime.stories.js +91 -0
  115. package/dist/utils/colors.js +92 -0
  116. package/package.json +4 -2
  117. package/src/components/ActionButton/ActionButton.stories.tsx +6 -6
  118. package/src/components/ActionButton/ActionButton.styles.ts +1 -1
  119. package/src/components/AlertDialog/AlertDialog.stories.tsx +22 -0
  120. package/src/components/AlertDialog/AlertDialog.tsx +6 -6
  121. package/src/components/Avatar/Avatar.stories.tsx +1 -1
  122. package/src/components/Avatar/Avatar.styles.ts +1 -1
  123. package/src/components/Avatar/AvatarBase.tsx +1 -1
  124. package/src/components/Avatar/AvatarGroup.stories.tsx +1 -1
  125. package/src/components/Button/Buttons.stories.tsx +10 -10
  126. package/src/components/Calendar/Calendar.tsx +3 -3
  127. package/src/components/Checkbox/Checkbox.stories.tsx +35 -12
  128. package/src/components/Checkbox/Checkbox.tsx +7 -5
  129. package/src/components/Collapsible/Collapsible.styles.ts +1 -1
  130. package/src/components/DataTable/DataTable.tsx +2 -2
  131. package/src/components/Dialog/Dialog.stories.tsx +173 -0
  132. package/src/components/Dialog/Dialog.tsx +32 -15
  133. package/src/components/Dropdown/Dropdown.styles.ts +1 -1
  134. package/src/components/Dropdown/Dropdown.tsx +16 -14
  135. package/src/components/DropdownMenu/DropdownMenu.tsx +3 -3
  136. package/src/components/FocusedScrollView/FocusedScrollView.stories.tsx +10 -10
  137. package/src/components/Form/Field.tsx +160 -0
  138. package/src/components/Form/FieldMessage.tsx +38 -0
  139. package/src/components/Form/Form.docs.mdx +67 -0
  140. package/src/components/Form/Form.stories.tsx +490 -0
  141. package/src/components/Form/Form.tsx +185 -87
  142. package/src/components/Form/README.md +284 -0
  143. package/src/components/Form/ValidationHintList.stories.tsx +118 -0
  144. package/src/components/Form/ValidationHintList.tsx +95 -0
  145. package/src/components/Form/index.ts +28 -0
  146. package/src/components/Form/useOptionBridge.ts +55 -0
  147. package/src/components/InputFilter/InputFilter.stories.tsx +1 -1
  148. package/src/components/InputFilter/InputFilter.styles.ts +14 -1
  149. package/src/components/InputFilter/InputFilter.tsx +33 -28
  150. package/src/components/Label/Label.styles.ts +2 -2
  151. package/src/components/Label/Label.tsx +1 -1
  152. package/src/components/Menu/Menu.tsx +12 -12
  153. package/src/components/NumberInput/NumberInput.stories.tsx +1 -1
  154. package/src/components/OtpInput/OtpInput.stories.tsx +168 -0
  155. package/src/components/OtpInput/OtpInput.tsx +223 -0
  156. package/src/components/OtpInput/OtpInputGroup.tsx +74 -0
  157. package/src/components/OtpInput/index.ts +5 -0
  158. package/src/components/PasswordInput/PasswordInput.stories.tsx +1 -1
  159. package/src/components/Popover/Popover.tsx +1 -1
  160. package/src/components/RadioGroup/RadioGroup.stories.tsx +4 -4
  161. package/src/components/RadioGroup/RadioGroup.tsx +2 -1
  162. package/src/components/Search/Search.stories.tsx +1 -1
  163. package/src/components/Search/Search.tsx +6 -2
  164. package/src/components/Slider/Slider.stories.tsx +7 -7
  165. package/src/components/Slider/Slider.tsx +1 -1
  166. package/src/components/Switch/Switch.stories.tsx +4 -4
  167. package/src/components/Table/Table.tsx +5 -5
  168. package/src/components/Tabs/Tabs.stories.tsx +1 -1
  169. package/src/components/Tabs/Tabs.tsx +29 -18
  170. package/src/components/Text/Text.stories.tsx +1 -1
  171. package/src/components/Text/Text.tsx +1 -1
  172. package/src/components/TextArea/TextArea.stories.tsx +1 -1
  173. package/src/components/TextArea/TextArea.styles.ts +3 -3
  174. package/src/components/TextInput/TextInput.stories.tsx +7 -7
  175. package/src/components/TextInput/TextInput.styles.ts +42 -19
  176. package/src/components/TextInput/TextInput.tsx +3 -1
  177. package/src/components/Toast/Toast.stories.tsx +1 -1
  178. package/src/components/Toast/Toast.styles.tsx +7 -7
  179. package/src/components/Toast/Toast.tsx +5 -4
  180. package/src/components/Toast/Toaster.tsx +17 -20
  181. package/src/components/Tree/Tree.stories.tsx +1 -1
  182. package/src/components/Tree/TreeItem.tsx +1 -1
  183. package/src/index.ts +5 -0
  184. package/src/theme/ThemeColorCoverageRuntime.stories.tsx +236 -0
  185. package/src/theme/direct-token-migration-plan.md +121 -0
  186. package/src/theme/figma-mcp-check-report.md +225 -0
  187. package/src/theme/figma-mcp-component-checklist.json +1250 -0
  188. package/src/theme/presets/colors.js +155 -44
  189. package/src/theme/themes/xspector/components/loading.css +2 -2
  190. package/src/theme/tokens/color.css +3 -3
  191. package/src/theme/tokens/components/action-button.css +1 -1
  192. package/src/theme/tokens/components/dropdown-menu.css +3 -3
  193. package/src/theme/tokens/components/loading.css +2 -2
  194. package/src/theme/tokens/components/switch.css +1 -1
  195. package/src/theme/utils.js +164 -25
  196. package/src/utils/colors.ts +92 -0
@@ -0,0 +1,221 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import React, { useMemo, useState } from "react";
3
+ import * as yup from "yup";
4
+ import Button from "@/components/Button/Button";
5
+ import TextInput from "@/components/TextInput/TextInput";
6
+ import Dropdown from "@/components/Dropdown/Dropdown";
7
+ import DatePicker from "@/components/DatePicker/DatePicker";
8
+ import { NumberInput } from "@/components/NumberInput/NumberInput";
9
+ import { Switch } from "@/components/Switch/Switch";
10
+ import { Field } from "./Field";
11
+ import { Form, useControlledForm, } from "./Form";
12
+ const roleOptions = [
13
+ { value: "dev", label: "Developer" },
14
+ { value: "pm", label: "Product Manager" },
15
+ { value: "qa", label: "QA Engineer" },
16
+ ];
17
+ const meta = {
18
+ title: "Components/Form",
19
+ tags: ["autodocs"],
20
+ parameters: {
21
+ layout: "centered",
22
+ },
23
+ decorators: [
24
+ (Story) => (_jsx("div", { className: "w-[520px] max-w-full p-5 bg-bg-bg2 rounded-md", children: _jsx(Story, {}) })),
25
+ ],
26
+ };
27
+ export default meta;
28
+ const loginSchema = yup.object({
29
+ email: yup
30
+ .string()
31
+ .email("Invalid email format")
32
+ .required("Email is required"),
33
+ password: yup
34
+ .string()
35
+ .required("Password is required")
36
+ .min(6, "Password must be at least 6 characters"),
37
+ });
38
+ export const BasicYupLogin = {
39
+ args: {},
40
+ render: () => {
41
+ const [lastEmailChange, setLastEmailChange] = useState("");
42
+ return (_jsxs(Form, { className: "flex flex-col gap-3", defaultValues: {
43
+ email: "",
44
+ password: "",
45
+ }, validationSchema: loginSchema, onSubmit: (values) => {
46
+ // eslint-disable-next-line no-console
47
+ console.log("Submitted form values:", values);
48
+ }, children: [_jsx(Field, { name: "email", component: TextInput, componentProps: {
49
+ label: "Email",
50
+ type: "email",
51
+ helperText: "Use your work email",
52
+ required: true,
53
+ }, onChange: (value) => {
54
+ setLastEmailChange(String(value !== null && value !== void 0 ? value : ""));
55
+ } }), _jsx(Field, { name: "password", component: TextInput, componentProps: {
56
+ label: "Password",
57
+ type: "password",
58
+ required: true,
59
+ } }), _jsxs("div", { className: "text-xs text-text-g-contrast-medium", children: ["Last email change: ", lastEmailChange || "-"] }), _jsx(Button, { type: "submit", children: "Sign in" })] }));
60
+ },
61
+ };
62
+ export const MixedUiKitControls = {
63
+ args: {},
64
+ render: () => {
65
+ const selectedRole = useMemo(() => roleOptions.find((item) => item.value === "dev"), []);
66
+ return (_jsxs(Form, { className: "flex flex-col gap-4", defaultValues: {
67
+ fullName: "",
68
+ role: (selectedRole === null || selectedRole === void 0 ? void 0 : selectedRole.value) || "",
69
+ salary: undefined,
70
+ startDate: undefined,
71
+ isActive: true,
72
+ }, validationSchema: yup.object({
73
+ fullName: yup.string().required("Full name is required"),
74
+ role: yup.string().required("Please select a role"),
75
+ }), onSubmit: (values) => {
76
+ // eslint-disable-next-line no-console
77
+ console.log("Submitted employee values:", values);
78
+ }, children: [_jsx(Field, { name: "fullName", component: TextInput, componentProps: {
79
+ label: "Full name",
80
+ required: true,
81
+ } }), _jsx(Field, { name: "role", component: Dropdown, componentProps: {
82
+ label: "Role",
83
+ options: roleOptions,
84
+ required: true,
85
+ helperText: "Choose one role",
86
+ }, valuePropName: "value", changePropName: "onSelect", parseValue: (incoming) => {
87
+ const option = incoming;
88
+ return (option === null || option === void 0 ? void 0 : option.value) || "";
89
+ }, formatValue: (value) => roleOptions.find((option) => option.value === value) }), _jsx(Field, { name: "salary", component: NumberInput, componentProps: {
90
+ label: "Salary",
91
+ min: 0,
92
+ thousandSeparator: ",",
93
+ formatDisplay: true,
94
+ }, parseValue: (incoming) => typeof incoming === "number" ? incoming : undefined }), _jsx(Field, { name: "startDate", component: DatePicker, componentProps: {
95
+ date: undefined,
96
+ onSelect: () => undefined,
97
+ textInputProps: { label: "Start date", required: false },
98
+ }, valuePropName: "date", changePropName: "onSelect", blurPropName: false, refPropName: false, errorMessagePropName: false, invalidPropName: false, parseValue: (incoming) => incoming }), _jsxs("div", { className: "flex items-center justify-between rounded-md border border-bg-stroke1 px-3 py-2", children: [_jsx("span", { className: "text-sm text-text-contrast-max", children: "Active employee" }), _jsx(Field, { name: "isActive", component: Switch, valuePropName: "checked", changePropName: "onCheckedChange", blurPropName: false, errorMessagePropName: false, invalidPropName: false, parseValue: (incoming) => Boolean(incoming) })] }), _jsx(Button, { type: "submit", children: "Create employee" })] }));
99
+ },
100
+ };
101
+ export const ResolverBasedValidation = {
102
+ args: {},
103
+ render: () => {
104
+ const resolver = async (values) => {
105
+ var _a;
106
+ const errors = {};
107
+ if (!((_a = values.email) === null || _a === void 0 ? void 0 : _a.includes("@"))) {
108
+ errors.email = { type: "validate", message: "Email must include @" };
109
+ }
110
+ if (!values.password || values.password.length < 8) {
111
+ errors.password = {
112
+ type: "validate",
113
+ message: "Password must be at least 8 characters",
114
+ };
115
+ }
116
+ return {
117
+ values: Object.keys(errors).length ? {} : values,
118
+ errors,
119
+ };
120
+ };
121
+ return (_jsxs(Form, { className: "flex flex-col gap-3", defaultValues: {
122
+ email: "",
123
+ password: "",
124
+ }, resolver: resolver, onSubmit: (values) => {
125
+ // eslint-disable-next-line no-console
126
+ console.log("Resolver submit values:", values);
127
+ }, children: [_jsx(Field, { name: "email", component: TextInput, componentProps: {
128
+ label: "Email",
129
+ type: "email",
130
+ required: true,
131
+ } }), _jsx(Field, { name: "password", component: TextInput, componentProps: {
132
+ label: "Password",
133
+ type: "password",
134
+ helperText: "This story demonstrates custom resolver.",
135
+ required: true,
136
+ } }), _jsx(Button, { type: "submit", children: "Validate" })] }));
137
+ },
138
+ };
139
+ export const RenderPropsCodeControl = {
140
+ args: {},
141
+ render: () => {
142
+ const codeControlSchema = yup.object({
143
+ title: yup.string().required("Title is required"),
144
+ description: yup
145
+ .string()
146
+ .required("Description is required")
147
+ .min(10, "Description must be at least 10 characters"),
148
+ });
149
+ return (_jsx(Form, { className: "flex flex-col gap-3", defaultValues: {
150
+ title: "",
151
+ description: "",
152
+ }, mode: "onChange", validationSchema: codeControlSchema, onSubmit: (values) => {
153
+ // eslint-disable-next-line no-console
154
+ console.log("Submit with code:", values);
155
+ }, children: ({ formState, getValues, handleSubmit }) => (_jsxs(_Fragment, { children: [_jsx(Field, { name: "title", component: TextInput, componentProps: {
156
+ label: "Title",
157
+ required: true,
158
+ } }), _jsx(Field, { name: "description", component: TextInput, componentProps: {
159
+ label: "Description",
160
+ helperText: "Try at least 10 characters",
161
+ required: true,
162
+ } }), _jsx("div", { className: "flex gap-2", children: _jsx(Button, { type: "submit", disabled: !formState.isValid || formState.isSubmitting, isLoading: formState.isSubmitting, children: "Submit with code" }) })] })) }));
163
+ },
164
+ };
165
+ export const HigherLayerCodeControl = {
166
+ args: {},
167
+ render: () => {
168
+ const [stateSnapshot, setStateSnapshot] = useState("-");
169
+ const [submitCount, setSubmitCount] = useState(0);
170
+ const formRef = React.useRef(null);
171
+ const schema = yup.object({
172
+ title: yup.string().required("Title is required"),
173
+ description: yup
174
+ .string()
175
+ .required("Description is required")
176
+ .min(10, "Description must be at least 10 characters"),
177
+ });
178
+ const { methods, FormRoot } = useControlledForm({
179
+ defaultValues: {
180
+ title: "",
181
+ description: "",
182
+ },
183
+ validationSchema: schema,
184
+ mode: "onChange",
185
+ });
186
+ const onSubmit = (values) => {
187
+ setSubmitCount((prev) => prev + 1);
188
+ // eslint-disable-next-line no-console
189
+ console.log("Higher layer submit with code:", values);
190
+ };
191
+ return (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsxs("div", { className: "rounded-md border border-bg-stroke1 bg-bg-bg2 p-3 text-xs text-text-g-contrast-medium", children: ["Higher layer state: ", methods.formState.isValid ? "valid" : "invalid", " ", "/", methods.formState.isDirty ? " dirty" : " pristine", " / submits:", " ", submitCount] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => {
192
+ const snapshot = JSON.stringify({
193
+ values: methods.getValues(),
194
+ isValid: methods.formState.isValid,
195
+ isDirty: methods.formState.isDirty,
196
+ });
197
+ setStateSnapshot(snapshot);
198
+ }, children: "Get state with code (outside Form)" }), _jsx(Button, { type: "button", disabled: !methods.formState.isValid || methods.formState.isSubmitting, isLoading: methods.formState.isSubmitting, onClick: () => {
199
+ methods.handleSubmit(onSubmit)();
200
+ }, children: "Submit with code (outside Form)" }), _jsx(Button, { type: "button", variant: "outline", onClick: async () => {
201
+ var _a;
202
+ await ((_a = formRef.current) === null || _a === void 0 ? void 0 : _a.submit());
203
+ }, children: "Submit with ref" }), _jsx(Button, { type: "button", variant: "outline", onClick: () => {
204
+ var _a;
205
+ const valuesFromRef = (_a = formRef.current) === null || _a === void 0 ? void 0 : _a.getValues();
206
+ if (!valuesFromRef)
207
+ return;
208
+ setStateSnapshot(JSON.stringify({
209
+ values: valuesFromRef,
210
+ via: "controllerRef",
211
+ }));
212
+ }, children: "Get state with ref" })] }), _jsxs("div", { className: "rounded-md border border-bg-stroke1 bg-bg-bg2 p-3 text-xs text-text-g-contrast-medium", children: ["Snapshot: ", stateSnapshot] }), _jsxs(FormRoot, { ref: formRef, className: "flex flex-col gap-3", onSubmit: onSubmit, children: [_jsx(Field, { name: "title", component: TextInput, componentProps: {
213
+ label: "Title",
214
+ required: true,
215
+ } }), _jsx(Field, { name: "description", component: TextInput, componentProps: {
216
+ label: "Description",
217
+ helperText: "Try at least 10 characters",
218
+ required: true,
219
+ } })] })] }));
220
+ },
221
+ };
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CircleCheck } from "lucide-react";
3
+ import { cn } from "@/utils/cn";
4
+ const resolveHintState = (values, rule) => {
5
+ const shouldEvaluate = rule.when ? rule.when(values) : true;
6
+ if (!shouldEvaluate)
7
+ return "pending";
8
+ return rule.validate(values) ? "valid" : "invalid";
9
+ };
10
+ const hintLabelStateClass = {
11
+ valid: "text-text-g-contrast-high",
12
+ invalid: "text-input-error",
13
+ pending: "text-text-g-contrast-high",
14
+ };
15
+ const hintIconStateClass = {
16
+ valid: "text-primary",
17
+ invalid: "text-input-error",
18
+ pending: "text-text-g-contrast-low",
19
+ };
20
+ export const ValidationHintList = ({ values, rules, mode = ["pending", "valid", "invalid"], className, itemClassName, labelStateClassName, iconStateClassName, }) => {
21
+ const enabledStates = new Set(mode);
22
+ return (_jsx("ul", { className: cn("mt-2 flex flex-col gap-3", className), children: rules.map((rule) => {
23
+ const state = resolveHintState(values, rule);
24
+ const normalizedState = enabledStates.has(state)
25
+ ? state
26
+ : "pending";
27
+ return (_jsxs("li", { className: cn("flex items-center gap-2 typography-small2 ", hintLabelStateClass[normalizedState], labelStateClassName === null || labelStateClassName === void 0 ? void 0 : labelStateClassName[normalizedState], itemClassName), children: [_jsx(CircleCheck, { className: cn("size-4", hintIconStateClass[normalizedState], iconStateClassName === null || iconStateClassName === void 0 ? void 0 : iconStateClassName[normalizedState]) }), _jsx("span", { children: rule.label })] }, rule.id));
28
+ }) }));
29
+ };
30
+ export default ValidationHintList;
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import PasswordInput from "@/components/PasswordInput";
4
+ import { ValidationHintList } from "./ValidationHintList";
5
+ const rules = [
6
+ {
7
+ id: "min-length",
8
+ label: "Minimum 12 characters",
9
+ validate: (values) => values.password.length >= 12,
10
+ when: (values) => Boolean(values.password),
11
+ },
12
+ {
13
+ id: "upper-lower",
14
+ label: "At least one upper and lower case",
15
+ validate: (values) => /[a-z]/.test(values.password) && /[A-Z]/.test(values.password),
16
+ when: (values) => Boolean(values.password),
17
+ },
18
+ {
19
+ id: "match",
20
+ label: "Password and confirmation are matching",
21
+ validate: (values) => Boolean(values.password) && values.password === values.confirmPassword,
22
+ when: (values) => Boolean(values.password) && Boolean(values.confirmPassword),
23
+ },
24
+ ];
25
+ const meta = {
26
+ title: "Components/Form/ValidationHintList",
27
+ tags: ["autodocs"],
28
+ parameters: {
29
+ layout: "centered",
30
+ },
31
+ };
32
+ export default meta;
33
+ export const ThreeState = {
34
+ render: () => {
35
+ const [values, setValues] = useState({
36
+ password: "",
37
+ confirmPassword: "",
38
+ });
39
+ return (_jsxs("div", { className: "w-[420px] max-w-full rounded-md bg-bg-bg2 p-4", children: [_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(PasswordInput, { label: "Password", value: values.password, onChange: (event) => setValues((prev) => (Object.assign(Object.assign({}, prev), { password: event.target.value }))) }), _jsx(PasswordInput, { label: "Confirm password", value: values.confirmPassword, onChange: (event) => setValues((prev) => (Object.assign(Object.assign({}, prev), { confirmPassword: event.target.value }))) })] }), _jsx(ValidationHintList, { values: values, rules: rules })] }));
40
+ },
41
+ };
42
+ export const TwoState = {
43
+ render: () => {
44
+ const [values, setValues] = useState({
45
+ password: "",
46
+ confirmPassword: "",
47
+ });
48
+ return (_jsxs("div", { className: "w-[420px] max-w-full rounded-md bg-bg-bg2 p-4", children: [_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(PasswordInput, { label: "Password", value: values.password, onChange: (event) => setValues((prev) => (Object.assign(Object.assign({}, prev), { password: event.target.value }))) }), _jsx(PasswordInput, { label: "Confirm password", value: values.confirmPassword, onChange: (event) => setValues((prev) => (Object.assign(Object.assign({}, prev), { confirmPassword: event.target.value }))) })] }), _jsx(ValidationHintList, { values: values, rules: rules, mode: ["pending", "valid"] })] }));
49
+ },
50
+ };
@@ -0,0 +1,5 @@
1
+ export { Form, createControlledForm, createYupResolver, useControlledForm, } from "./Form";
2
+ export { Field } from "./Field";
3
+ export { FieldMessage } from "./FieldMessage";
4
+ export { ValidationHintList } from "./ValidationHintList";
5
+ export { useOptionBridge } from "./useOptionBridge";
@@ -0,0 +1,27 @@
1
+ import { useMemo } from "react";
2
+ export const useOptionBridge = ({ options, loading = false, buildFallbackOption, }) => {
3
+ const optionsByValue = useMemo(() => {
4
+ return new Map(options.map((option) => [option.value, option]));
5
+ }, [options]);
6
+ const toOption = (value) => {
7
+ if (value === null || value === undefined)
8
+ return undefined;
9
+ return optionsByValue.get(value);
10
+ };
11
+ const toValue = (option) => {
12
+ return option === null || option === void 0 ? void 0 : option.value;
13
+ };
14
+ const toOptionWithFallback = (value) => {
15
+ var _a;
16
+ if (value === null || value === undefined)
17
+ return undefined;
18
+ return (_a = toOption(value)) !== null && _a !== void 0 ? _a : buildFallbackOption === null || buildFallbackOption === void 0 ? void 0 : buildFallbackOption(value, loading);
19
+ };
20
+ return {
21
+ toOption,
22
+ toOptionWithFallback,
23
+ toValue,
24
+ optionsByValue,
25
+ };
26
+ };
27
+ export default useOptionBridge;
@@ -77,7 +77,7 @@ const InputFilter = forwardRef((_a, ref) => {
77
77
  onClick: handleOptionClick,
78
78
  });
79
79
  }
80
- return (_jsxs("ul", { className: cn("absolute mt-1 w-full bg-base-popup border border-base-popup text-base-popup-foreground rounded-md shadow-md z-10 max-h-60 overflow-y-auto", optionContainerClassName), children: [optionsFiltered.map((option) => {
80
+ return (_jsxs("ul", { className: cn("absolute text-text-g-contrast-high mt-1 w-full bg-modal-surface border border-modal-surface text-text-contrast-low rounded-md shadow-md z-10 max-h-60 overflow-y-auto", optionContainerClassName), children: [optionsFiltered.map((option) => {
81
81
  if (option.renderLabel) {
82
82
  return (_jsx(Fragment, { children: option.renderLabel({
83
83
  value: option.value,
@@ -88,8 +88,8 @@ const InputFilter = forwardRef((_a, ref) => {
88
88
  : ""}`,
89
89
  }) }, option.value));
90
90
  }
91
- return (_jsxs("li", { onMouseDown: () => handleOptionClick(option), className: `p-4 typography-subtitle4 hover:bg-primary-hover-bg cursor-pointer flex items-center gap-3 ${selectedOptions.some((selected) => selected.value === option.value)
92
- ? "bg-base-popup-highlight"
91
+ return (_jsxs("li", { onMouseDown: () => handleOptionClick(option), className: `p-4 text-text-g-contrast-high typography-subtitle4 hover:bg-primary-hover-bg cursor-pointer flex items-center gap-3 ${selectedOptions.some((selected) => selected.value === option.value)
92
+ ? "bg-modal-highlight"
93
93
  : ""}`, children: [_jsx(Checkbox, { checked: selectedOptions.some((selected) => selected.value === option.value) }), option.label] }, option.value));
94
94
  }), optionsFiltered.length === 0 && (_jsx("li", { className: "px-4 py-14 text-center text-input-text", children: "Not found" }))] }));
95
95
  };
@@ -119,6 +119,7 @@ const InputFilter = forwardRef((_a, ref) => {
119
119
  active: !!values.length,
120
120
  disabled,
121
121
  });
122
- return (_jsxs("div", { ref: containerRef, className: `relative ${fullwidth ? "w-full" : ""}`, children: [_jsx(TextInput, Object.assign({ hasClearIcon: false, endIcon: _jsx(Icon, { type: "heroicons", name: "adjustments-horizontal", variant: "outline", color: "inherit", stroke: "inherit", fill: "transparent" }), classes: { endIconWrapper: filterIconClassName } }, props, { ref: ref, readOnly: !filterMode, value: textValue, onChange: handleOnChangeText, label: label, placeholder: " ", type: "text", rounded: rounded, variant: variant, helperText: helperText, errorMessage: errorMessage, fullwidth: fullwidth, error: error, required: required, id: _id, disabled: disabled, size: size, className: customInputVariant({ size }), onFocus: handleOnFocus, onKeyDown: handleOnKeyDown })), isFocused && renderOptions()] }));
122
+ const filterIconSizeClassName = size === "lg" ? "size-8" : size === "md" ? "size-[22px]" : "size-[18px]";
123
+ return (_jsxs("div", { ref: containerRef, className: `relative ${fullwidth ? "w-full" : ""}`, children: [_jsx(TextInput, Object.assign({ hasClearIcon: false, endIcon: _jsx(Icon, { type: "heroicons", name: "adjustments-horizontal", variant: "outline", color: "inherit", stroke: "inherit", fill: "transparent", className: filterIconSizeClassName }), classes: { endIconWrapper: filterIconClassName } }, props, { ref: ref, readOnly: !filterMode, value: textValue, onChange: handleOnChangeText, label: label, placeholder: " ", type: "text", rounded: rounded, variant: variant, helperText: helperText, errorMessage: errorMessage, fullwidth: fullwidth, error: error, required: required, id: _id, disabled: disabled, size: size, className: customInputVariant({ size }), onFocus: handleOnFocus, onKeyDown: handleOnKeyDown })), isFocused && renderOptions()] }));
123
124
  });
124
125
  export { InputFilter };
@@ -9,7 +9,7 @@ const meta = {
9
9
  layout: "fullscreen",
10
10
  },
11
11
  decorators: [
12
- (Story) => (_jsx("div", { className: "p-5 flex w-full bg-base-bg2", children: _jsx(Story, {}) })),
12
+ (Story) => (_jsx("div", { className: "p-5 flex w-full bg-bg-bg2", children: _jsx(Story, {}) })),
13
13
  ],
14
14
  };
15
15
  export default meta;
@@ -23,7 +23,7 @@ export const filterIconVariant = cva([
23
23
  },
24
24
  rounded: {
25
25
  none: "rounded-r-none",
26
- normal: "rounded-r-xl",
26
+ normal: "",
27
27
  full: "rounded-r-full",
28
28
  },
29
29
  error: {
@@ -48,12 +48,25 @@ export const filterIconVariant = cva([
48
48
  },
49
49
  disabled: {
50
50
  true: [
51
+ "cursor-default pointer-events-none",
51
52
  "border-l-input-disable-stroke",
52
53
  "fill-input-disable-stroke",
53
54
  "stroke-input-disable-stroke",
54
55
  ],
55
56
  },
56
57
  },
58
+ compoundVariants: [
59
+ {
60
+ rounded: "normal",
61
+ size: "sm",
62
+ className: "rounded-r-sm",
63
+ },
64
+ {
65
+ rounded: "normal",
66
+ size: ["md", "lg"],
67
+ className: "rounded-r-md",
68
+ },
69
+ ],
57
70
  defaultVariants: {
58
71
  size: "md",
59
72
  rounded: "normal",
@@ -1,6 +1,6 @@
1
1
  import { cva } from "class-variance-authority";
2
2
  export const labelVariant = cva([
3
- "block cursor-pointer duration-450 transition-all px-[2px] text-foreground peer-focus:text-input-text-active",
3
+ "block cursor-pointer duration-450 transition-all px-[2px] text-input-default-text peer-focus:text-input-text-active",
4
4
  ], {
5
5
  variants: {
6
6
  size: {
@@ -4,7 +4,7 @@ import { cn } from "@/utils/cn";
4
4
  import Icon from "../Icon/Icon";
5
5
  // ==================== Menu Container ====================
6
6
  export const Menu = forwardRef(({ items, selectedValues = [], onSelect, className, style, isAbove = false }, ref) => {
7
- return (_jsx("div", { ref: ref, className: cn("z-50 min-w-[154px] overflow-hidden rounded-md bg-base-popup text-base-popup-foreground", "border border-base-popup", className), style: Object.assign({ boxShadow: "var(--dropdown-menu-shadow)" }, style), children: items.map((item, index) => {
7
+ return (_jsx("div", { ref: ref, className: cn("z-50 min-w-[154px] overflow-hidden rounded-md bg-modal-surface text-text-g-contrast-high", "border border-modal-surface", className), style: Object.assign({ boxShadow: "var(--dropdown-menu-shadow)" }, style), children: items.map((item, index) => {
8
8
  var _a;
9
9
  if (item.type === "separator") {
10
10
  return _jsx(MenuSeparator, {}, `separator-${index}`);
@@ -57,7 +57,7 @@ export const MenuSeparator = forwardRef(({ className }, ref) => {
57
57
  });
58
58
  MenuSeparator.displayName = "MenuSeparator";
59
59
  export const MenuLabel = forwardRef(({ children, className }, ref) => {
60
- return (_jsx("div", { ref: ref, className: cn("px-3 py-2 typography-small4 text-text-grey-medium", className), children: children }));
60
+ return (_jsx("div", { ref: ref, className: cn("px-3 py-2 typography-small4 text-text-g-contrast-medium", className), children: children }));
61
61
  });
62
62
  MenuLabel.displayName = "MenuLabel";
63
63
  // ==================== Exports ====================
@@ -8,7 +8,7 @@ const meta = {
8
8
  },
9
9
  tags: ["autodocs"],
10
10
  decorators: [
11
- (Story) => (_jsx("div", { className: "p-5 flex h-screen w-full bg-base-bg2", children: _jsx(Story, {}) })),
11
+ (Story) => (_jsx("div", { className: "p-5 flex h-screen w-full bg-bg-bg2", children: _jsx(Story, {}) })),
12
12
  ],
13
13
  argTypes: {
14
14
  size: {
@@ -0,0 +1,118 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { forwardRef, useImperativeHandle, useMemo, useRef, } from "react";
3
+ import { cn } from "@/utils/cn";
4
+ const sanitizeChars = (raw, charPattern, maxLength) => {
5
+ const chars = Array.from(raw).filter((char) => charPattern.test(char));
6
+ return chars.slice(0, maxLength);
7
+ };
8
+ export const OtpInput = forwardRef(({ value, onChange, onBlur, onComplete, length = 6, disabled = false, invalid = false, autoFocus = false, inputMode = "numeric", charPattern = /^\d$/, className, inputClassName, }, ref) => {
9
+ const inputRefs = useRef([]);
10
+ const containerRef = useRef(null);
11
+ const slots = useMemo(() => {
12
+ const normalizedValue = value || "";
13
+ return Array.from({ length }, (_, index) => normalizedValue[index] || "");
14
+ }, [length, value]);
15
+ useImperativeHandle(ref, () => inputRefs.current[0], []);
16
+ const setCode = (nextSlots) => {
17
+ const nextValue = nextSlots.join("");
18
+ onChange(nextValue);
19
+ if (onComplete && nextSlots.every((slot) => slot !== "")) {
20
+ onComplete(nextValue);
21
+ }
22
+ };
23
+ const focusInput = (index) => {
24
+ var _a, _b;
25
+ const clamped = Math.max(0, Math.min(length - 1, index));
26
+ (_a = inputRefs.current[clamped]) === null || _a === void 0 ? void 0 : _a.focus();
27
+ (_b = inputRefs.current[clamped]) === null || _b === void 0 ? void 0 : _b.select();
28
+ };
29
+ const handleInputChange = (index, rawValue) => {
30
+ if (disabled)
31
+ return;
32
+ if (!rawValue) {
33
+ const nextSlots = [...slots];
34
+ nextSlots[index] = "";
35
+ setCode(nextSlots);
36
+ return;
37
+ }
38
+ const incomingChars = sanitizeChars(rawValue, charPattern, length);
39
+ if (!incomingChars.length)
40
+ return;
41
+ const nextSlots = [...slots];
42
+ let cursor = index;
43
+ incomingChars.forEach((char) => {
44
+ if (cursor < length) {
45
+ nextSlots[cursor] = char;
46
+ cursor += 1;
47
+ }
48
+ });
49
+ setCode(nextSlots);
50
+ if (cursor < length) {
51
+ focusInput(cursor);
52
+ }
53
+ else {
54
+ focusInput(length - 1);
55
+ }
56
+ };
57
+ const handlePaste = (index, event) => {
58
+ event.preventDefault();
59
+ if (disabled)
60
+ return;
61
+ const pasted = event.clipboardData.getData("text");
62
+ const incomingChars = sanitizeChars(pasted, charPattern, length - index);
63
+ if (!incomingChars.length)
64
+ return;
65
+ const nextSlots = [...slots];
66
+ incomingChars.forEach((char, offset) => {
67
+ nextSlots[index + offset] = char;
68
+ });
69
+ setCode(nextSlots);
70
+ const nextFocusIndex = Math.min(index + incomingChars.length, length - 1);
71
+ focusInput(nextFocusIndex);
72
+ };
73
+ const handleKeyDown = (index, event) => {
74
+ if (disabled)
75
+ return;
76
+ if (event.key === "ArrowLeft") {
77
+ event.preventDefault();
78
+ focusInput(index - 1);
79
+ return;
80
+ }
81
+ if (event.key === "ArrowRight") {
82
+ event.preventDefault();
83
+ focusInput(index + 1);
84
+ return;
85
+ }
86
+ if (event.key !== "Backspace")
87
+ return;
88
+ if (slots[index]) {
89
+ event.preventDefault();
90
+ const nextSlots = [...slots];
91
+ nextSlots[index] = "";
92
+ setCode(nextSlots);
93
+ return;
94
+ }
95
+ if (index > 0) {
96
+ event.preventDefault();
97
+ const nextSlots = [...slots];
98
+ nextSlots[index - 1] = "";
99
+ setCode(nextSlots);
100
+ focusInput(index - 1);
101
+ }
102
+ };
103
+ return (_jsx("div", { className: cn("flex items-center gap-3", className), ref: containerRef, children: slots.map((slot, index) => (_jsx("input", { ref: (element) => {
104
+ inputRefs.current[index] = element;
105
+ }, type: "text", inputMode: inputMode, autoComplete: index === 0 ? "one-time-code" : "off", value: slot, maxLength: 1, disabled: disabled, autoFocus: autoFocus && index === 0, "aria-invalid": invalid || undefined, className: cn("h-14 w-[46px] rounded-[8px] text-input-filled-text border bg-transparent text-center text-2xl font-semibold outline-none transition-all duration-200", "border-input-default-stroke focus:border-input-active-stroke", "disabled:cursor-not-allowed disabled:opacity-50", invalid && "border-input-error focus:border-input-error", inputClassName), onFocus: (event) => {
106
+ event.target.select();
107
+ }, onBlur: (event) => {
108
+ var _a;
109
+ const nextFocused = event.relatedTarget;
110
+ const stillInside = nextFocused && ((_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.contains(nextFocused));
111
+ // Notify RHF only when focus leaves the whole OTP control.
112
+ if (!stillInside) {
113
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur();
114
+ }
115
+ }, onChange: (event) => handleInputChange(index, event.target.value), onPaste: (event) => handlePaste(index, event), onKeyDown: (event) => handleKeyDown(index, event) }, index))) }));
116
+ });
117
+ OtpInput.displayName = "OtpInput";
118
+ export default OtpInput;
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from "react";
3
+ import * as yup from "yup";
4
+ import Button from "@/components/Button/Button";
5
+ import { Field } from "@/components/Form/Field";
6
+ import { Form } from "@/components/Form/Form";
7
+ import OtpInput from "./OtpInput";
8
+ import { OtpInputGroup } from "./OtpInputGroup";
9
+ const meta = {
10
+ title: "Components/OtpInput",
11
+ tags: ["autodocs"],
12
+ parameters: {
13
+ layout: "centered",
14
+ },
15
+ };
16
+ export default meta;
17
+ export const Basic = {
18
+ render: () => {
19
+ const [value, setValue] = useState("");
20
+ const [completed, setCompleted] = useState("-");
21
+ return (_jsx("div", { className: "w-[420px] max-w-full rounded-md bg-bg-bg2 p-4", children: _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(OtpInput, { value: value, onChange: setValue, onComplete: (code) => setCompleted(code) }), _jsxs("div", { className: "text-xs text-text-g-contrast-medium", children: ["Value: ", value || "-"] }), _jsxs("div", { className: "text-xs text-text-g-contrast-medium", children: ["Last completed: ", completed] })] }) }));
22
+ },
23
+ };
24
+ const otpSchema = yup.object({
25
+ otp: yup
26
+ .string()
27
+ .required("OTP is required")
28
+ .length(6, "OTP must be 6 digits"),
29
+ });
30
+ export const WithFormField = {
31
+ render: () => {
32
+ return (_jsx("div", { className: "w-[420px] max-w-full rounded-md bg-bg-bg2 p-4", children: _jsx(Form, { defaultValues: { otp: "" }, validationSchema: otpSchema, mode: "onTouched", className: "flex flex-col gap-3", onSubmit: (values) => {
33
+ // eslint-disable-next-line no-console
34
+ console.log("OTP submit:", values);
35
+ }, children: ({ formState }) => (_jsxs(_Fragment, { children: [_jsx(Field, { name: "otp", component: OtpInput, componentProps: {
36
+ autoFocus: true,
37
+ }, invalidPropName: "invalid" }), _jsx(Button, { type: "submit", disabled: !formState.isValid || formState.isSubmitting, isLoading: formState.isSubmitting, children: "Verify OTP" })] })) }) }));
38
+ },
39
+ };
40
+ export const GroupWithLabelAndError = {
41
+ render: () => {
42
+ const [value, setValue] = useState("");
43
+ const isInvalid = value.length > 0 && value.length < 6;
44
+ return (_jsx("div", { className: "w-[420px] max-w-full rounded-md bg-bg-bg2 p-4", children: _jsx(OtpInputGroup, { id: "otp-group", label: "Verification code", required: true, value: value, onChange: setValue, helperText: "Enter the 6-digit code from your authenticator app", error: isInvalid, errorMessage: isInvalid ? "OTP must be 6 digits" : undefined, autoFocus: true }) }));
45
+ },
46
+ };
47
+ export const GroupWithFormField = {
48
+ render: () => {
49
+ return (_jsx("div", { className: "w-[420px] max-w-full rounded-md bg-bg-bg2 p-4", children: _jsx(Form, { defaultValues: { otp: "" }, validationSchema: otpSchema, mode: "onTouched", className: "flex flex-col gap-3", onSubmit: (values) => {
50
+ // eslint-disable-next-line no-console
51
+ console.log("OTP group submit:", values);
52
+ }, children: ({ formState }) => (_jsxs(_Fragment, { children: [_jsx(Field, { name: "otp", component: OtpInputGroup, componentProps: {
53
+ id: "otp-field-group",
54
+ label: "Verification code",
55
+ helperText: "Paste is supported",
56
+ required: true,
57
+ autoFocus: true,
58
+ }, invalidPropName: "error" }), _jsx(Button, { type: "submit", disabled: !formState.isValid || formState.isSubmitting, isLoading: formState.isSubmitting, children: "Verify OTP" })] })) }) }));
59
+ },
60
+ };