@rovula/ui 0.1.6 → 0.1.8

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 (232) hide show
  1. package/dist/cjs/bundle.css +630 -467
  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 +17 -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 +351 -267
  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/Switch/Switch.styles.js +1 -1
  72. package/dist/components/Table/Table.js +5 -5
  73. package/dist/components/Tabs/Tabs.js +12 -9
  74. package/dist/components/Tabs/Tabs.stories.js +1 -1
  75. package/dist/components/Text/Text.js +1 -1
  76. package/dist/components/Text/Text.stories.js +1 -1
  77. package/dist/components/TextArea/TextArea.stories.js +1 -1
  78. package/dist/components/TextArea/TextArea.styles.js +3 -3
  79. package/dist/components/TextInput/TextInput.js +3 -2
  80. package/dist/components/TextInput/TextInput.stories.js +3 -3
  81. package/dist/components/TextInput/TextInput.styles.js +41 -19
  82. package/dist/components/Toast/Toast.js +4 -2
  83. package/dist/components/Toast/Toast.stories.js +1 -1
  84. package/dist/components/Toast/Toast.styles.js +4 -4
  85. package/dist/components/Toast/Toaster.js +2 -2
  86. package/dist/components/Tree/Tree.stories.js +1 -1
  87. package/dist/components/Tree/TreeItem.js +1 -1
  88. package/dist/esm/bundle.css +630 -467
  89. package/dist/esm/bundle.js +1545 -1545
  90. package/dist/esm/bundle.js.map +1 -1
  91. package/dist/esm/types/components/AlertDialog/AlertDialog.stories.d.ts +3 -0
  92. package/dist/esm/types/components/Dialog/Dialog.d.ts +7 -1
  93. package/dist/esm/types/components/Dialog/Dialog.stories.d.ts +3 -0
  94. package/dist/esm/types/components/Dropdown/Dropdown.d.ts +2 -0
  95. package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +2 -0
  96. package/dist/esm/types/components/Form/Field.d.ts +26 -0
  97. package/dist/esm/types/components/Form/FieldMessage.d.ts +7 -0
  98. package/dist/esm/types/components/Form/Form.d.ts +49 -11
  99. package/dist/esm/types/components/Form/Form.stories.d.ts +23 -0
  100. package/dist/esm/types/components/Form/ValidationHintList.d.ts +17 -0
  101. package/dist/esm/types/components/Form/ValidationHintList.stories.d.ts +9 -0
  102. package/dist/esm/types/components/Form/index.d.ts +10 -0
  103. package/dist/esm/types/components/Form/useOptionBridge.d.ts +17 -0
  104. package/dist/esm/types/components/OtpInput/OtpInput.d.ts +17 -0
  105. package/dist/esm/types/components/OtpInput/OtpInput.stories.d.ts +15 -0
  106. package/dist/esm/types/components/OtpInput/OtpInputGroup.d.ts +25 -0
  107. package/dist/esm/types/components/OtpInput/index.d.ts +5 -0
  108. package/dist/esm/types/components/TextInput/TextInput.styles.d.ts +3 -0
  109. package/dist/esm/types/index.d.ts +5 -0
  110. package/dist/esm/types/theme/ThemeColorCoverageRuntime.stories.d.ts +10 -0
  111. package/dist/esm/types/utils/colors.d.ts +351 -267
  112. package/dist/index.d.ts +512 -269
  113. package/dist/index.js +3 -0
  114. package/dist/src/theme/global.css +2739 -2681
  115. package/dist/theme/ThemeColorCoverageRuntime.stories.js +91 -0
  116. package/dist/utils/colors.js +359 -267
  117. package/package.json +4 -2
  118. package/src/components/ActionButton/ActionButton.stories.tsx +6 -6
  119. package/src/components/ActionButton/ActionButton.styles.ts +1 -1
  120. package/src/components/AlertDialog/AlertDialog.stories.tsx +22 -0
  121. package/src/components/AlertDialog/AlertDialog.tsx +6 -6
  122. package/src/components/Avatar/Avatar.stories.tsx +1 -1
  123. package/src/components/Avatar/Avatar.styles.ts +1 -1
  124. package/src/components/Avatar/AvatarBase.tsx +1 -1
  125. package/src/components/Avatar/AvatarGroup.stories.tsx +1 -1
  126. package/src/components/Button/Buttons.stories.tsx +25 -17
  127. package/src/components/Calendar/Calendar.tsx +3 -3
  128. package/src/components/Checkbox/Checkbox.stories.tsx +35 -12
  129. package/src/components/Checkbox/Checkbox.tsx +7 -5
  130. package/src/components/Collapsible/Collapsible.styles.ts +1 -1
  131. package/src/components/DataTable/DataTable.tsx +2 -2
  132. package/src/components/Dialog/Dialog.stories.tsx +173 -0
  133. package/src/components/Dialog/Dialog.tsx +32 -15
  134. package/src/components/Dropdown/Dropdown.styles.ts +1 -1
  135. package/src/components/Dropdown/Dropdown.tsx +16 -14
  136. package/src/components/DropdownMenu/DropdownMenu.tsx +3 -3
  137. package/src/components/FocusedScrollView/FocusedScrollView.stories.tsx +10 -10
  138. package/src/components/Form/Field.tsx +160 -0
  139. package/src/components/Form/FieldMessage.tsx +38 -0
  140. package/src/components/Form/Form.docs.mdx +67 -0
  141. package/src/components/Form/Form.stories.tsx +490 -0
  142. package/src/components/Form/Form.tsx +185 -87
  143. package/src/components/Form/README.md +284 -0
  144. package/src/components/Form/ValidationHintList.stories.tsx +118 -0
  145. package/src/components/Form/ValidationHintList.tsx +82 -0
  146. package/src/components/Form/index.ts +28 -0
  147. package/src/components/Form/useOptionBridge.ts +55 -0
  148. package/src/components/InputFilter/InputFilter.stories.tsx +1 -1
  149. package/src/components/InputFilter/InputFilter.styles.ts +14 -1
  150. package/src/components/InputFilter/InputFilter.tsx +33 -28
  151. package/src/components/Label/Label.styles.ts +2 -2
  152. package/src/components/Label/Label.tsx +1 -1
  153. package/src/components/Menu/Menu.tsx +12 -12
  154. package/src/components/NumberInput/NumberInput.stories.tsx +1 -1
  155. package/src/components/OtpInput/OtpInput.stories.tsx +168 -0
  156. package/src/components/OtpInput/OtpInput.tsx +210 -0
  157. package/src/components/OtpInput/OtpInputGroup.tsx +74 -0
  158. package/src/components/OtpInput/index.ts +5 -0
  159. package/src/components/PasswordInput/PasswordInput.stories.tsx +1 -1
  160. package/src/components/Popover/Popover.tsx +1 -1
  161. package/src/components/RadioGroup/RadioGroup.stories.tsx +4 -4
  162. package/src/components/RadioGroup/RadioGroup.tsx +2 -1
  163. package/src/components/Search/Search.stories.tsx +1 -1
  164. package/src/components/Search/Search.tsx +6 -2
  165. package/src/components/Slider/Slider.stories.tsx +7 -7
  166. package/src/components/Slider/Slider.tsx +1 -1
  167. package/src/components/Switch/Switch.stories.tsx +4 -4
  168. package/src/components/Switch/Switch.styles.ts +1 -1
  169. package/src/components/Table/Table.tsx +5 -5
  170. package/src/components/Tabs/Tabs.stories.tsx +1 -1
  171. package/src/components/Tabs/Tabs.tsx +29 -18
  172. package/src/components/Text/Text.stories.tsx +1 -1
  173. package/src/components/Text/Text.tsx +1 -1
  174. package/src/components/TextArea/TextArea.stories.tsx +1 -1
  175. package/src/components/TextArea/TextArea.styles.ts +3 -3
  176. package/src/components/TextInput/TextInput.stories.tsx +7 -7
  177. package/src/components/TextInput/TextInput.styles.ts +42 -19
  178. package/src/components/TextInput/TextInput.tsx +3 -1
  179. package/src/components/Toast/Toast.stories.tsx +1 -1
  180. package/src/components/Toast/Toast.styles.tsx +7 -7
  181. package/src/components/Toast/Toast.tsx +5 -4
  182. package/src/components/Toast/Toaster.tsx +17 -20
  183. package/src/components/Tree/Tree.stories.tsx +1 -1
  184. package/src/components/Tree/TreeItem.tsx +1 -1
  185. package/src/index.ts +5 -0
  186. package/src/theme/THEME_MAPPING.md +36 -37
  187. package/src/theme/ThemeColorCoverageRuntime.stories.tsx +236 -0
  188. package/src/theme/direct-token-migration-plan.md +121 -0
  189. package/src/theme/figma-mcp-check-report.md +225 -0
  190. package/src/theme/figma-mcp-component-checklist.json +1250 -0
  191. package/src/theme/global.css +7 -3
  192. package/src/theme/presets/colors.js +173 -64
  193. package/src/theme/themes/skyller/baseline.css +0 -4
  194. package/src/theme/themes/variable-mapping.css +1064 -0
  195. package/src/theme/themes/variable.css +248 -230
  196. package/src/theme/themes/xspector/baseline.css +0 -4
  197. package/src/theme/themes/xspector/components/dropdown-menu.css +4 -4
  198. package/src/theme/themes/xspector/components/loading.css +2 -2
  199. package/src/theme/tokens/baseline.css +0 -3
  200. package/src/theme/tokens/color.css +36 -65
  201. package/src/theme/tokens/components/action-button.css +6 -6
  202. package/src/theme/tokens/components/button.css +189 -189
  203. package/src/theme/tokens/components/dropdown-menu.css +5 -5
  204. package/src/theme/tokens/components/footer.css +1 -1
  205. package/src/theme/tokens/components/loading.css +2 -2
  206. package/src/theme/tokens/components/switch.css +11 -11
  207. package/src/theme/tokens/typography.css +28 -28
  208. package/src/theme/tokens_old/baseline.css +13 -0
  209. package/src/theme/tokens_old/color.css +78 -0
  210. package/src/theme/tokens_old/components/action-button.css +127 -0
  211. package/src/theme/tokens_old/components/button.css +512 -0
  212. package/src/theme/tokens_old/components/dropdown-menu.css +27 -0
  213. package/src/theme/tokens_old/components/footer.css +9 -0
  214. package/src/theme/tokens_old/components/loading.css +11 -0
  215. package/src/theme/tokens_old/components/navbar.css +9 -0
  216. package/src/theme/tokens_old/components/progress-bar.css +8 -0
  217. package/src/theme/tokens_old/components/switch.css +29 -0
  218. package/src/theme/tokens_old/typography.css +199 -0
  219. package/src/theme/tokens_old/variables.css +28 -0
  220. package/src/theme/utils.js +172 -33
  221. package/src/utils/colors.ts +367 -278
  222. package/src/theme/themes/skyller/color.css +0 -79
  223. package/src/theme/themes/skyller/palette.css +0 -143
  224. package/src/theme/themes/skyller/state.css +0 -94
  225. package/src/theme/themes/skyller/transparent.css +0 -94
  226. package/src/theme/themes/xspector/color.css +0 -83
  227. package/src/theme/themes/xspector/palette.css +0 -142
  228. package/src/theme/themes/xspector/state.css +0 -94
  229. package/src/theme/themes/xspector/transparent.css +0 -93
  230. /package/src/theme/{tokens → tokens_old}/palette.css +0 -0
  231. /package/src/theme/{tokens → tokens_old}/state.css +0 -0
  232. /package/src/theme/{tokens → tokens_old}/transparent.css +0 -0
