@scbt-ecom/ui 0.4.1 → 0.4.2

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 (299) hide show
  1. package/.env +3 -0
  2. package/.github/workflows/publish.yml +61 -0
  3. package/.github/workflows/setup-node/action.yml +22 -0
  4. package/.husky/pre-commit +1 -0
  5. package/.prettierignore +1 -0
  6. package/.prettierrc +20 -0
  7. package/.releaserc +18 -0
  8. package/.storybook/main.ts +44 -0
  9. package/.storybook/preview.tsx +37 -0
  10. package/chromatic.config.json +5 -0
  11. package/eslint.config.mjs +193 -0
  12. package/index.html +13 -0
  13. package/lib/client.ts +12 -0
  14. package/lib/configs/index.ts +2 -0
  15. package/lib/configs/tailwindConfigBase.ts +110 -0
  16. package/lib/configs/tailwindPresets/extendsPreset.ts +43 -0
  17. package/lib/configs/tailwindPresets/index.ts +2 -0
  18. package/lib/configs/tailwindPresets/resetPreset.ts +71 -0
  19. package/lib/hybrid.ts +25 -0
  20. package/lib/shared/constants/api.ts +2 -0
  21. package/lib/shared/constants/designSystem/colors.ts +121 -0
  22. package/lib/shared/constants/designSystem/index.ts +3 -0
  23. package/lib/shared/constants/designSystem/others.ts +30 -0
  24. package/lib/shared/constants/designSystem/typography.ts +88 -0
  25. package/lib/shared/constants/index.ts +2 -0
  26. package/lib/shared/hooks/index.ts +5 -0
  27. package/lib/shared/hooks/useBoolean.ts +12 -0
  28. package/lib/shared/hooks/useClickOutside.ts +22 -0
  29. package/lib/shared/hooks/useCombineRef.ts +23 -0
  30. package/lib/shared/hooks/useControlledForm.ts +16 -0
  31. package/lib/shared/hooks/useDebounce.ts +38 -0
  32. package/lib/shared/hooks/useMediaQuery.tsx +42 -0
  33. package/lib/shared/style.css +118 -0
  34. package/lib/shared/ui/Badge.tsx +20 -0
  35. package/lib/shared/ui/Breadcrumbs.tsx +57 -0
  36. package/lib/shared/ui/ButtonIcon.tsx +50 -0
  37. package/lib/shared/ui/CustomLink.tsx +76 -0
  38. package/lib/shared/ui/Document.tsx +51 -0
  39. package/lib/shared/ui/Heading.tsx +33 -0
  40. package/lib/shared/ui/Hint.tsx +72 -0
  41. package/lib/shared/ui/Loader.tsx +58 -0
  42. package/lib/shared/ui/PhoneView.tsx +23 -0
  43. package/lib/shared/ui/ProgressBar.tsx +43 -0
  44. package/lib/shared/ui/ResponsiveContainer.tsx +15 -0
  45. package/lib/shared/ui/Section.tsx +15 -0
  46. package/lib/shared/ui/Skeleton.tsx +9 -0
  47. package/lib/shared/ui/TabsSwitcher.tsx +87 -0
  48. package/lib/shared/ui/accordion/Accordion.tsx +36 -0
  49. package/lib/shared/ui/accordion/index.ts +1 -0
  50. package/lib/shared/ui/accordion/model/types.ts +20 -0
  51. package/lib/shared/ui/accordion/ui/AccordionHeader.tsx +35 -0
  52. package/lib/shared/ui/brandLogos.tsx +14 -0
  53. package/lib/shared/ui/button/Button.tsx +117 -0
  54. package/lib/shared/ui/button/index.ts +1 -0
  55. package/lib/shared/ui/button/model/helpers.ts +16 -0
  56. package/lib/shared/ui/formControlElements/CheckboxControl.tsx +92 -0
  57. package/lib/shared/ui/formControlElements/FormControl.tsx +5 -0
  58. package/lib/shared/ui/formControlElements/InputControlMask.tsx +90 -0
  59. package/lib/shared/ui/formControlElements/RadioControl.tsx +130 -0
  60. package/lib/shared/ui/formControlElements/SwitchControl.tsx +79 -0
  61. package/lib/shared/ui/formControlElements/TextareaControl.tsx +96 -0
  62. package/lib/shared/ui/formControlElements/calendarControl/CalendarControl.tsx +178 -0
  63. package/lib/shared/ui/formControlElements/calendarControl/hooks/index.ts +2 -0
  64. package/lib/shared/ui/formControlElements/calendarControl/hooks/useCalendar.tsx +86 -0
  65. package/lib/shared/ui/formControlElements/calendarControl/hooks/useCalendarDropdowns.ts +38 -0
  66. package/lib/shared/ui/formControlElements/calendarControl/index.ts +1 -0
  67. package/lib/shared/ui/formControlElements/calendarControl/model/helpers.ts +60 -0
  68. package/lib/shared/ui/formControlElements/calendarControl/model/types.ts +44 -0
  69. package/lib/shared/ui/formControlElements/calendarControl/ui/DaysOfMonth.tsx +53 -0
  70. package/lib/shared/ui/formControlElements/calendarControl/ui/DaysOfWeek.tsx +28 -0
  71. package/lib/shared/ui/formControlElements/calendarControl/ui/Dropdown.tsx +62 -0
  72. package/lib/shared/ui/formControlElements/calendarControl/ui/Header.tsx +51 -0
  73. package/lib/shared/ui/formControlElements/calendarControl/ui/Navigation.tsx +32 -0
  74. package/lib/shared/ui/formControlElements/calendarControl/ui/OptionsList.tsx +44 -0
  75. package/lib/shared/ui/formControlElements/calendarControl/ui/index.ts +4 -0
  76. package/lib/shared/ui/formControlElements/comboboxControl/ComboboxControl.tsx +134 -0
  77. package/lib/shared/ui/formControlElements/comboboxControl/index.ts +1 -0
  78. package/lib/shared/ui/formControlElements/comboboxControl/model/selectClassnames.ts +51 -0
  79. package/lib/shared/ui/formControlElements/comboboxControl/model/types.ts +42 -0
  80. package/lib/shared/ui/formControlElements/comboboxControl/ui/ComboboxOption.tsx +38 -0
  81. package/lib/shared/ui/formControlElements/comboboxControl/ui/DropdownIndicator.tsx +23 -0
  82. package/lib/shared/ui/formControlElements/comboboxControl/ui/MultiValueRemove.tsx +16 -0
  83. package/lib/shared/ui/formControlElements/comboboxControl/ui/index.ts +3 -0
  84. package/lib/shared/ui/formControlElements/dadata/DadataInputControl.tsx +137 -0
  85. package/lib/shared/ui/formControlElements/dadata/index.ts +1 -0
  86. package/lib/shared/ui/formControlElements/dadata/model/api.ts +25 -0
  87. package/lib/shared/ui/formControlElements/dadata/model/helpers.ts +76 -0
  88. package/lib/shared/ui/formControlElements/dadata/model/types.ts +52 -0
  89. package/lib/shared/ui/formControlElements/dadata/model/useDadata.ts +25 -0
  90. package/lib/shared/ui/formControlElements/editorControl/EditorControl.tsx +82 -0
  91. package/lib/shared/ui/formControlElements/editorControl/components/conrols.tsx +136 -0
  92. package/lib/shared/ui/formControlElements/editorControl/components/menu.tsx +107 -0
  93. package/lib/shared/ui/formControlElements/editorControl/index.ts +60 -0
  94. package/lib/shared/ui/formControlElements/editorControl/ui/RemoveBlockButton.tsx +23 -0
  95. package/lib/shared/ui/formControlElements/editorControl/ui/ResetBlockType.tsx +17 -0
  96. package/lib/shared/ui/formControlElements/index.ts +14 -0
  97. package/lib/shared/ui/formControlElements/inputControl/InputControl.tsx +87 -0
  98. package/lib/shared/ui/formControlElements/inputControl/index.ts +1 -0
  99. package/lib/shared/ui/formControlElements/inputControl/model/hooks.tsx +26 -0
  100. package/lib/shared/ui/formControlElements/inputControlUploader/InputControlUploader.tsx +47 -0
  101. package/lib/shared/ui/formControlElements/inputControlUploader/index.ts +1 -0
  102. package/lib/shared/ui/formControlElements/inputControlUploader/model/helpers.ts +18 -0
  103. package/lib/shared/ui/formControlElements/inputControlUploader/model/hooks/useUploader.tsx +66 -0
  104. package/lib/shared/ui/formControlElements/inputControlUploader/model/index.ts +1 -0
  105. package/lib/shared/ui/formControlElements/inputControlUploader/model/types.ts +22 -0
  106. package/lib/shared/ui/formControlElements/inputControlUploader/ui/File.tsx +35 -0
  107. package/lib/shared/ui/formControlElements/inputControlUploader/ui/Filename.tsx +40 -0
  108. package/lib/shared/ui/formControlElements/inputControlUploader/ui/Files.tsx +30 -0
  109. package/lib/shared/ui/formControlElements/inputControlUploader/ui/Input.tsx +48 -0
  110. package/lib/shared/ui/formControlElements/inputControlUploader/ui/Uploader.tsx +58 -0
  111. package/lib/shared/ui/formControlElements/inputControlUploader/ui/index.ts +3 -0
  112. package/lib/shared/ui/formControlElements/inputCurrencyControl/InputCurrencyControl.tsx +88 -0
  113. package/lib/shared/ui/formControlElements/inputCurrencyControl/index.ts +1 -0
  114. package/lib/shared/ui/formControlElements/inputCurrencyControl/model/helpers.ts +46 -0
  115. package/lib/shared/ui/formControlElements/inputCurrencyControl/model/useInputCurrency.tsx +33 -0
  116. package/lib/shared/ui/formControlElements/inputCurrencyControl/ui/MenuTrigger.tsx +20 -0
  117. package/lib/shared/ui/formControlElements/inputCurrencyControl/ui/OptionList.tsx +29 -0
  118. package/lib/shared/ui/formControlElements/inputCurrencyControl/ui/index.ts +2 -0
  119. package/lib/shared/ui/formControlElements/inputSliderControl/InputSliderControl.tsx +144 -0
  120. package/lib/shared/ui/formControlElements/inputSliderControl/index.ts +1 -0
  121. package/lib/shared/ui/formControlElements/inputSliderControl/model/helpers/dates/getEndWordMonth.ts +14 -0
  122. package/lib/shared/ui/formControlElements/inputSliderControl/model/helpers/dates/getYearEnding.ts +13 -0
  123. package/lib/shared/ui/formControlElements/inputSliderControl/model/helpers/dates/index.ts +2 -0
  124. package/lib/shared/ui/formControlElements/inputSliderControl/model/helpers/formatNumber.ts +6 -0
  125. package/lib/shared/ui/formControlElements/inputSliderControl/model/helpers/getInputSliderSuffix.ts +20 -0
  126. package/lib/shared/ui/formControlElements/inputSliderControl/model/helpers/getStepByVariant.ts +29 -0
  127. package/lib/shared/ui/formControlElements/inputSliderControl/model/helpers/index.ts +4 -0
  128. package/lib/shared/ui/formControlElements/inputSliderControl/model/types.ts +1 -0
  129. package/lib/shared/ui/formControlElements/inputSliderControl/model/useSlider.ts +26 -0
  130. package/lib/shared/ui/formControlElements/inputSliderControl/ui/SliderControl.tsx +47 -0
  131. package/lib/shared/ui/formControlElements/inputSliderControl/ui/index.ts +1 -0
  132. package/lib/shared/ui/formControlElements/model/classes-types.ts +22 -0
  133. package/lib/shared/ui/formControlElements/model/index.ts +2 -0
  134. package/lib/shared/ui/formControlElements/model/message-view-animation.ts +6 -0
  135. package/lib/shared/ui/formControlElements/model/props-types.ts +31 -0
  136. package/lib/shared/ui/formControlElements/ui/FieldAttachment.tsx +76 -0
  137. package/lib/shared/ui/formControlElements/ui/FieldContainer.tsx +37 -0
  138. package/lib/shared/ui/formControlElements/ui/FieldWrapper.tsx +33 -0
  139. package/lib/shared/ui/formControlElements/ui/Label.tsx +32 -0
  140. package/lib/shared/ui/formControlElements/ui/MessageView.tsx +41 -0
  141. package/lib/shared/ui/formControlElements/ui/index.ts +4 -0
  142. package/lib/shared/ui/icon/Icon.tsx +41 -0
  143. package/lib/shared/ui/icon/index.ts +2 -0
  144. package/lib/shared/ui/icon/sprite.gen.ts +177 -0
  145. package/lib/shared/ui/index.ts +68 -0
  146. package/lib/shared/ui/modal/Modal.tsx +68 -0
  147. package/lib/shared/ui/modal/index.ts +1 -0
  148. package/lib/shared/ui/modal/model/helpers.ts +13 -0
  149. package/lib/shared/ui/modal/ui/ModalHeader.tsx +33 -0
  150. package/lib/shared/ui/notification/Notification.tsx +31 -0
  151. package/lib/shared/ui/notification/index.ts +1 -0
  152. package/lib/shared/ui/notification/ui/CustomToast.tsx +42 -0
  153. package/lib/shared/ui/popover/Popover.tsx +74 -0
  154. package/lib/shared/ui/popover/index.ts +1 -0
  155. package/lib/shared/ui/providers/NotificationProvider.tsx +29 -0
  156. package/lib/shared/ui/providers/index.ts +1 -0
  157. package/lib/shared/ui/table/Table.tsx +144 -0
  158. package/lib/shared/ui/table/index.ts +1 -0
  159. package/lib/shared/ui/table/type.ts +30 -0
  160. package/lib/shared/utils/capitalize.ts +6 -0
  161. package/lib/shared/utils/cn.ts +6 -0
  162. package/lib/shared/utils/deepCompare.ts +1 -0
  163. package/lib/shared/utils/formatToDate.ts +5 -0
  164. package/lib/shared/utils/index.ts +5 -0
  165. package/lib/shared/utils/isClient.ts +1 -0
  166. package/lib/shared/validation/index.ts +3 -0
  167. package/lib/shared/validation/messages.ts +12 -0
  168. package/lib/shared/validation/regExp.ts +5 -0
  169. package/lib/shared/validation/zodValidation/calendar.ts +32 -0
  170. package/lib/shared/validation/zodValidation/dadataFio.ts +67 -0
  171. package/lib/shared/validation/zodValidation/index.ts +2 -0
  172. package/lib/vite-env.d.ts +2 -0
  173. package/lib/widgets/Advantages.tsx +45 -0
  174. package/lib/widgets/banner/Banner.tsx +74 -0
  175. package/lib/widgets/banner/index.ts +1 -0
  176. package/lib/widgets/banner/model/helpers.ts +159 -0
  177. package/lib/widgets/banner/money.png +0 -0
  178. package/lib/widgets/banner/saif.jpg +0 -0
  179. package/lib/widgets/banner/saifMob.jpg +0 -0
  180. package/lib/widgets/banner/seif.jpg +0 -0
  181. package/lib/widgets/banner/shield.jpg +0 -0
  182. package/lib/widgets/banner/shield.png +0 -0
  183. package/lib/widgets/banner/ui/BannerButtonsGroup.tsx +44 -0
  184. package/lib/widgets/banner/ui/banners/BannerImageFull.tsx +82 -0
  185. package/lib/widgets/banner/ui/banners/BannerWithSeparateImg.tsx +60 -0
  186. package/lib/widgets/banner/ui/banners/index.ts +1 -0
  187. package/lib/widgets/footer/Footer.tsx +95 -0
  188. package/lib/widgets/footer/index.ts +1 -0
  189. package/lib/widgets/footer/model/defaultValues.tsx +105 -0
  190. package/lib/widgets/footer/model/types.ts +19 -0
  191. package/lib/widgets/footer/ui/Copyright.tsx +15 -0
  192. package/lib/widgets/footer/ui/Ligal.tsx +50 -0
  193. package/lib/widgets/footer/ui/NavLinks.tsx +41 -0
  194. package/lib/widgets/footer/ui/PhonesBlock.tsx +34 -0
  195. package/lib/widgets/footer/ui/SocialLinks.tsx +30 -0
  196. package/lib/widgets/footer/ui/index.ts +5 -0
  197. package/lib/widgets/index.ts +5 -0
  198. package/lib/widgets/pageHeader/PageHeader.tsx +54 -0
  199. package/lib/widgets/pageHeader/index.ts +1 -0
  200. package/lib/widgets/stepper/Stepper.tsx +43 -0
  201. package/lib/widgets/stepper/index.ts +1 -0
  202. package/lib/widgets/stepper/ui/SingleStep.tsx +42 -0
  203. package/package.json +1 -4
  204. package/postcss.config.mjs +8 -0
  205. package/public/sprites/arrows.svg +1 -0
  206. package/public/sprites/brandLogos.svg +1 -0
  207. package/public/sprites/files.svg +1 -0
  208. package/public/sprites/general.svg +1 -0
  209. package/public/sprites/info.svg +1 -0
  210. package/public/sprites/social.svg +1 -0
  211. package/src/App.tsx +9 -0
  212. package/src/app/providers/RootProvider.tsx +11 -0
  213. package/src/app/providers/index.ts +1 -0
  214. package/src/app/providers/model/types.ts +5 -0
  215. package/src/configs/setup.ts +9 -0
  216. package/src/configs/storybook.config.ts +23 -0
  217. package/src/main.tsx +10 -0
  218. package/src/stories/primitives/Accordion.stories.tsx +66 -0
  219. package/src/stories/primitives/Badge.stories.tsx +28 -0
  220. package/src/stories/primitives/Breadcrumbs.stories.tsx +29 -0
  221. package/src/stories/primitives/Button/Button.stories.tsx +149 -0
  222. package/src/stories/primitives/Button/Button.test.tsx +150 -0
  223. package/src/stories/primitives/ButtonIcon.stories.tsx +75 -0
  224. package/src/stories/primitives/CustomLink.stories.tsx +64 -0
  225. package/src/stories/primitives/Document.stories.tsx +36 -0
  226. package/src/stories/primitives/Heading.stories.tsx +29 -0
  227. package/src/stories/primitives/Hint.stories.tsx +82 -0
  228. package/src/stories/primitives/Icon.stories.tsx +36 -0
  229. package/src/stories/primitives/Loader.stories.tsx +39 -0
  230. package/src/stories/primitives/Modal.stories.tsx +106 -0
  231. package/src/stories/primitives/Notification.stories.tsx +102 -0
  232. package/src/stories/primitives/PhoneView.stories.tsx +22 -0
  233. package/src/stories/primitives/Popover.stories.tsx +41 -0
  234. package/src/stories/primitives/ProgressBar.stories.tsx +68 -0
  235. package/src/stories/primitives/Skeleton.stories.tsx +21 -0
  236. package/src/stories/primitives/Table.stories.tsx +44 -0
  237. package/src/stories/primitives/TabsSwitcher.stories.tsx +45 -0
  238. package/src/stories/primitives/formControl/CalendarControl.stories.tsx +45 -0
  239. package/src/stories/primitives/formControl/CheckboxControl.stories.tsx +64 -0
  240. package/src/stories/primitives/formControl/ComboboxControl.stories.tsx +67 -0
  241. package/src/stories/primitives/formControl/DadataInputControl.stories.tsx +79 -0
  242. package/src/stories/primitives/formControl/EditorControl.stories.tsx +31 -0
  243. package/src/stories/primitives/formControl/FormControlAllFields.stories.tsx +25 -0
  244. package/src/stories/primitives/formControl/InputControl.stories.tsx +84 -0
  245. package/src/stories/primitives/formControl/InputControlPassword.stories.tsx +38 -0
  246. package/src/stories/primitives/formControl/InputControlUploader.stories.tsx +44 -0
  247. package/src/stories/primitives/formControl/InputCurrencyControl.stories.tsx +73 -0
  248. package/src/stories/primitives/formControl/InputSliderControl.stories.tsx +62 -0
  249. package/src/stories/primitives/formControl/RadioContol.stories.tsx +61 -0
  250. package/src/stories/primitives/formControl/SwitchControl.stories.tsx +51 -0
  251. package/src/stories/primitives/formControl/TextareaControl.stories.tsx +55 -0
  252. package/src/stories/primitives/formControl/inputControlMask.stories.tsx +67 -0
  253. package/src/stories/widgets/Advantages.stories.tsx +42 -0
  254. package/src/stories/widgets/Banner.stories.tsx +94 -0
  255. package/src/stories/widgets/Footer.stories.tsx +36 -0
  256. package/src/stories/widgets/PageHeader.stories.tsx +33 -0
  257. package/src/stories/widgets/Stepper.stories.tsx +24 -0
  258. package/src/storybookHelpers/actions.tsx +5 -0
  259. package/src/storybookHelpers/index.ts +2 -0
  260. package/src/storybookHelpers/reactHookForm/index.ts +3 -0
  261. package/src/storybookHelpers/reactHookForm/model/mockData.ts +19 -0
  262. package/src/storybookHelpers/reactHookForm/model/mocks.tsx +105 -0
  263. package/src/storybookHelpers/reactHookForm/model/renderFields.tsx +58 -0
  264. package/src/storybookHelpers/reactHookForm/model/types.ts +86 -0
  265. package/src/storybookHelpers/reactHookForm/ui/StorybookFieldsMapper.tsx +32 -0
  266. package/src/storybookHelpers/reactHookForm/ui/StorybookFormProvider.tsx +43 -0
  267. package/src/storybookHelpers/reactHookForm/ui/index.ts +2 -0
  268. package/src/storybookHelpers/table/utils/defaultValue.ts +51 -0
  269. package/static/arrows/arrowCircle.svg +18 -0
  270. package/static/arrows/arrowLink.svg +3 -0
  271. package/static/arrows/arrowRight.svg +3 -0
  272. package/static/brandLogos/logoBlack.svg +14 -0
  273. package/static/brandLogos/logoBusiness.svg +80 -0
  274. package/static/brandLogos/logoGray.svg +56 -0
  275. package/static/brandLogos/logoInsurance.svg +124 -0
  276. package/static/brandLogos/logoMain.svg +14 -0
  277. package/static/brandLogos/logoWhite.svg +56 -0
  278. package/static/files/border.svg +6 -0
  279. package/static/files/borderError.svg +6 -0
  280. package/static/files/documentFilled.svg +4 -0
  281. package/static/files/documentOutline.svg +4 -0
  282. package/static/files/upload.svg +3 -0
  283. package/static/general/calendar.svg +3 -0
  284. package/static/general/check.svg +6 -0
  285. package/static/general/close.svg +12 -0
  286. package/static/general/edit.svg +4 -0
  287. package/static/general/hiddenEye.svg +4 -0
  288. package/static/general/plus.svg +3 -0
  289. package/static/general/showEye.svg +4 -0
  290. package/static/info/warningCircle.svg +5 -0
  291. package/static/social/classmates.svg +3 -0
  292. package/static/social/telegram.svg +3 -0
  293. package/static/social/vk.svg +3 -0
  294. package/tailwind.config.ts +10 -0
  295. package/tsconfig.json +33 -0
  296. package/tsconfig.node.json +11 -0
  297. package/vite.config.ts +68 -0
  298. package/vitest.config.mjs +12 -0
  299. package/dist/scbt-ecom-ui-0.4.1.tgz +0 -0
