@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,66 @@
1
+ import { useState } from 'react'
2
+ import { type DropzoneOptions, type FileRejection, useDropzone } from 'react-dropzone'
3
+ import { FilesErrorCode } from '../helpers'
4
+ import { Notification } from '$/shared/ui/'
5
+
6
+ export type TUseUploader = {
7
+ controlledFiles: File[]
8
+ dropzoneOptions: DropzoneOptions
9
+ onValueChange: (f: File[]) => void
10
+ }
11
+
12
+ export const useUploader = ({ dropzoneOptions, controlledFiles, onValueChange }: TUseUploader) => {
13
+ const [filesStatus, setFilesStatus] = useState<Record<string, 'loading' | 'success' | 'error'>>({})
14
+
15
+ const removeFile = (index: number) => {
16
+ const updatedFiles = controlledFiles.filter((_, idx) => idx !== index)
17
+ onValueChange(updatedFiles)
18
+ }
19
+
20
+ const onDrop = (acceptedFiles: File[], rejectedFiles: FileRejection[]) => {
21
+ acceptedFiles.forEach((file) => {
22
+ const reader = new FileReader()
23
+ setFilesStatus((prev) => ({ ...prev, [file.name]: 'loading' }))
24
+
25
+ reader.onload = () => {
26
+ setFilesStatus((prev) => ({ ...prev, [file.name]: 'success' }))
27
+ }
28
+
29
+ reader.readAsArrayBuffer(file)
30
+ })
31
+
32
+ switch (rejectedFiles[0]?.errors[0]?.code) {
33
+ case FilesErrorCode.FileInvalidType:
34
+ Notification({
35
+ intent: 'error',
36
+ text: 'Неверный формат файла. Загрузите в формате jpg/png/pdf'
37
+ })
38
+ break
39
+ case FilesErrorCode.FileTooLarge:
40
+ Notification({
41
+ intent: 'error',
42
+ text: `Файл слишком большой. Максимальный размер ${dropzoneOptions.maxSize} МБ`
43
+ })
44
+ break
45
+ case FilesErrorCode.TooManyFiles:
46
+ Notification({
47
+ intent: 'error',
48
+ text: `Вы загрузили слишком много файлов. Максимальное количество ${dropzoneOptions.maxFiles}`
49
+ })
50
+ break
51
+ default:
52
+ break
53
+ }
54
+
55
+ const updatedFiles = [...controlledFiles, ...acceptedFiles]
56
+
57
+ onValueChange(updatedFiles)
58
+ }
59
+
60
+ const dropzoneState = useDropzone({
61
+ onDrop,
62
+ ...dropzoneOptions
63
+ })
64
+
65
+ return { filesStatus, removeFile, dropzoneState }
66
+ }
@@ -0,0 +1 @@
1
+ export { defaultDropzoneOptions, FilesErrorCode } from './helpers'
@@ -0,0 +1,22 @@
1
+ export type TClassesUploaderMain = {
2
+ wrapperMainContent: string
3
+ borderContent: string
4
+ wrapperTextContent: string
5
+ uploaderIcon: string
6
+ selectFileText: string
7
+ selectFileTextSpan: string
8
+ uploaderInput: string
9
+ message: string
10
+ }
11
+
12
+ export type TClassesUploaderFiles = {
13
+ filesWrapperContent: string
14
+ fileWrapperContent: string
15
+ fileContent: string
16
+ fileDeleteIcon: string
17
+ fileText: string
18
+ fileButtonDeleteWrapper: string
19
+ fileButtonDelete: string
20
+ }
21
+
22
+ export type TClassesUploader = TClassesUploaderFiles & TClassesUploaderMain
@@ -0,0 +1,35 @@
1
+ import { type TClassesUploader } from '../model/types'
2
+ import { Filename } from './Filename'
3
+ import { Icon } from '$/shared/ui/icon'
4
+ import { Loader } from '$/shared/ui/Loader'
5
+ import { cn } from '$/shared/utils'
6
+
7
+ interface IFileProps {
8
+ filesStatus: {
9
+ [key: string]: 'loading' | 'success' | 'error'
10
+ }
11
+ removeFile: (index: number) => void
12
+ classes?: TClassesUploader
13
+ file: File
14
+ index: number
15
+ }
16
+
17
+ export const File = ({ filesStatus, removeFile, classes, file, index }: IFileProps) => {
18
+ const fileSizeMb = file.size / 1024 / 1024
19
+
20
+ return (
21
+ <li key={file.name} className={cn('flex h-6 items-center justify-between p-1', classes?.fileWrapperContent)}>
22
+ <div className={cn('flex items-center gap-2', classes?.fileContent)}>
23
+ {filesStatus[file.name] === 'loading' && <Loader size='sm' />}
24
+ {filesStatus[file.name] === 'success' && <Icon name='general/check' className='text-icon-positive-default' />}
25
+ <Filename file={file} classes={classes} />
26
+ </div>
27
+ <div className={cn('flex items-center gap-2', classes?.fileButtonDeleteWrapper)}>
28
+ <p className='desk-body-regular-m text-color-blue-grey-600'>{`${fileSizeMb.toFixed(1)} MB`}</p>
29
+ <button className={cn('cursor-pointer', classes?.fileButtonDelete)} onClick={() => removeFile(index)}>
30
+ <Icon name='general/close' className={cn('text-icon-blue-grey-600', classes?.fileDeleteIcon)} />
31
+ </button>
32
+ </div>
33
+ </li>
34
+ )
35
+ }
@@ -0,0 +1,40 @@
1
+ import { useEffect, useRef, useState } from 'react'
2
+ import { type TClassesUploader } from '../model/types'
3
+ import { Hint } from '$/shared/ui/Hint'
4
+ import { cn } from '$/shared/utils'
5
+
6
+ interface IFileNameProps {
7
+ file: File
8
+ classes?: TClassesUploader
9
+ }
10
+
11
+ export const Filename = ({ file, classes }: IFileNameProps) => {
12
+ const fileRef = useRef<HTMLParagraphElement>(null)
13
+ const [isOverflow, setIsOverflow] = useState(false)
14
+
15
+ useEffect(() => {
16
+ if (fileRef.current) {
17
+ setIsOverflow(fileRef.current.clientWidth > 300)
18
+ }
19
+ }, [file])
20
+
21
+ return (
22
+ <>
23
+ {isOverflow ? (
24
+ <Hint
25
+ triggerElement={
26
+ <p ref={fileRef} className={cn('desk-body-regular-m max-w-[300px] truncate text-color-dark')}>
27
+ {file.name}
28
+ </p>
29
+ }
30
+ >
31
+ {file.name}
32
+ </Hint>
33
+ ) : (
34
+ <p ref={fileRef} className={cn('desk-body-regular-m text-color-dark', classes?.fileText)}>
35
+ {file.name}
36
+ </p>
37
+ )}
38
+ </>
39
+ )
40
+ }
@@ -0,0 +1,30 @@
1
+ import { type HTMLAttributes } from 'react'
2
+ import { type TClassesUploader } from '../model/types'
3
+ import { File } from './File'
4
+ import { cn } from '$/shared/utils'
5
+
6
+ interface IUploaderContentProps extends HTMLAttributes<HTMLDivElement> {
7
+ controlledFiles: File[]
8
+ filesStatus: {
9
+ [key: string]: 'loading' | 'success' | 'error'
10
+ }
11
+ removeFile: (index: number) => void
12
+ classes?: TClassesUploader
13
+ }
14
+
15
+ export const Files = ({ controlledFiles, filesStatus, classes, removeFile }: IUploaderContentProps) => {
16
+ const isFilesExist = controlledFiles && controlledFiles.length > 0
17
+
18
+ if (isFilesExist) {
19
+ return (
20
+ <ul className={cn('flex max-w-[476px] flex-col gap-1 px-1', classes?.filesWrapperContent)}>
21
+ {controlledFiles.map((file, index) => (
22
+ // eslint-disable-next-line react/no-array-index-key
23
+ <File key={index} file={file} index={index} filesStatus={filesStatus} removeFile={removeFile} classes={classes} />
24
+ ))}
25
+ </ul>
26
+ )
27
+ }
28
+
29
+ return null
30
+ }
@@ -0,0 +1,48 @@
1
+ import { forwardRef, type Ref } from 'react'
2
+ import { type DropzoneRootProps } from 'react-dropzone'
3
+ import { type FieldError, type FieldValues, type Path } from 'react-hook-form'
4
+ import { type TClassesUploader } from '../model/types'
5
+ import { Icon } from '$/shared/ui/icon'
6
+ import { cn } from '$/shared/utils'
7
+
8
+ export interface IFileInputProps<T extends FieldValues> {
9
+ dropzoneState: DropzoneRootProps
10
+ disabled?: boolean
11
+ error?: FieldError
12
+ classes?: Partial<TClassesUploader>
13
+ name?: Path<T>
14
+ }
15
+
16
+ export const Input = forwardRef(function Input<T extends FieldValues>(
17
+ { dropzoneState, classes, disabled, error, name }: IFileInputProps<T>,
18
+ ref: Ref<HTMLDivElement>
19
+ ) {
20
+ const dropzoneProps = dropzoneState.getRootProps()
21
+ return (
22
+ <div
23
+ className={cn(
24
+ 'flex h-[64px] w-[476px] rounded-sm bg-[length:100%_100%] bg-no-repeat hover:border hover:border-primary-hover hover:bg-color-primary-tr-hover focus-visible:border focus-visible:border-primary-hover focus-visible:bg-color-primary-tr-hover focus-visible:outline-none active:border active:border-primary-hover active:bg-color-primary-tr-pressed [&:not(:hover)(:focus-visible)(:active)(:disabled)]:bg-[url("../../static/files/border.svg")]',
25
+ classes?.borderContent,
26
+ { 'bg-color-primary-light-default opacity-50': disabled },
27
+ { '[&:not(:hover)(:focus-visible)(:active)(:disabled)]:bg-[url("../../static/files/borderError.svg")]': error }
28
+ )}
29
+ ref={ref}
30
+ {...dropzoneProps}
31
+ >
32
+ <div className={cn('flex h-full w-full items-center justify-center gap-2', classes?.wrapperTextContent)}>
33
+ <Icon name='files/upload' className={cn('text-icon-primary-default', classes?.uploaderIcon)} />
34
+ <p className={cn('desk-body-regular-l select-none text-icon-primary-default', classes?.wrapperTextContent)}>
35
+ Выберите файл
36
+ <span className={cn('text-icon-blue-grey-600', classes?.selectFileTextSpan)}> или перетащите сюда</span>
37
+ </p>
38
+ </div>
39
+ <input
40
+ name={name}
41
+ ref={dropzoneState.inputRef}
42
+ disabled={disabled}
43
+ {...dropzoneState.getInputProps()}
44
+ className={cn(classes?.uploaderInput)}
45
+ />
46
+ </div>
47
+ )
48
+ })
@@ -0,0 +1,58 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { type DropzoneOptions } from 'react-dropzone'
5
+ import { type FieldError, type FieldValues, type Path } from 'react-hook-form'
6
+ import { MessageView } from '../../ui'
7
+ import { defaultDropzoneOptions } from '../model'
8
+ import { useUploader } from '../model/hooks/useUploader'
9
+ import { type TClassesUploader } from '../model/types'
10
+ import { Files } from './Files'
11
+ import { Input } from './Input'
12
+ import { cn } from '$/shared/utils'
13
+
14
+ export interface IUploaderProps<T extends FieldValues> extends React.HTMLAttributes<HTMLDivElement> {
15
+ controlledFiles: File[]
16
+ dropzoneOptions: DropzoneOptions
17
+ helperText?: string
18
+ onValueChange: (f: File[]) => void
19
+ disabled?: boolean
20
+ error?: FieldError | undefined
21
+ classes?: Partial<TClassesUploader>
22
+ name: Path<T>
23
+ }
24
+
25
+ export const Uploader = React.forwardRef(function InputControl<T extends FieldValues>(
26
+ {
27
+ dropzoneOptions = defaultDropzoneOptions,
28
+ controlledFiles,
29
+ onValueChange,
30
+ helperText,
31
+ disabled,
32
+ error,
33
+ classes,
34
+ name
35
+ }: IUploaderProps<T>,
36
+ ref: React.Ref<HTMLDivElement>
37
+ ) {
38
+ const { filesStatus, removeFile, dropzoneState } = useUploader({
39
+ dropzoneOptions,
40
+ controlledFiles,
41
+ onValueChange
42
+ })
43
+
44
+ return (
45
+ <>
46
+ <div className={cn('relative mb-3 w-[476px]', classes?.wrapperMainContent)}>
47
+ <Input ref={ref} name={name} classes={classes} error={error} disabled={disabled} dropzoneState={dropzoneState} />
48
+ <MessageView
49
+ className={cn(classes?.message)}
50
+ intent={error?.message ? 'error' : 'simple'}
51
+ text={error?.message || helperText}
52
+ disabled={disabled}
53
+ />
54
+ </div>
55
+ <Files filesStatus={filesStatus} removeFile={removeFile} controlledFiles={controlledFiles} />
56
+ </>
57
+ )
58
+ })
@@ -0,0 +1,3 @@
1
+ export { Input } from './Input'
2
+ export { Uploader } from './Uploader'
3
+ export { Files } from './Files'
@@ -0,0 +1,88 @@
1
+ import * as React from 'react'
2
+ import { Controller, type FieldValues } from 'react-hook-form'
3
+ import { NumericFormat } from 'react-number-format'
4
+ import { type TControlledInputProps, type TInputCommonProps } from '../model'
5
+ import { FieldContainer, FieldWrapper, MessageView } from '../ui'
6
+ import { currencyOptionsList, getDelimiterForCurrency } from './model/helpers'
7
+ import { useInputCurrency } from './model/useInputCurrency'
8
+ import { MenuTrigger, OptionList } from './ui'
9
+ import { useClickOutside } from '$/shared/hooks'
10
+ import { cn } from '$/shared/utils'
11
+
12
+ export type TCurrencyVariant = 'euro' | 'dollars' | 'rubles' | 'yuan' | 'dirhams'
13
+ export interface ICurrencyOption {
14
+ ruName: string
15
+ engName: string
16
+ currency: TCurrencyVariant
17
+ }
18
+
19
+ export interface InputCurrencyControlProps<T extends FieldValues> extends TControlledInputProps<T>, TInputCommonProps {
20
+ classes?: any // TODO ADD CLASSES AND CONNECT WITH BACKEND API TO GET CURRENCY
21
+ defaultCurrency?: TCurrencyVariant
22
+ }
23
+
24
+ export const InputCurrencyControl = <T extends FieldValues>({
25
+ label,
26
+ size = 'full',
27
+ helperText,
28
+ control,
29
+ classes,
30
+ disabled,
31
+ defaultCurrency = 'rubles',
32
+ ...props
33
+ }: InputCurrencyControlProps<T>) => {
34
+ const inputId = React.useId()
35
+ const containerRef = React.useRef<HTMLDivElement | null>(null)
36
+ const { menuIsOpen, currentCurrencyOption, onSelectOption, onToggleMenu, onCloseMenu } = useInputCurrency(defaultCurrency)
37
+
38
+ useClickOutside(containerRef, onCloseMenu)
39
+
40
+ return (
41
+ <Controller
42
+ control={control}
43
+ name={props.name}
44
+ render={({ field: { onChange, ref, value }, fieldState: { error } }) => (
45
+ <FieldContainer size={size} classes={classes}>
46
+ <FieldWrapper
47
+ fieldId={inputId}
48
+ label={label}
49
+ classes={classes}
50
+ disabled={disabled}
51
+ value={value}
52
+ error={!!error?.message}
53
+ >
54
+ <div ref={containerRef} className='relative flex w-full items-center'>
55
+ <NumericFormat
56
+ thousandsGroupStyle='wan'
57
+ thousandSeparator=' '
58
+ prefix={getDelimiterForCurrency(currentCurrencyOption.currency)}
59
+ getInputRef={ref}
60
+ allowNegative={false}
61
+ id={inputId}
62
+ aria-invalid={error?.message ? 'true' : 'false'}
63
+ className={cn(
64
+ 'desk-body-regular-l h-[56px] w-full rounded-md bg-color-transparent px-4 pt-5 text-color-dark outline-none transition-all',
65
+ classes?.input
66
+ )}
67
+ disabled={disabled}
68
+ value={value?.toString()}
69
+ onChange={onChange}
70
+ {...props}
71
+ />
72
+
73
+ <MenuTrigger onToggleMenu={onToggleMenu} currentCurrencyOption={currentCurrencyOption} />
74
+ <OptionList menuIsOpen={menuIsOpen} optionsList={currencyOptionsList} onSelectOption={onSelectOption} />
75
+ </div>
76
+ </FieldWrapper>
77
+
78
+ <MessageView
79
+ className={cn(classes?.message)}
80
+ intent={error?.message ? 'error' : 'simple'}
81
+ text={error?.message || helperText}
82
+ disabled={disabled}
83
+ />
84
+ </FieldContainer>
85
+ )}
86
+ />
87
+ )
88
+ }
@@ -0,0 +1 @@
1
+ export { InputCurrencyControl, type InputCurrencyControlProps } from './InputCurrencyControl'
@@ -0,0 +1,46 @@
1
+ import type { ICurrencyOption, TCurrencyVariant } from '../InputCurrencyControl'
2
+
3
+ export const getDelimiterForCurrency = (currency: TCurrencyVariant) => {
4
+ switch (currency) {
5
+ case 'euro':
6
+ return '\u20AC '
7
+ case 'dollars':
8
+ return '$ '
9
+ case 'rubles':
10
+ return '\u20BD '
11
+ case 'yuan':
12
+ return '¥ '
13
+ case 'dirhams':
14
+ return 'د.إ '
15
+ default:
16
+ return ''
17
+ }
18
+ }
19
+
20
+ export const currencyOptionsList: ICurrencyOption[] = [
21
+ {
22
+ ruName: 'Евро',
23
+ engName: 'Eur',
24
+ currency: 'euro'
25
+ },
26
+ {
27
+ ruName: 'Доллар США',
28
+ engName: 'USD',
29
+ currency: 'dollars'
30
+ },
31
+ {
32
+ ruName: 'Российский рубль',
33
+ engName: 'RUB',
34
+ currency: 'rubles'
35
+ },
36
+ {
37
+ ruName: 'Китайский Юань',
38
+ engName: 'CNY',
39
+ currency: 'yuan'
40
+ },
41
+ {
42
+ ruName: 'Дирхам ОАЭ',
43
+ engName: 'AED',
44
+ currency: 'dirhams'
45
+ }
46
+ ]
@@ -0,0 +1,33 @@
1
+ import React from 'react'
2
+ import { type ICurrencyOption, type TCurrencyVariant } from '../InputCurrencyControl'
3
+ import { currencyOptionsList } from './helpers'
4
+
5
+ type TUseInputCurrencyReturn = {
6
+ menuIsOpen: boolean
7
+ currentCurrencyOption: ICurrencyOption
8
+ onSelectOption: (option: ICurrencyOption) => void
9
+ onToggleMenu: () => void
10
+ onCloseMenu: () => void
11
+ }
12
+
13
+ export const useInputCurrency = (defaultCurrency?: TCurrencyVariant): TUseInputCurrencyReturn => {
14
+ const [menuIsOpen, setMenuIsOpen] = React.useState(false)
15
+ const [currentCurrencyOption, setCurrencyOption] = React.useState<ICurrencyOption>(
16
+ () => currencyOptionsList?.find((option) => option?.currency === defaultCurrency) || currencyOptionsList[0]
17
+ )
18
+ const onSelectOption = (option: ICurrencyOption) => {
19
+ setCurrencyOption(option)
20
+ setMenuIsOpen(false)
21
+ }
22
+
23
+ const onToggleMenu = () => setMenuIsOpen((prev) => !prev)
24
+ const onCloseMenu = () => setMenuIsOpen(false)
25
+
26
+ return {
27
+ menuIsOpen,
28
+ currentCurrencyOption,
29
+ onSelectOption,
30
+ onToggleMenu,
31
+ onCloseMenu
32
+ }
33
+ }
@@ -0,0 +1,20 @@
1
+ import { type ICurrencyOption } from '../InputCurrencyControl'
2
+ import { Icon } from '$/shared/ui'
3
+
4
+ interface IMenuTriggerProps {
5
+ onToggleMenu: () => void
6
+ currentCurrencyOption: ICurrencyOption
7
+ }
8
+
9
+ export const MenuTrigger = ({ onToggleMenu, currentCurrencyOption }: IMenuTriggerProps) => {
10
+ return (
11
+ <button
12
+ type='button'
13
+ onClick={onToggleMenu}
14
+ className='desk-body-regular-l flex items-center gap-1 border-l border-solid border-blue-grey-500 pl-2 pr-4'
15
+ >
16
+ <span className='uppercase text-color-tetriary'>{currentCurrencyOption.engName}</span>
17
+ <Icon name='arrows/arrowRight' className='rotate-90 text-icon-blue-grey-700' />
18
+ </button>
19
+ )
20
+ }
@@ -0,0 +1,29 @@
1
+ import { type ICurrencyOption } from '../InputCurrencyControl'
2
+ import { cn } from '$/shared/utils'
3
+
4
+ interface IOptionListProps {
5
+ optionsList: ICurrencyOption[]
6
+ menuIsOpen: boolean
7
+ onSelectOption: (option: ICurrencyOption) => void
8
+ }
9
+
10
+ export const OptionList = ({ optionsList, menuIsOpen, onSelectOption }: IOptionListProps) => {
11
+ return (
12
+ <div
13
+ className={cn(
14
+ 'invisible absolute top-[64px] z-10 h-[208px] w-full scale-0 rounded-sm bg-color-white p-1 opacity-0 shadow-sm transition-all',
15
+ { 'opacity-1 scale-1 visible': menuIsOpen }
16
+ )}
17
+ >
18
+ {optionsList?.map((option) => (
19
+ <div
20
+ key={option.engName}
21
+ onClick={() => onSelectOption(option)}
22
+ className='desk-body-regular-l flex cursor-pointer items-center gap-2 rounded-sm bg-color-white p-2 hover:bg-color-blue-grey-100'
23
+ >
24
+ <span>{option.ruName}</span> <span className='uppercase'>({option.engName})</span>
25
+ </div>
26
+ ))}
27
+ </div>
28
+ )
29
+ }
@@ -0,0 +1,2 @@
1
+ export { MenuTrigger } from './MenuTrigger'
2
+ export { OptionList } from './OptionList'