@@ -0,0 +1,82 @@
1
+ import React from "react";
2
+ import { CheckCircleIcon } from "@heroicons/react/24/outline";
3
+ import { cn } from "@/utils/cn";
4
+
5
+ export type ValidationHintState = "pending" | "valid" | "invalid";
6
+ export type ValidationHintMode = ValidationHintState[];
7
+
8
+ export type ValidationHintRule<TValues> = {
9
+ id: string;
10
+ label: string;
11
+ validate: (values: TValues) => boolean;
12
+ when?: (values: TValues) => boolean;
13
+ };
14
+
15
+ export type ValidationHintListProps<TValues> = {
16
+ values: TValues;
17
+ rules: ValidationHintRule<TValues>[];
18
+ mode?: ValidationHintMode;
19
+ className?: string;
20
+ itemClassName?: string;
21
+ };
22
+
23
+ const resolveHintState = <TValues,>(
24
+ values: TValues,
25
+ rule: ValidationHintRule<TValues>,
26
+ ): ValidationHintState => {
27
+ const shouldEvaluate = rule.when ? rule.when(values) : true;
28
+ if (!shouldEvaluate) return "pending";
29
+
30
+ return rule.validate(values) ? "valid" : "invalid";
31
+ };
32
+
33
+ const hintTextStateClass: Record<ValidationHintState, string> = {
34
+ valid: "text-success",
35
+ invalid: "text-input-error",
36
+ pending: "text-text-g-contrast-medium",
37
+ };
38
+
39
+ const hintIconStateClass: Record<ValidationHintState, string> = {
40
+ valid: "opacity-100",
41
+ invalid: "opacity-100",
42
+ pending: "opacity-40",
43
+ };
44
+
45
+ export const ValidationHintList = <TValues,>({
46
+ values,
47
+ rules,
48
+ mode = ["pending", "valid", "invalid"],
49
+ className,
50
+ itemClassName,
51
+ }: ValidationHintListProps<TValues>) => {
52
+ const enabledStates = new Set<ValidationHintState>(mode);
53
+
54
+ return (
55
+ <ul className={cn("mt-2 flex flex-col gap-3", className)}>
56
+ {rules.map((rule) => {
57
+ const state = resolveHintState(values, rule);
58
+ const normalizedState: ValidationHintState = enabledStates.has(state)
59
+ ? state
60
+ : "pending";
61
+
62
+ return (
63
+ <li
64
+ key={rule.id}
65
+ className={cn(
66
+ "flex items-center gap-2 typography-small2",
67
+ hintTextStateClass[normalizedState],
68
+ itemClassName,
69
+ )}
70
+ >
71
+ <CheckCircleIcon
72
+ className={cn("size-4", hintIconStateClass[normalizedState])}
73
+ />
74
+ <span>{rule.label}</span>
75
+ </li>
76
+ );
77
+ })}
78
+ </ul>
79
+ );
80
+ };
81
+
82
+ export default ValidationHintList;
@@ -0,0 +1,28 @@
1
+ export {
2
+ Form,
3
+ createControlledForm,
4
+ createYupResolver,
5
+ useControlledForm,
6
+ } from "./Form";
7
+ export type {
8
+ ControlledFormFactoryOptions,
9
+ FormController,
10
+ FormProps,
11
+ UseControlledFormOptions,
12
+ } from "./Form";
13
+ export { Field } from "./Field";
14
+ export type { FieldProps } from "./Field";
15
+ export { FieldMessage } from "./FieldMessage";
16
+ export type { FieldMessageProps } from "./FieldMessage";
17
+ export { ValidationHintList } from "./ValidationHintList";
18
+ export type {
19
+ ValidationHintListProps,
20
+ ValidationHintMode,
21
+ ValidationHintRule,
22
+ ValidationHintState,
23
+ } from "./ValidationHintList";
24
+ export { useOptionBridge } from "./useOptionBridge";
25
+ export type {
26
+ OptionLike,
27
+ UseOptionBridgeOptions,
28
+ } from "./useOptionBridge";
@@ -0,0 +1,55 @@
1
+ import { useMemo } from "react";
2
+
3
+ type OptionValue = string | number;
4
+
5
+ export type OptionLike<TValue extends OptionValue = string> = {
6
+ value: TValue;
7
+ label?: string;
8
+ };
9
+
10
+ export type UseOptionBridgeOptions<
11
+ TValue extends OptionValue,
12
+ TOption extends OptionLike<TValue>,
13
+ > = {
14
+ options: TOption[];
15
+ loading?: boolean;
16
+ buildFallbackOption?: (value: TValue, loading: boolean) => TOption;
17
+ };
18
+
19
+ export const useOptionBridge = <
20
+ TValue extends OptionValue = string,
21
+ TOption extends OptionLike<TValue> = OptionLike<TValue>,
22
+ >({
23
+ options,
24
+ loading = false,
25
+ buildFallbackOption,
26
+ }: UseOptionBridgeOptions<TValue, TOption>) => {
27
+ const optionsByValue = useMemo(() => {
28
+ return new Map<TValue, TOption>(
29
+ options.map((option) => [option.value as TValue, option])
30
+ );
31
+ }, [options]);
32
+
33
+ const toOption = (value: TValue | null | undefined) => {
34
+ if (value === null || value === undefined) return undefined;
35
+ return optionsByValue.get(value);
36
+ };
37
+
38
+ const toValue = (option: TOption | null | undefined) => {
39
+ return option?.value;
40
+ };
41
+
42
+ const toOptionWithFallback = (value: TValue | null | undefined) => {
43
+ if (value === null || value === undefined) return undefined;
44
+ return toOption(value) ?? buildFallbackOption?.(value, loading);
45
+ };
46
+
47
+ return {
48
+ toOption,
49
+ toOptionWithFallback,
50
+ toValue,
51
+ optionsByValue,
52
+ };
53
+ };
54
+
55
+ export default useOptionBridge;
@@ -11,7 +11,7 @@ const meta = {
11
11
  },