@@ -0,0 +1,144 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Controller, type FieldValues } from 'react-hook-form'
5
+ import { NumericFormat } from 'react-number-format'
6
+ import type { TAdditionalInputClassesWithAttachment, TControlledInputProps } from '../model'
7
+ import { FieldAttachment, FieldContainer, FieldWrapper, MessageView } from '../ui'
8
+ import { formatNumber, getMinMaxTextSlider, getStepByVariant } from './model/helpers'
9
+ import { type TSliderVariants } from './model/types'
10
+ import { useSlider } from './model/useSlider'
11
+ import { SliderControl } from './ui/SliderControl'
12
+ import { cn } from '$/shared/utils'
13
+
14
+ export interface IInputSliderCommonProps {
15
+ min: number
16
+ max: number
17
+ variant: TSliderVariants
18
+ step?: number
19
+ }
20
+
21
+ export interface InputSliderControlProps<T extends FieldValues> extends TControlledInputProps<T>, IInputSliderCommonProps {
22
+ classes?: Partial<TAdditionalInputClassesWithAttachment> & {
23
+ spanLeft?: string
24
+ spanRight?: string
25
+ }
26
+ disabled?: boolean
27
+ onInputChange?: (value?: number) => void
28
+ leftText?: string
29
+ rightText?: string
30
+ }
31
+
32
+ export const InputSliderControl = <T extends FieldValues>({
33
+ label,
34
+ size = 'full',
35
+ helperText,
36
+ control,
37
+ classes,
38
+ badge,
39
+ icon,
40
+ disabled,
41
+ onInputChange,
42
+ min,
43
+ max,
44
+ variant,
45
+ ...props
46
+ }: InputSliderControlProps<T>) => {
47
+ const inputId = React.useId()
48
+ const ref = React.useRef<HTMLInputElement>(null)
49
+
50
+ const handleIconClick = () => {
51
+ if (ref.current) {
52
+ ref?.current?.focus()
53
+ }
54
+ }
55
+
56
+ const { handleBlur, handleChange, getSuffixText } = useSlider()
57
+
58
+ return (
59
+ <Controller
60
+ control={control}
61
+ name={props.name}
62
+ render={({ field: { onChange, value }, fieldState: { error } }) => {
63
+ const [start, end] = getMinMaxTextSlider(variant, min, max)
64
+ return (
65
+ <div className='flex flex-col gap-1'>
66
+ <FieldContainer size={size} classes={classes}>
67
+ <FieldWrapper
68
+ fieldId={inputId}
69
+ label={label}
70
+ classes={classes}
71
+ disabled={disabled}
72
+ value={value}
73
+ error={!!error?.message}
74
+ >
75
+ <>
76
+ <NumericFormat
77
+ aria-invalid={error?.message ? 'true' : 'false'}
78
+ className={cn(
79
+ 'group/slider desk-title-bold-s h-[56px] w-full rounded-md bg-color-transparent px-4 pt-5 text-color-dark outline-none transition-all',
80
+ classes?.input
81
+ )}
82
+ id={inputId}
83
+ onBlur={() => {
84
+ handleBlur(value, min, max, onChange)
85
+ }}
86
+ value={value}
87
+ disabled={disabled}
88
+ suffix={` ${getSuffixText(value, variant)}`}
89
+ thousandsGroupStyle='thousand'
90
+ onValueChange={({ floatValue }) => {
91
+ handleChange(onChange, floatValue)
92
+ if (onInputChange) {
93
+ onInputChange(floatValue)
94
+ }
95
+ }}
96
+ thousandSeparator={' '}
97
+ allowNegative={false}
98
+ getInputRef={ref}
99
+ {...props}
100
+ />
101
+ <SliderControl
102
+ onValueChange={(inputValue) => onChange(inputValue[0])}
103
+ value={[value]}
104
+ min={min}
105
+ max={max}
106
+ step={getStepByVariant(value, variant)}
107
+ variant={variant}
108
+ />
109
+
110
+ <div aria-label='edit' onKeyDown={handleIconClick} role='button' tabIndex={0} onClick={handleIconClick}>
111
+ <FieldAttachment
112
+ onClickIcon={handleIconClick}
113
+ onKeyDownIcon={handleIconClick}
114
+ isSlider
115
+ badge={badge}
116
+ icon={icon}
117
+ error={!!error?.message}
118
+ classes={classes}
119
+ />
120
+ </div>
121
+ </>
122
+ </FieldWrapper>
123
+
124
+ <MessageView
125
+ className={cn(classes?.message)}
126
+ intent={error?.message ? 'error' : 'simple'}
127
+ text={error?.message || helperText}
128
+ disabled={disabled}
129
+ />
130
+ </FieldContainer>
131
+ <div className='flex justify-between'>
132
+ <span
133
+ className={cn('desk-body-regular-m text-color-tetriary', classes?.spanLeft)}
134
+ >{`${formatNumber(min)} ${start}`}</span>
135
+ <span
136
+ className={cn('desk-body-regular-m text-color-tetriary', classes?.spanRight)}
137
+ >{`${formatNumber(max)} ${end}`}</span>
138
+ </div>
139
+ </div>
140
+ )
141
+ }}
142
+ />
143
+ )
144
+ }
@@ -0,0 +1 @@
1
+ export { InputSliderControl, type InputSliderControlProps } from './InputSliderControl'
@@ -0,0 +1,14 @@
1
+ export function getEndWordMonth(months: number) {
2
+ const remMonthsDivByTen = months % 10
3
+
4
+ switch (true) {
5
+ case months > 10 && months < 20:
6
+ return 'месяцев'
7
+ case remMonthsDivByTen > 1 && remMonthsDivByTen < 5:
8
+ return 'месяца'
9
+ case remMonthsDivByTen === 1:
10
+ return 'месяц'
11
+ default:
12
+ return 'месяцев'
13
+ }
14
+ }
@@ -0,0 +1,13 @@
1
+ export function getYearEnding(years: number) {
2
+ const lastDigit = Math.floor(years) % 10
3
+ if (years >= 5 && years <= 20) {
4
+ return 'лет'
5
+ }
6
+ if (lastDigit === 1) {
7
+ return 'год'
8
+ }
9
+ if (lastDigit >= 2 && lastDigit <= 4) {
10
+ return 'года'
11
+ }
12
+ return 'лет'
13
+ }
@@ -0,0 +1,2 @@
1
+ export { getEndWordMonth } from './getEndWordMonth'
2
+ export { getYearEnding } from './getYearEnding'
@@ -0,0 +1,6 @@
1
+ export const formatNumber = (value: number | string) => {
2
+ if (typeof value === 'number') {
3
+ return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ')
4
+ }
5
+ return value.replace(/\B(?=(\d{3})+(?!\d))/g, ' ')
6
+ }
@@ -0,0 +1,20 @@
1
+ import { type TSliderVariants } from '../types'
2
+ import { getYearEnding } from './dates'
3
+
4
+ export const getInputSliderSuffix = (variant: TSliderVariants, value: number) => {
5
+ const variants = {
6
+ years: getYearEnding(value),
7
+ credit: '₽'
8
+ }
9
+
10
+ return variants[variant]
11
+ }
12
+
13
+ export const getMinMaxTextSlider = (variant: TSliderVariants, min: number, max: number) => {
14
+ const variants = {
15
+ years: [min, max].map(getYearEnding),
16
+ credit: ['₽', '₽']
17
+ }
18
+
19
+ return variants[variant]
20
+ }
@@ -0,0 +1,29 @@
1
+ import { type TSliderVariants } from '../types'
2
+
3
+ const getStepCredit = (value: number) => {
4
+ const step = 1000
5
+
6
+ const firstStep = value < 100_000
7
+ const secondStep = value >= 100_000 && value < 500_000
8
+ const max = value >= 1_000_000
9
+
10
+ switch (true) {
11
+ case firstStep:
12
+ return 1000
13
+ case secondStep:
14
+ return 10_000
15
+ case max:
16
+ return 100_000
17
+ default:
18
+ return step
19
+ }
20
+ }
21
+
22
+ export const getStepByVariant = (value: number, variant: TSliderVariants) => {
23
+ const variants = {
24
+ years: 1,
25
+ credit: getStepCredit(value)
26
+ }
27
+
28
+ return variants[variant]
29
+ }
@@ -0,0 +1,4 @@
1
+ export { getMinMaxTextSlider } from './getInputSliderSuffix'
2
+ export { formatNumber } from './formatNumber'
3
+ export { getInputSliderSuffix } from './getInputSliderSuffix'
4
+ export { getStepByVariant } from './getStepByVariant'
@@ -0,0 +1 @@
1
+ export type TSliderVariants = 'years' | 'credit'
@@ -0,0 +1,26 @@
1
+ import { getInputSliderSuffix } from './helpers'
2
+ import { type TSliderVariants } from './types'
3
+
4
+ export const useSlider = () => {
5
+ const getSuffixText = (value: number, variant: TSliderVariants) => {
6
+ return getInputSliderSuffix(variant, value)
7
+ }
8
+
9
+ const handleBlur = (value: number, min: number, max: number, onChange: (...event: unknown[]) => void) => {
10
+ if (value > max) {
11
+ onChange(max)
12
+ }
13
+ if (value < min) {
14
+ onChange(min)
15
+ }
16
+ }
17
+
18
+ const handleChange = (onChange: (...event: unknown[]) => void, val?: number) => {
19
+ if (val === undefined) {
20
+ return
21
+ }
22
+ onChange(val)
23
+ }
24
+
25
+ return { getSuffixText, handleBlur, handleChange }
26
+ }
@@ -0,0 +1,47 @@
1
+ import * as React from 'react'
2
+ import * as SliderPrimitive from '@radix-ui/react-slider'
3
+ import { type IInputSliderCommonProps } from '../InputSliderControl'
4
+ import { cn } from '$/shared/utils'
5
+
6
+ export interface ISliderControllProps extends IInputSliderCommonProps {
7
+ classes?: TSliderControlClasses
8
+ onValueChange?: (value: number[]) => void
9
+ value: number[]
10
+ }
11
+
12
+ export type TSliderControlClasses = {
13
+ sliderWrapper?: string
14
+ sliderRoot?: string
15
+ spanLeft?: string
16
+ spanRight?: string
17
+ sliderTrack?: string
18
+ sliderThumb?: string
19
+ sliderRange?: string
20
+ }
21
+
22
+ export const SliderControl = React.forwardRef<React.ElementRef<typeof SliderPrimitive.Root>, ISliderControllProps>(
23
+ ({ min, max, classes, ...props }, ref) => {
24
+ return (
25
+ <div className={cn('absolute bottom-[-7px] w-full px-4', classes?.sliderWrapper)}>
26
+ <SliderPrimitive.Root
27
+ ref={ref}
28
+ className={cn('relative flex h-4 w-full touch-none select-none items-center', classes?.sliderRoot)}
29
+ min={min}
30
+ max={max}
31
+ {...props}
32
+ >
33
+ <SliderPrimitive.Track
34
+ className={cn(
35
+ 'relative h-[2px] w-full grow overflow-hidden rounded-full bg-color-blue-grey-500',
36
+ classes?.sliderTrack
37
+ )}
38
+ >
39
+ <SliderPrimitive.Range className={cn('absolute h-full bg-color-primary-default', classes?.sliderRange)} />
40
+ </SliderPrimitive.Track>
41
+ <SliderPrimitive.Thumb className='ring-offset-background block h-4 w-4 cursor-pointer rounded-full bg-color-primary-default transition-colors hover:before:absolute hover:before:left-1/2 hover:before:top-1/2 hover:before:h-8 hover:before:w-8 hover:before:-translate-x-1/2 hover:before:-translate-y-1/2 hover:before:rounded-full hover:before:bg-color-primary-tr-hover hover:before:content-[""] focus:outline-none focus:before:bg-color-primary-tr-pressed disabled:pointer-events-none disabled:opacity-50' />
42
+ </SliderPrimitive.Root>
43
+ </div>
44
+ )
45
+ }
46
+ )
47
+ SliderPrimitive.Slider.displayName = SliderPrimitive.Root.displayName
@@ -0,0 +1 @@
1
+ export { SliderControl } from './SliderControl'
@@ -0,0 +1,22 @@
1
+ export type TFieldWrapperClasses = {
2
+ field: string
3
+ label: string
4
+ }
5
+
6
+ export type TFieldAttachmentClasses = {
7
+ badge: string
8
+ icon: string
9
+ attachmentWrapper: string
10
+ }
11
+
12
+ export type TFieldContainerClasses = {
13
+ container: string
14
+ }
15
+
16
+ export type TInputCommonClasses = {
17
+ input: string
18
+ message: string
19
+ }
20
+
21
+ export type TAdditionalInputPrimitiveClasses = TFieldWrapperClasses & TFieldContainerClasses & TInputCommonClasses
22
+ export type TAdditionalInputClassesWithAttachment = TAdditionalInputPrimitiveClasses & TFieldAttachmentClasses
@@ -0,0 +1,2 @@
1
+ export type * from './classes-types'
2
+ export type * from './props-types'
@@ -0,0 +1,6 @@
1
+ export const animation = {
2
+ initial: { height: 0, opacity: 0, transformOrigin: 'center bottom' },
3
+ animate: { height: 'auto', opacity: 1, transformOrigin: 'center bottom' },
4
+ exit: { height: 0, opacity: 0, transformOrigin: 'center bottom' },
5
+ transition: { duration: 0.2, delay: 0.2, ease: 'easeInOut' }
6
+ }
@@ -0,0 +1,31 @@
1
+ import type * as React from 'react'
2
+ import type { Control, FieldValues, Path } from 'react-hook-form'
3
+ import type { TFieldContainerConfig } from '../ui/FieldContainer'
4
+
5
+ export type TInputCommonProps = Omit<
6
+ React.ComponentProps<'input'>,
7
+ 'name' | 'placeholder' | 'size' | 'type' | 'defaultValue' | 'className'
8
+ >
9
+ export type TTextareaCommonProps = Omit<React.ComponentProps<'textarea'>, 'name' | 'size' | 'type' | 'defaultValue' | 'className'>
10
+
11
+ type TFieldControlledProps<T extends FieldValues> = {
12
+ name: Path<T>
13
+ control: Control<T>
14
+ label: string
15
+ helperText?: string
16
+ }
17
+
18
+ export type TFieldContainerSize = 'sm' | 'md' | 'lg' | 'full'
19
+ export type TFieldAttachment = {
20
+ badge?: string
21
+ icon?: React.ReactElement
22
+ swapPosition?: boolean
23
+ onClickIcon?: (...args: unknown[]) => unknown
24
+ onKeyDownIcon?: (event: React.KeyboardEvent) => unknown
25
+ }
26
+
27
+ // # Required props on controlled INPUTS - [maskInput, baseInput, dadata]
28
+ export type TControlledInputProps<T extends FieldValues> = TFieldContainerConfig & TFieldAttachment & TFieldControlledProps<T>
29
+
30
+ // # Required props on controlled INPUTS - [checkbox, radio]
31
+ export type TControlledInputPrimitiveProps<T extends FieldValues> = TInputCommonProps & TFieldControlledProps<T>
@@ -0,0 +1,76 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { Badge } from '../../Badge'
5
+ import { Icon } from '../../icon/Icon'
6
+ import type { TFieldAttachmentClasses } from '../model/classes-types'
7
+ import type { TFieldAttachment } from '../model/props-types'
8
+ import { cn } from '$/shared/utils'
9
+
10
+ interface IFieldAttachmentProps extends TFieldAttachment {
11
+ classes?: Partial<TFieldAttachmentClasses>
12
+ error?: boolean
13
+ isTextarea?: boolean
14
+ swapPosition?: boolean
15
+ onClickIcon?: (...args: unknown[]) => unknown
16
+ onKeyDownIcon?: (event: React.KeyboardEvent) => unknown
17
+ isSlider?: boolean
18
+ absolute?: boolean
19
+ }
20
+
21
+ export const FieldAttachment = ({
22
+ badge,
23
+ isSlider,
24
+ icon,
25
+ error,
26
+ isTextarea = false,
27
+ classes,
28
+ swapPosition,
29
+ onClickIcon,
30
+ onKeyDownIcon,
31
+ absolute = false
32
+ }: IFieldAttachmentProps) => {
33
+ const interactiveIconAttr = (onClickIcon || onKeyDownIcon) && {
34
+ role: 'button',
35
+ tabIndex: 0,
36
+ onClick: onClickIcon,
37
+ onKeyDown: onKeyDownIcon
38
+ }
39
+
40
+ return (
41
+ <>
42
+ {error ? (
43
+ <Icon name='info/warningCircle' className={cn('mr-4 size-6 text-icon-secondary-default', { 'm-0 size-5': isTextarea })} />
44
+ ) : (
45
+ <>
46
+ {(badge || icon) && (
47
+ <div
48
+ className={cn(
49
+ 'mr-4 flex items-center gap-4',
50
+ { 'm-0': isTextarea },
51
+ { 'flex-row-reverse': swapPosition },
52
+ { 'absolute right-0 top-1/2 ml-1 -translate-y-1/2': absolute },
53
+ classes?.attachmentWrapper
54
+ )}
55
+ >
56
+ {icon && (
57
+ <span
58
+ {...interactiveIconAttr}
59
+ className={cn(
60
+ 'flex size-6 items-center justify-center',
61
+ { 'size-5': isTextarea },
62
+ { 'group-focus-within:[&_svg]:text-icon-blue-grey-800': isSlider },
63
+ classes?.icon
64
+ )}
65
+ >
66
+ {icon}
67
+ </span>
68
+ )}
69
+ {badge && <Badge className={cn('bg-color-positive', classes?.badge)}>{badge}</Badge>}
70
+ </div>
71
+ )}
72
+ </>
73
+ )}
74
+ </>
75
+ )
76
+ }
@@ -0,0 +1,37 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { cva, type VariantProps } from 'class-variance-authority'
5
+ import type { TFieldContainerClasses } from '../model/classes-types'
6
+ import { cn } from '$/shared/utils'
7
+
8
+ const fieldContainerConfig = cva('relative min-w-[360px] flex flex-col group', {
9
+ variants: {
10
+ intent: {
11
+ clear: '!w-full min-w-[140px]',
12
+ filled: ''
13
+ },
14
+ size: {
15
+ sm: 'w-[360px]',
16
+ md: 'w-[520px]',
17
+ lg: 'w-[720px]',
18
+ full: 'w-full'
19
+ }
20
+ },
21
+ defaultVariants: {
22
+ size: 'full',
23
+ intent: 'filled'
24
+ }
25
+ })
26
+
27
+ export type TFieldContainerConfig = VariantProps<typeof fieldContainerConfig>
28
+
29
+ interface IFieldContainerProps extends TFieldContainerConfig {
30
+ classes?: Partial<TFieldContainerClasses>
31
+ children: React.ReactNode
32
+ intent?: 'clear' | 'filled' | null
33
+ }
34
+
35
+ export const FieldContainer = ({ size, intent = 'filled', classes, children }: IFieldContainerProps) => {
36
+ return <div className={cn(fieldContainerConfig({ size, intent }), classes?.container)}>{children}</div>
37
+ }
@@ -0,0 +1,33 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import type { TFieldWrapperClasses } from '../model'
5
+ import { Label } from './Label'
6
+ import { cn } from '$/shared/utils'
7
+
8
+ interface IFieldWrapperProps<V> {
9
+ children: React.ReactElement
10
+ label: string
11
+ fieldId: string
12
+ value: V
13
+ error?: boolean
14
+ classes?: Partial<TFieldWrapperClasses>
15
+ disabled?: boolean
16
+ isTextarea?: boolean
17
+ }
18
+
19
+ export const FieldWrapper = <V,>({ children, error, disabled, classes, ...props }: IFieldWrapperProps<V>) => {
20
+ return (
21
+ <div
22
+ className={cn(
23
+ 'relative flex items-center justify-between rounded-sm border border-solid border-transparent bg-color-blue-grey-100 transition-colors hover:bg-color-blue-grey-200 focus:outline-blue-grey-800 active:bg-color-blue-grey-100 group-focus-within:border-blue-grey-800',
24
+ { '!border-negative': error },
25
+ { '!bg-color-blue-grey-100': disabled },
26
+ classes?.field
27
+ )}
28
+ >
29
+ <Label {...props} />
30
+ {children}
31
+ </div>
32
+ )
33
+ }
@@ -0,0 +1,32 @@
1
+ import { type TFieldWrapperClasses } from '../model'
2
+ import { cn } from '$/shared/utils'
3
+
4
+ interface ILabelProps<V> {
5
+ label: string
6
+ fieldId: string
7
+ value: V
8
+ classes?: Partial<TFieldWrapperClasses>
9
+ isTextarea?: boolean
10
+ disabled?: boolean
11
+ }
12
+
13
+ export const Label = <V,>({ disabled, fieldId, label, value, classes, isTextarea = false }: ILabelProps<V>) => {
14
+ return (
15
+ <label
16
+ htmlFor={fieldId}
17
+ className={cn(
18
+ 'desk-body-regular-l pointer-events-none absolute left-4 top-2/4 -translate-y-1/2 text-color-tetriary transition-all duration-15',
19
+ { '!top-2 !translate-y-0 !bg-color-transparent [&&]:desk-body-regular-s': value && !isTextarea },
20
+ {
21
+ 'group-focus-within:desk-body-regular-s group-focus-within:top-2 group-focus-within:translate-y-0 group-focus-within:bg-color-transparent':
22
+ !isTextarea
23
+ },
24
+ { 'desk-body-regular-s top-2 translate-y-0': isTextarea },
25
+ { 'text-color-disabled': disabled },
26
+ classes?.label
27
+ )}
28
+ >
29
+ {label}
30
+ </label>
31
+ )
32
+ }
@@ -0,0 +1,41 @@
1
+ 'use client'
2
+
3
+ import type { FieldError } from 'react-hook-form'
4
+ import { cva, type VariantProps } from 'class-variance-authority'
5
+ import { motion } from 'framer-motion'
6
+ import { animation } from '../model/message-view-animation'
7
+ import { cn } from '$/shared/utils'
8
+
9
+ const messageViewConfig = cva('desk-body-regular-m mt-2', {
10
+ variants: {
11
+ intent: {
12
+ simple: 'text-color-tetriary',
13
+ error: 'text-color-negative'
14
+ },
15
+ disabled: {
16
+ true: 'text-color-disabled',
17
+ false: ''
18
+ }
19
+ },
20
+ defaultVariants: {
21
+ intent: 'simple'
22
+ }
23
+ })
24
+
25
+ export interface IMessageViewProps extends VariantProps<typeof messageViewConfig> {
26
+ as?: 'div' | 'span' | 'p'
27
+ text?: string | FieldError['message']
28
+ className?: string
29
+ }
30
+
31
+ export const MessageView = ({ intent, as: Element = 'p', disabled, text, className, ...props }: IMessageViewProps) => {
32
+ if (!text) return null
33
+
34
+ const MotionElement = motion(Element)
35
+
36
+ return (
37
+ <MotionElement className={cn(messageViewConfig({ intent, disabled }), className)} {...animation} {...props}>
38
+ {text}
39
+ </MotionElement>
40
+ )
41
+ }
@@ -0,0 +1,4 @@
1
+ export { FieldAttachment } from './FieldAttachment'
2
+ export { MessageView } from './MessageView'
3
+ export { FieldWrapper } from './FieldWrapper'
4
+ export { FieldContainer } from './FieldContainer'
@@ -0,0 +1,41 @@
1
+ import * as React from 'react'
2
+ import { SPRITES_META, type SpritesMap } from './sprite.gen'
3
+ import { cn } from '$/shared/utils'
4
+
5
+ export type IconName<Key extends keyof SpritesMap> = `${Key}/${SpritesMap[Key]}`
6
+ export type TAllowedIcons = { [Key in keyof SpritesMap]: IconName<Key> }[keyof SpritesMap]
7
+
8
+ export interface IconProps extends React.SVGProps<SVGSVGElement> {
9
+ name: TAllowedIcons
10
+ }
11
+
12
+ const getIconMeta = <Key extends keyof SpritesMap>(name: IconName<Key>) => {
13
+ const [spriteName, iconName] = name.split('/') as [Key, SpritesMap[Key]]
14
+ const {
15
+ filePath,
16
+ items: {
17
+ [iconName]: { viewBox, width, height }
18
+ }
19
+ } = SPRITES_META[spriteName]
20
+
21
+ const axis = width === height ? 'xy' : width > height ? 'x' : 'y'
22
+
23
+ return { filePath, iconName, viewBox, axis }
24
+ }
25
+
26
+ export const Icon = ({ name, className, ...props }: IconProps) => {
27
+ const { viewBox, filePath, iconName, axis } = getIconMeta(name)
28
+
29
+ return (
30
+ <svg
31
+ className={cn('text-inherit inline-block size-6 select-none fill-current', className)}
32
+ focusable='false'
33
+ viewBox={viewBox}
34
+ aria-hidden
35
+ data-axis={axis}
36
+ {...props}
37
+ >
38
+ <use href={`/sprites/${filePath}#${iconName}`} />
39
+ </svg>
40
+ )
41
+ }
@@ -0,0 +1,2 @@
1
+ export { Icon, type IconProps, type TAllowedIcons } from './Icon'
2
+ export type { SpritesMap } from './sprite.gen'