12
12
  decorators: [
13
13
  (Story) => (
14
- <div className="p-5 flex w-full bg-base-bg2">
14
+ <div className="p-5 flex w-full bg-bg-bg2">
15
15
  <Story />
16
16
  </div>
17
17
  ),
@@ -30,7 +30,7 @@ export const filterIconVariant = cva(
30
30
  },
31
31
  rounded: {
32
32
  none: "rounded-r-none",
33
- normal: "rounded-r-xl",
33
+ normal: "",
34
34
  full: "rounded-r-full",
35
35
  },
36
36
  error: {
@@ -56,12 +56,25 @@ export const filterIconVariant = cva(
56
56
  },
57
57
  disabled: {
58
58
  true: [
59
+ "cursor-default pointer-events-none",
59
60
  "border-l-input-disable-stroke",
60
61
  "fill-input-disable-stroke",
61
62
  "stroke-input-disable-stroke",
62
63
  ],
63
64
  },
64
65
  },
66
+ compoundVariants: [
67
+ {
68
+ rounded: "normal",
69
+ size: "sm",
70
+ className: "rounded-r-sm",
71
+ },
72
+ {
73
+ rounded: "normal",
74
+ size: ["md", "lg"],
75
+ className: "rounded-r-md",
76
+ },
77
+ ],
65
78
  defaultVariants: {
66
79
  size: "md",
67
80
  rounded: "normal",
@@ -78,7 +78,7 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
78
78
  optionContainerClassName,
79
79
  ...props
80
80
  },
81
- ref
81
+ ref,
82
82
  ) => {
83
83
  const _id = id || `${label}-select`;
84
84
 
@@ -117,19 +117,19 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
117
117
  clearMismatchValues(event as any);
118
118
  }
119
119
  },
120
- [onChangeText]
120
+ [onChangeText],
121
121
  );
122
122
 
123
123
  const handleOptionClick = useCallback(
124
124
  (option: Options) => {
125
125
  const isSelected = selectedOptions.some(
126
- (selected) => selected.value === option.value
126
+ (selected) => selected.value === option.value,
127
127
  );
128
128
  let newSelectedOptions = [...selectedOptions];
129
129
 
130
130
  if (isSelected) {
131
131
  newSelectedOptions = newSelectedOptions.filter(
132
- (selected) => selected.value !== option.value
132
+ (selected) => selected.value !== option.value,
133
133
  );
134
134
  } else {
135
135
  newSelectedOptions.push(option);
@@ -137,11 +137,11 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
137
137
 
138
138
  setSelectedOptions(newSelectedOptions);
139
139
  setTextValue(
140
- newSelectedOptions.map((option) => option.label).join(", ")
140
+ newSelectedOptions.map((option) => option.label).join(", "),
141
141
  );
142
142
  onSelect?.(newSelectedOptions);
143
143
  },
144
- [selectedOptions, onSelect]
144
+ [selectedOptions, onSelect],
145
145
  );
146
146
 
147
147
  const optionsFiltered = useMemo(() => {
@@ -151,7 +151,7 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
151
151
  return options.filter(
152
152
  (option) =>
153
153
  !filterMode ||
154
- option.label?.toLowerCase().includes(filterText?.toLowerCase())
154
+ option.label?.toLowerCase().includes(filterText?.toLowerCase()),
155
155
  );
156
156
  }, [options, filterMode, textValue]);
157
157
 
@@ -167,8 +167,8 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
167
167
  return (
168
168
  <ul
169
169
  className={cn(
170
- "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",
171
- optionContainerClassName
170
+ "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",
171
+ optionContainerClassName,
172
172
  )}
173
173
  >
174
174
  {optionsFiltered.map((option) => {
@@ -179,12 +179,13 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
179
179
  value: option.value,
180
180
  label: option.label,
181
181
  handleOnClick: () => handleOptionClick(option),
182
- className: `p-4 typography-subtitle4 hover:bg-gray-100 cursor-pointer flex items-center gap-3 ${selectedOptions.some(
183
- (selected) => selected.value === option.value
184
- )
185
- ? "bg-gray-200"
186
- : ""
187
- }`,
182
+ className: `p-4 typography-subtitle4 hover:bg-gray-100 cursor-pointer flex items-center gap-3 ${
183
+ selectedOptions.some(
184
+ (selected) => selected.value === option.value,
185
+ )
186
+ ? "bg-gray-200"
187
+ : ""
188
+ }`,
188
189
  })}
189
190
  </Fragment>
190
191
  );
@@ -193,16 +194,17 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
193
194
  <li
194
195
  key={option.value}
195
196
  onMouseDown={() => handleOptionClick(option)}
196
- className={`p-4 typography-subtitle4 hover:bg-primary-hover-bg cursor-pointer flex items-center gap-3 ${selectedOptions.some(
197
- (selected) => selected.value === option.value
198
- )
199
- ? "bg-base-popup-highlight"
200
- : ""
201
- }`}
197
+ className={`p-4 text-text-g-contrast-high typography-subtitle4 hover:bg-primary-hover-bg cursor-pointer flex items-center gap-3 ${
198
+ selectedOptions.some(
199
+ (selected) => selected.value === option.value,
200
+ )
201
+ ? "bg-modal-highlight"
202
+ : ""
203
+ }`}
202
204
  >
203
205
  <Checkbox
204
206
  checked={selectedOptions.some(
205
- (selected) => selected.value === option.value
207
+ (selected) => selected.value === option.value,
206
208
  )}
207
209
  />
208
210
  {option.label}
@@ -223,14 +225,14 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
223
225
  setIsFocused(true);
224
226
  props?.onFocus?.(e);
225
227
  },
226
- [props?.onFocus]
228
+ [props?.onFocus],
227
229
  );
228
230
 
229
231
  const clearMismatchValues = useCallback(
230
232
  (e: React.FocusEvent<HTMLInputElement, Element>) => {
231
233
  const matchSelectedValues = optionsFiltered.filter(
232
234
  (opt) =>
233
- opt.value === e.target?.value || opt.label === e.target?.value
235
+ opt.value === e.target?.value || opt.label === e.target?.value,
234
236
  );
235
237
 
236
238
  if (keyCode.current === "Enter") {
@@ -239,11 +241,11 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
239
241
 
240
242
  setSelectedOptions(matchSelectedValues);
241
243
  setTextValue(
242
- matchSelectedValues.map((option) => option.label).join(", ")
244
+ matchSelectedValues.map((option) => option.label).join(", "),
243
245
  );
244
246
  onSelect?.(matchSelectedValues);
245
247
  },
246
- [optionsFiltered, textValue]
248
+ [optionsFiltered, textValue],
247
249
  );
248
250
 
249
251
  const handleOnKeyDown = useCallback(
@@ -251,7 +253,7 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
251
253
  keyCode.current = e.code;
252
254
  props?.onKeyDown?.(e);
253
255
  },
254
- [props?.onKeyDown]
256
+ [props?.onKeyDown],
255
257
  );
256
258
 
257
259
  const filterIconClassName = filterIconVariant({
@@ -261,6 +263,8 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
261
263
  active: !!values.length,
262
264
  disabled,
263
265
  });
266
+ const filterIconSizeClassName =
267
+ size === "lg" ? "size-8" : size === "md" ? "size-[22px]" : "size-[18px]";
264
268
 
265
269
  return (
266
270
  <div
@@ -277,6 +281,7 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
277
281
  color="inherit"
278
282
  stroke="inherit"
279
283
  fill="transparent"
284
+ className={filterIconSizeClassName}
280
285
  />
281
286
  }
282
287
  classes={{ endIconWrapper: filterIconClassName }}
@@ -305,7 +310,7 @@ const InputFilter = forwardRef<HTMLInputElement, InputFilterProps>(
305
310
  {isFocused && renderOptions()}
306
311
  </div>
307
312
  );
308
- }
313
+ },
309
314
  );
310
315
 
311
316
  export { InputFilter };
@@ -2,7 +2,7 @@ import { cva } from "class-variance-authority";
2
2
 
3
3
  export const labelVariant = cva(
4
4
  [
5
- "block cursor-pointer duration-450 transition-all px-[2px] text-foreground peer-focus:text-input-text-active",
5
+ "block cursor-pointer duration-450 transition-all px-[2px] text-input-default-text peer-focus:text-input-text-active",
6
6
  ],
7
7
  {
8
8
  variants: {
@@ -57,5 +57,5 @@ export const labelVariant = cva(
57
57
  error: false,
58
58
  isFloatable: false,
59
59
  },
60
- }
60
+ },
61
61
  );
@@ -21,7 +21,7 @@ const Label = React.forwardRef<
21
21
  error: props.error,
22
22
  isFloatable: props.isFloatable,
23
23
  }),
24
- className
24
+ className,
25
25
  )}
26
26
  {...props}
27
27
  />
@@ -56,15 +56,15 @@ export type MenuProps = {
56
56
  export const Menu = forwardRef<HTMLDivElement, MenuProps>(
57
57
  (
58
58
  { items, selectedValues = [], onSelect, className, style, isAbove = false },
59
- ref
59
+ ref,
60
60
  ) => {
61
61
  return (
62
62
  <div
63
63
  ref={ref}
64
64
  className={cn(
65
- "z-50 min-w-[154px] overflow-hidden rounded-md bg-base-popup text-base-popup-foreground",
66
- "border border-base-popup",
67
- className
65
+ "z-50 min-w-[154px] overflow-hidden rounded-md bg-modal-surface text-text-g-contrast-high",
66
+ "border border-modal-surface",
67
+ className,
68
68
  )}
69
69
  style={{
70
70
  boxShadow: "var(--dropdown-menu-shadow)",
@@ -115,7 +115,7 @@ export const Menu = forwardRef<HTMLDivElement, MenuProps>(
115
115
  })}
116
116
  </div>
117
117
  );
118
- }
118
+ },
119
119
  );
120
120
 
121
121
  Menu.displayName = "Menu";
@@ -169,7 +169,7 @@ export const MenuItem = forwardRef<HTMLDivElement, MenuItemProps>(
169
169
  "pointer-events-none opacity-50": option.disabled,
170
170
  "text-red-500": option.danger,
171
171
  },
172
- className
172
+ className,
173
173
  )}
174
174
  onMouseDown={option.disabled ? undefined : onSelect}
175
175
  >
@@ -178,7 +178,7 @@ export const MenuItem = forwardRef<HTMLDivElement, MenuItemProps>(
178
178
  {option.label}
179
179
  </div>
180
180
  );
181
- }
181
+ },
182
182
  );
183
183
 
184
184
  MenuItem.displayName = "MenuItem";
@@ -196,11 +196,11 @@ export const MenuSeparator = forwardRef<HTMLDivElement, MenuSeparatorProps>(
196
196
  ref={ref}
197
197
  className={cn(
198
198
  "-mx-2 my-2 h-px bg-[var(--dropdown-menu-seperator-bg)]",
199
- className
199
+ className,
200
200
  )}
201
201
  />
202
202
  );
203
- }
203
+ },
204
204
  );
205
205
 
206
206
  MenuSeparator.displayName = "MenuSeparator";
@@ -218,14 +218,14 @@ export const MenuLabel = forwardRef<HTMLDivElement, MenuLabelProps>(
218
218
  <div
219
219
  ref={ref}
220
220
  className={cn(
221
- "px-3 py-2 typography-small4 text-text-grey-medium",
222
- className
221
+ "px-3 py-2 typography-small4 text-text-g-contrast-medium",
222
+ className,
223
223
  )}
224
224
  >
225
225
  {children}
226
226
  </div>
227
227
  );
228
- }
228
+ },
229
229
  );
230
230
 
231
231
  MenuLabel.displayName = "MenuLabel";
@@ -11,7 +11,7 @@ const meta: Meta<typeof NumberInput> = {
11
11
  tags: ["autodocs"],
12
12
  decorators: [
13
13
  (Story) => (
14
- <div className="p-5 flex h-screen w-full bg-base-bg2">
14
+ <div className="p-5 flex h-screen w-full bg-bg-bg2">
15
15
  <Story />
16
16
  </div>
17
17
  ),
@@ -0,0 +1,168 @@
1
+ import React, { useState } from "react";
2
+ import type { Meta, StoryObj } from "@storybook/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
+
10
+ const meta: Meta = {
11
+ title: "Components/OtpInput",
12
+ tags: ["autodocs"],
13
+ parameters: {
14
+ layout: "centered",
15
+ },
16
+ };
17
+
18
+ export default meta;
19
+ type Story = StoryObj;
20
+
21
+ export const Basic = {
22
+ render: () => {
23
+ const [value, setValue] = useState("");
24
+ const [completed, setCompleted] = useState("-");
25
+
26
+ return (
27
+ <div className="w-[420px] max-w-full rounded-md bg-bg-bg2 p-4">
28
+ <div className="flex flex-col gap-3">
29
+ <OtpInput
30
+ value={value}
31
+ onChange={setValue}
32
+ onComplete={(code) => setCompleted(code)}
33
+ />
34
+ <div className="text-xs text-text-g-contrast-medium">
35
+ Value: {value || "-"}
36
+ </div>
37
+ <div className="text-xs text-text-g-contrast-medium">
38
+ Last completed: {completed}
39
+ </div>
40
+ </div>
41
+ </div>
42
+ );
43
+ },
44
+ } satisfies Story;
45
+
46
+ type OtpFormValues = {
47
+ otp: string;
48
+ };
49
+
50
+ const otpSchema = yup.object({
51
+ otp: yup
52
+ .string()
53
+ .required("OTP is required")
54
+ .length(6, "OTP must be 6 digits"),
55
+ });
56
+
57
+ export const WithFormField = {
58
+ render: () => {
59
+ return (
60
+ <div className="w-[420px] max-w-full rounded-md bg-bg-bg2 p-4">
61
+ <Form<OtpFormValues>
62
+ defaultValues={{ otp: "" }}
63
+ validationSchema={otpSchema}
64
+ mode="onTouched"
65
+ className="flex flex-col gap-3"
66
+ onSubmit={(values) => {
67
+ // eslint-disable-next-line no-console
68
+ console.log("OTP submit:", values);
69
+ }}
70
+ >
71
+ {({ formState }) => (
72
+ <>
73
+ <Field<
74
+ OtpFormValues,
75
+ "otp",
76
+ React.ComponentProps<typeof OtpInput>
77
+ >
78
+ name="otp"
79
+ component={OtpInput}
80
+ componentProps={{
81
+ autoFocus: true,
82
+ }}
83
+ invalidPropName="invalid"
84
+ />
85
+ <Button
86
+ type="submit"
87
+ disabled={!formState.isValid || formState.isSubmitting}
88
+ isLoading={formState.isSubmitting}
89
+ >
90
+ Verify OTP
91
+ </Button>
92
+ </>
93
+ )}
94
+ </Form>
95
+ </div>
96
+ );
97
+ },
98
+ } satisfies Story;
99
+
100
+ export const GroupWithLabelAndError = {
101
+ render: () => {
102
+ const [value, setValue] = useState("");
103
+ const isInvalid = value.length > 0 && value.length < 6;
104
+
105
+ return (
106
+ <div className="w-[420px] max-w-full rounded-md bg-bg-bg2 p-4">
107
+ <OtpInputGroup
108
+ id="otp-group"
109
+ label="Verification code"
110
+ required
111
+ value={value}
112
+ onChange={setValue}
113
+ helperText="Enter the 6-digit code from your authenticator app"
114
+ error={isInvalid}
115
+ errorMessage={isInvalid ? "OTP must be 6 digits" : undefined}
116
+ autoFocus
117
+ />
118
+ </div>
119
+ );
120
+ },
121
+ } satisfies Story;
122
+
123
+ export const GroupWithFormField = {
124
+ render: () => {
125
+ return (
126
+ <div className="w-[420px] max-w-full rounded-md bg-bg-bg2 p-4">
127
+ <Form<OtpFormValues>
128
+ defaultValues={{ otp: "" }}
129
+ validationSchema={otpSchema}
130
+ mode="onTouched"
131
+ className="flex flex-col gap-3"
132
+ onSubmit={(values) => {
133
+ // eslint-disable-next-line no-console
134
+ console.log("OTP group submit:", values);
135
+ }}
136
+ >
137
+ {({ formState }) => (
138
+ <>
139
+ <Field<
140
+ OtpFormValues,
141
+ "otp",
142
+ React.ComponentProps<typeof OtpInputGroup>
143
+ >
144
+ name="otp"
145
+ component={OtpInputGroup}
146
+ componentProps={{
147
+ id: "otp-field-group",
148
+ label: "Verification code",
149
+ helperText: "Paste is supported",
150
+ required: true,
151
+ autoFocus: true,
152
+ }}
153
+ invalidPropName="error"
154
+ />
155
+ <Button
156
+ type="submit"
157
+ disabled={!formState.isValid || formState.isSubmitting}
158
+ isLoading={formState.isSubmitting}
159
+ >
160
+ Verify OTP
161
+ </Button>
162
+ </>
163
+ )}
164
+ </Form>
165
+ </div>
166
+ );
167
+ },
168
+ } satisfies Story;