@cloud-ru/ds-fields 1.0.0

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 (314) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +201 -0
  3. package/README.md +888 -0
  4. package/dist/cjs/components/FieldColor/FieldColor.d.ts +48 -0
  5. package/dist/cjs/components/FieldColor/FieldColor.js +147 -0
  6. package/dist/cjs/components/FieldColor/index.d.ts +1 -0
  7. package/dist/cjs/components/FieldColor/index.js +17 -0
  8. package/dist/cjs/components/FieldColor/styles.module.css +79 -0
  9. package/dist/cjs/components/FieldColor/utils.d.ts +7 -0
  10. package/dist/cjs/components/FieldColor/utils.js +19 -0
  11. package/dist/cjs/components/FieldCombo/FieldCombo.d.ts +91 -0
  12. package/dist/cjs/components/FieldCombo/FieldCombo.js +200 -0
  13. package/dist/cjs/components/FieldCombo/index.d.ts +1 -0
  14. package/dist/cjs/components/FieldCombo/index.js +17 -0
  15. package/dist/cjs/components/FieldDate/FieldDate.d.ts +2 -0
  16. package/dist/cjs/components/FieldDate/FieldDate.js +369 -0
  17. package/dist/cjs/components/FieldDate/constants.d.ts +10 -0
  18. package/dist/cjs/components/FieldDate/constants.js +13 -0
  19. package/dist/cjs/components/FieldDate/index.d.ts +3 -0
  20. package/dist/cjs/components/FieldDate/index.js +19 -0
  21. package/dist/cjs/components/FieldDate/mask.d.ts +20 -0
  22. package/dist/cjs/components/FieldDate/mask.js +117 -0
  23. package/dist/cjs/components/FieldDate/styles.module.css +43 -0
  24. package/dist/cjs/components/FieldDate/types.d.ts +100 -0
  25. package/dist/cjs/components/FieldDate/types.js +2 -0
  26. package/dist/cjs/components/FieldDate/utils.d.ts +3 -0
  27. package/dist/cjs/components/FieldDate/utils.js +8 -0
  28. package/dist/cjs/components/FieldSecure/FieldSecure.d.ts +64 -0
  29. package/dist/cjs/components/FieldSecure/FieldSecure.js +158 -0
  30. package/dist/cjs/components/FieldSecure/index.d.ts +1 -0
  31. package/dist/cjs/components/FieldSecure/index.js +17 -0
  32. package/dist/cjs/components/FieldSelect/FieldSelect.d.ts +2 -0
  33. package/dist/cjs/components/FieldSelect/FieldSelect.js +360 -0
  34. package/dist/cjs/components/FieldSelect/constants.d.ts +4 -0
  35. package/dist/cjs/components/FieldSelect/constants.js +7 -0
  36. package/dist/cjs/components/FieldSelect/index.d.ts +3 -0
  37. package/dist/cjs/components/FieldSelect/index.js +19 -0
  38. package/dist/cjs/components/FieldSelect/styles.module.css +92 -0
  39. package/dist/cjs/components/FieldSelect/types.d.ts +197 -0
  40. package/dist/cjs/components/FieldSelect/types.js +2 -0
  41. package/dist/cjs/components/FieldSelect/utils.d.ts +19 -0
  42. package/dist/cjs/components/FieldSelect/utils.js +104 -0
  43. package/dist/cjs/components/FieldSlider/FieldSlider.d.ts +92 -0
  44. package/dist/cjs/components/FieldSlider/FieldSlider.js +196 -0
  45. package/dist/cjs/components/FieldSlider/index.d.ts +2 -0
  46. package/dist/cjs/components/FieldSlider/index.js +18 -0
  47. package/dist/cjs/components/FieldSlider/styles.module.css +44 -0
  48. package/dist/cjs/components/FieldSlider/types.d.ts +4 -0
  49. package/dist/cjs/components/FieldSlider/types.js +2 -0
  50. package/dist/cjs/components/FieldSlider/utils/generateAllowedValues.d.ts +1 -0
  51. package/dist/cjs/components/FieldSlider/utils/generateAllowedValues.js +16 -0
  52. package/dist/cjs/components/FieldSlider/utils/getClosestMark.d.ts +4 -0
  53. package/dist/cjs/components/FieldSlider/utils/getClosestMark.js +16 -0
  54. package/dist/cjs/components/FieldSlider/utils/getMarkLabel.d.ts +2 -0
  55. package/dist/cjs/components/FieldSlider/utils/getMarkLabel.js +7 -0
  56. package/dist/cjs/components/FieldSlider/utils/getTextFieldValue.d.ts +2 -0
  57. package/dist/cjs/components/FieldSlider/utils/getTextFieldValue.js +9 -0
  58. package/dist/cjs/components/FieldSlider/utils/index.d.ts +5 -0
  59. package/dist/cjs/components/FieldSlider/utils/index.js +21 -0
  60. package/dist/cjs/components/FieldSlider/utils/isMarkObject.d.ts +6 -0
  61. package/dist/cjs/components/FieldSlider/utils/isMarkObject.js +6 -0
  62. package/dist/cjs/components/FieldStepper/FieldStepper.d.ts +81 -0
  63. package/dist/cjs/components/FieldStepper/FieldStepper.js +160 -0
  64. package/dist/cjs/components/FieldStepper/index.d.ts +1 -0
  65. package/dist/cjs/components/FieldStepper/index.js +17 -0
  66. package/dist/cjs/components/FieldStepper/styles.module.css +78 -0
  67. package/dist/cjs/components/FieldStepper/utils.d.ts +14 -0
  68. package/dist/cjs/components/FieldStepper/utils.js +45 -0
  69. package/dist/cjs/components/FieldText/FieldText.d.ts +11 -0
  70. package/dist/cjs/components/FieldText/FieldText.js +14 -0
  71. package/dist/cjs/components/FieldText/index.d.ts +1 -0
  72. package/dist/cjs/components/FieldText/index.js +17 -0
  73. package/dist/cjs/components/FieldTextArea/FieldTextArea.d.ts +95 -0
  74. package/dist/cjs/components/FieldTextArea/FieldTextArea.js +114 -0
  75. package/dist/cjs/components/FieldTextArea/index.d.ts +1 -0
  76. package/dist/cjs/components/FieldTextArea/index.js +17 -0
  77. package/dist/cjs/components/FieldTextArea/styles.module.css +123 -0
  78. package/dist/cjs/components/FieldTime/FieldTime.d.ts +74 -0
  79. package/dist/cjs/components/FieldTime/FieldTime.js +167 -0
  80. package/dist/cjs/components/FieldTime/index.d.ts +2 -0
  81. package/dist/cjs/components/FieldTime/index.js +17 -0
  82. package/dist/cjs/components/FieldTime/mask.d.ts +3 -0
  83. package/dist/cjs/components/FieldTime/mask.js +24 -0
  84. package/dist/cjs/components/FieldTime/styles.module.css +3 -0
  85. package/dist/cjs/components/index.d.ts +14 -0
  86. package/dist/cjs/components/index.js +32 -0
  87. package/dist/cjs/components/shared/helperComponents/FieldElementButton/FieldElementButton.d.ts +35 -0
  88. package/dist/cjs/components/shared/helperComponents/FieldElementButton/FieldElementButton.js +47 -0
  89. package/dist/cjs/components/shared/helperComponents/FieldElementButton/FieldElementButtonList.d.ts +26 -0
  90. package/dist/cjs/components/shared/helperComponents/FieldElementButton/FieldElementButtonList.js +24 -0
  91. package/dist/cjs/components/shared/helperComponents/FieldElementButton/constants.d.ts +9 -0
  92. package/dist/cjs/components/shared/helperComponents/FieldElementButton/constants.js +12 -0
  93. package/dist/cjs/components/shared/helperComponents/FieldElementButton/index.d.ts +4 -0
  94. package/dist/cjs/components/shared/helperComponents/FieldElementButton/index.js +20 -0
  95. package/dist/cjs/components/shared/helperComponents/FieldElementButton/styles.module.css +204 -0
  96. package/dist/cjs/components/shared/helperComponents/FieldElementButton/types.d.ts +4 -0
  97. package/dist/cjs/components/shared/helperComponents/FieldElementButton/types.js +2 -0
  98. package/dist/cjs/components/shared/helperComponents/FieldShell/FieldShell.d.ts +43 -0
  99. package/dist/cjs/components/shared/helperComponents/FieldShell/FieldShell.js +22 -0
  100. package/dist/cjs/components/shared/helperComponents/FieldShell/index.d.ts +1 -0
  101. package/dist/cjs/components/shared/helperComponents/FieldShell/index.js +17 -0
  102. package/dist/cjs/components/shared/helperComponents/FieldShell/styles.module.css +11 -0
  103. package/dist/cjs/components/shared/hooks/useCopyButton.d.ts +20 -0
  104. package/dist/cjs/components/shared/hooks/useCopyButton.js +58 -0
  105. package/dist/cjs/components/shared/index.d.ts +4 -0
  106. package/dist/cjs/components/shared/index.js +20 -0
  107. package/dist/cjs/components/shared/styles.module.css +673 -0
  108. package/dist/cjs/components/shared/utils.d.ts +69 -0
  109. package/dist/cjs/components/shared/utils.js +104 -0
  110. package/dist/cjs/constants.d.ts +61 -0
  111. package/dist/cjs/constants.js +76 -0
  112. package/dist/cjs/hooks/index.d.ts +1 -0
  113. package/dist/cjs/hooks/index.js +17 -0
  114. package/dist/cjs/hooks/useAdaptiveAutoFocus.d.ts +7 -0
  115. package/dist/cjs/hooks/useAdaptiveAutoFocus.js +10 -0
  116. package/dist/cjs/index.d.ts +3 -0
  117. package/dist/cjs/index.js +19 -0
  118. package/dist/cjs/package.json +3 -0
  119. package/dist/cjs/segments/index.d.ts +2 -0
  120. package/dist/cjs/segments/index.js +18 -0
  121. package/dist/cjs/segments/segments.d.ts +22 -0
  122. package/dist/cjs/segments/segments.js +46 -0
  123. package/dist/cjs/segments/useSegmentedMask.d.ts +43 -0
  124. package/dist/cjs/segments/useSegmentedMask.js +282 -0
  125. package/dist/cjs/style.css +1350 -0
  126. package/dist/esm/components/FieldColor/FieldColor.d.ts +48 -0
  127. package/dist/esm/components/FieldColor/FieldColor.js +141 -0
  128. package/dist/esm/components/FieldColor/index.d.ts +1 -0
  129. package/dist/esm/components/FieldColor/index.js +1 -0
  130. package/dist/esm/components/FieldColor/styles.module.css +79 -0
  131. package/dist/esm/components/FieldColor/utils.d.ts +7 -0
  132. package/dist/esm/components/FieldColor/utils.js +16 -0
  133. package/dist/esm/components/FieldCombo/FieldCombo.d.ts +91 -0
  134. package/dist/esm/components/FieldCombo/FieldCombo.js +194 -0
  135. package/dist/esm/components/FieldCombo/index.d.ts +1 -0
  136. package/dist/esm/components/FieldCombo/index.js +1 -0
  137. package/dist/esm/components/FieldDate/FieldDate.d.ts +2 -0
  138. package/dist/esm/components/FieldDate/FieldDate.js +363 -0
  139. package/dist/esm/components/FieldDate/constants.d.ts +10 -0
  140. package/dist/esm/components/FieldDate/constants.js +10 -0
  141. package/dist/esm/components/FieldDate/index.d.ts +3 -0
  142. package/dist/esm/components/FieldDate/index.js +3 -0
  143. package/dist/esm/components/FieldDate/mask.d.ts +20 -0
  144. package/dist/esm/components/FieldDate/mask.js +111 -0
  145. package/dist/esm/components/FieldDate/styles.module.css +43 -0
  146. package/dist/esm/components/FieldDate/types.d.ts +100 -0
  147. package/dist/esm/components/FieldDate/types.js +1 -0
  148. package/dist/esm/components/FieldDate/utils.d.ts +3 -0
  149. package/dist/esm/components/FieldDate/utils.js +5 -0
  150. package/dist/esm/components/FieldSecure/FieldSecure.d.ts +64 -0
  151. package/dist/esm/components/FieldSecure/FieldSecure.js +152 -0
  152. package/dist/esm/components/FieldSecure/index.d.ts +1 -0
  153. package/dist/esm/components/FieldSecure/index.js +1 -0
  154. package/dist/esm/components/FieldSelect/FieldSelect.d.ts +2 -0
  155. package/dist/esm/components/FieldSelect/FieldSelect.js +354 -0
  156. package/dist/esm/components/FieldSelect/constants.d.ts +4 -0
  157. package/dist/esm/components/FieldSelect/constants.js +4 -0
  158. package/dist/esm/components/FieldSelect/index.d.ts +3 -0
  159. package/dist/esm/components/FieldSelect/index.js +3 -0
  160. package/dist/esm/components/FieldSelect/styles.module.css +92 -0
  161. package/dist/esm/components/FieldSelect/types.d.ts +197 -0
  162. package/dist/esm/components/FieldSelect/types.js +1 -0
  163. package/dist/esm/components/FieldSelect/utils.d.ts +19 -0
  164. package/dist/esm/components/FieldSelect/utils.js +93 -0
  165. package/dist/esm/components/FieldSlider/FieldSlider.d.ts +92 -0
  166. package/dist/esm/components/FieldSlider/FieldSlider.js +190 -0
  167. package/dist/esm/components/FieldSlider/index.d.ts +2 -0
  168. package/dist/esm/components/FieldSlider/index.js +2 -0
  169. package/dist/esm/components/FieldSlider/styles.module.css +44 -0
  170. package/dist/esm/components/FieldSlider/types.d.ts +4 -0
  171. package/dist/esm/components/FieldSlider/types.js +1 -0
  172. package/dist/esm/components/FieldSlider/utils/generateAllowedValues.d.ts +1 -0
  173. package/dist/esm/components/FieldSlider/utils/generateAllowedValues.js +13 -0
  174. package/dist/esm/components/FieldSlider/utils/getClosestMark.d.ts +4 -0
  175. package/dist/esm/components/FieldSlider/utils/getClosestMark.js +13 -0
  176. package/dist/esm/components/FieldSlider/utils/getMarkLabel.d.ts +2 -0
  177. package/dist/esm/components/FieldSlider/utils/getMarkLabel.js +4 -0
  178. package/dist/esm/components/FieldSlider/utils/getTextFieldValue.d.ts +2 -0
  179. package/dist/esm/components/FieldSlider/utils/getTextFieldValue.js +6 -0
  180. package/dist/esm/components/FieldSlider/utils/index.d.ts +5 -0
  181. package/dist/esm/components/FieldSlider/utils/index.js +5 -0
  182. package/dist/esm/components/FieldSlider/utils/isMarkObject.d.ts +6 -0
  183. package/dist/esm/components/FieldSlider/utils/isMarkObject.js +3 -0
  184. package/dist/esm/components/FieldStepper/FieldStepper.d.ts +81 -0
  185. package/dist/esm/components/FieldStepper/FieldStepper.js +154 -0
  186. package/dist/esm/components/FieldStepper/index.d.ts +1 -0
  187. package/dist/esm/components/FieldStepper/index.js +1 -0
  188. package/dist/esm/components/FieldStepper/styles.module.css +78 -0
  189. package/dist/esm/components/FieldStepper/utils.d.ts +14 -0
  190. package/dist/esm/components/FieldStepper/utils.js +38 -0
  191. package/dist/esm/components/FieldText/FieldText.d.ts +11 -0
  192. package/dist/esm/components/FieldText/FieldText.js +11 -0
  193. package/dist/esm/components/FieldText/index.d.ts +1 -0
  194. package/dist/esm/components/FieldText/index.js +1 -0
  195. package/dist/esm/components/FieldTextArea/FieldTextArea.d.ts +95 -0
  196. package/dist/esm/components/FieldTextArea/FieldTextArea.js +108 -0
  197. package/dist/esm/components/FieldTextArea/index.d.ts +1 -0
  198. package/dist/esm/components/FieldTextArea/index.js +1 -0
  199. package/dist/esm/components/FieldTextArea/styles.module.css +123 -0
  200. package/dist/esm/components/FieldTime/FieldTime.d.ts +74 -0
  201. package/dist/esm/components/FieldTime/FieldTime.js +161 -0
  202. package/dist/esm/components/FieldTime/index.d.ts +2 -0
  203. package/dist/esm/components/FieldTime/index.js +1 -0
  204. package/dist/esm/components/FieldTime/mask.d.ts +3 -0
  205. package/dist/esm/components/FieldTime/mask.js +20 -0
  206. package/dist/esm/components/FieldTime/styles.module.css +3 -0
  207. package/dist/esm/components/index.d.ts +14 -0
  208. package/dist/esm/components/index.js +12 -0
  209. package/dist/esm/components/shared/helperComponents/FieldElementButton/FieldElementButton.d.ts +35 -0
  210. package/dist/esm/components/shared/helperComponents/FieldElementButton/FieldElementButton.js +41 -0
  211. package/dist/esm/components/shared/helperComponents/FieldElementButton/FieldElementButtonList.d.ts +26 -0
  212. package/dist/esm/components/shared/helperComponents/FieldElementButton/FieldElementButtonList.js +21 -0
  213. package/dist/esm/components/shared/helperComponents/FieldElementButton/constants.d.ts +9 -0
  214. package/dist/esm/components/shared/helperComponents/FieldElementButton/constants.js +9 -0
  215. package/dist/esm/components/shared/helperComponents/FieldElementButton/index.d.ts +4 -0
  216. package/dist/esm/components/shared/helperComponents/FieldElementButton/index.js +4 -0
  217. package/dist/esm/components/shared/helperComponents/FieldElementButton/styles.module.css +204 -0
  218. package/dist/esm/components/shared/helperComponents/FieldElementButton/types.d.ts +4 -0
  219. package/dist/esm/components/shared/helperComponents/FieldElementButton/types.js +1 -0
  220. package/dist/esm/components/shared/helperComponents/FieldShell/FieldShell.d.ts +43 -0
  221. package/dist/esm/components/shared/helperComponents/FieldShell/FieldShell.js +16 -0
  222. package/dist/esm/components/shared/helperComponents/FieldShell/index.d.ts +1 -0
  223. package/dist/esm/components/shared/helperComponents/FieldShell/index.js +1 -0
  224. package/dist/esm/components/shared/helperComponents/FieldShell/styles.module.css +11 -0
  225. package/dist/esm/components/shared/hooks/useCopyButton.d.ts +20 -0
  226. package/dist/esm/components/shared/hooks/useCopyButton.js +55 -0
  227. package/dist/esm/components/shared/index.d.ts +4 -0
  228. package/dist/esm/components/shared/index.js +4 -0
  229. package/dist/esm/components/shared/styles.module.css +673 -0
  230. package/dist/esm/components/shared/utils.d.ts +69 -0
  231. package/dist/esm/components/shared/utils.js +91 -0
  232. package/dist/esm/constants.d.ts +61 -0
  233. package/dist/esm/constants.js +73 -0
  234. package/dist/esm/hooks/index.d.ts +1 -0
  235. package/dist/esm/hooks/index.js +1 -0
  236. package/dist/esm/hooks/useAdaptiveAutoFocus.d.ts +7 -0
  237. package/dist/esm/hooks/useAdaptiveAutoFocus.js +7 -0
  238. package/dist/esm/index.d.ts +3 -0
  239. package/dist/esm/index.js +3 -0
  240. package/dist/esm/segments/index.d.ts +2 -0
  241. package/dist/esm/segments/index.js +2 -0
  242. package/dist/esm/segments/segments.d.ts +22 -0
  243. package/dist/esm/segments/segments.js +43 -0
  244. package/dist/esm/segments/useSegmentedMask.d.ts +43 -0
  245. package/dist/esm/segments/useSegmentedMask.js +279 -0
  246. package/dist/esm/style.css +1350 -0
  247. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  248. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  249. package/package.json +62 -0
  250. package/src/components/FieldColor/FieldColor.tsx +426 -0
  251. package/src/components/FieldColor/index.ts +1 -0
  252. package/src/components/FieldColor/styles.module.scss +120 -0
  253. package/src/components/FieldColor/utils.ts +17 -0
  254. package/src/components/FieldCombo/FieldCombo.tsx +495 -0
  255. package/src/components/FieldCombo/index.ts +1 -0
  256. package/src/components/FieldDate/FieldDate.tsx +658 -0
  257. package/src/components/FieldDate/constants.ts +11 -0
  258. package/src/components/FieldDate/index.ts +3 -0
  259. package/src/components/FieldDate/mask.ts +123 -0
  260. package/src/components/FieldDate/styles.module.scss +56 -0
  261. package/src/components/FieldDate/types.ts +124 -0
  262. package/src/components/FieldDate/utils.ts +7 -0
  263. package/src/components/FieldSecure/FieldSecure.tsx +398 -0
  264. package/src/components/FieldSecure/index.ts +1 -0
  265. package/src/components/FieldSelect/FieldSelect.tsx +689 -0
  266. package/src/components/FieldSelect/constants.ts +4 -0
  267. package/src/components/FieldSelect/index.ts +3 -0
  268. package/src/components/FieldSelect/styles.module.scss +104 -0
  269. package/src/components/FieldSelect/types.ts +212 -0
  270. package/src/components/FieldSelect/utils.ts +134 -0
  271. package/src/components/FieldSlider/FieldSlider.tsx +502 -0
  272. package/src/components/FieldSlider/index.ts +2 -0
  273. package/src/components/FieldSlider/styles.module.scss +47 -0
  274. package/src/components/FieldSlider/types.ts +5 -0
  275. package/src/components/FieldSlider/utils/generateAllowedValues.ts +13 -0
  276. package/src/components/FieldSlider/utils/getClosestMark.ts +21 -0
  277. package/src/components/FieldSlider/utils/getMarkLabel.ts +7 -0
  278. package/src/components/FieldSlider/utils/getTextFieldValue.ts +8 -0
  279. package/src/components/FieldSlider/utils/index.ts +5 -0
  280. package/src/components/FieldSlider/utils/isMarkObject.ts +7 -0
  281. package/src/components/FieldStepper/FieldStepper.tsx +457 -0
  282. package/src/components/FieldStepper/index.ts +1 -0
  283. package/src/components/FieldStepper/styles.module.scss +98 -0
  284. package/src/components/FieldStepper/utils.ts +44 -0
  285. package/src/components/FieldText/FieldText.tsx +27 -0
  286. package/src/components/FieldText/index.ts +1 -0
  287. package/src/components/FieldTextArea/FieldTextArea.tsx +374 -0
  288. package/src/components/FieldTextArea/index.ts +1 -0
  289. package/src/components/FieldTextArea/styles.module.scss +192 -0
  290. package/src/components/FieldTime/FieldTime.tsx +401 -0
  291. package/src/components/FieldTime/index.ts +2 -0
  292. package/src/components/FieldTime/mask.ts +22 -0
  293. package/src/components/FieldTime/styles.module.scss +3 -0
  294. package/src/components/index.ts +14 -0
  295. package/src/components/shared/helperComponents/FieldElementButton/FieldElementButton.tsx +127 -0
  296. package/src/components/shared/helperComponents/FieldElementButton/FieldElementButtonList.tsx +72 -0
  297. package/src/components/shared/helperComponents/FieldElementButton/constants.ts +10 -0
  298. package/src/components/shared/helperComponents/FieldElementButton/index.ts +4 -0
  299. package/src/components/shared/helperComponents/FieldElementButton/styles.module.scss +137 -0
  300. package/src/components/shared/helperComponents/FieldElementButton/types.ts +6 -0
  301. package/src/components/shared/helperComponents/FieldShell/FieldShell.tsx +100 -0
  302. package/src/components/shared/helperComponents/FieldShell/index.ts +1 -0
  303. package/src/components/shared/helperComponents/FieldShell/styles.module.scss +13 -0
  304. package/src/components/shared/hooks/useCopyButton.tsx +103 -0
  305. package/src/components/shared/index.ts +4 -0
  306. package/src/components/shared/styles.module.scss +409 -0
  307. package/src/components/shared/utils.ts +128 -0
  308. package/src/constants.ts +73 -0
  309. package/src/hooks/index.ts +1 -0
  310. package/src/hooks/useAdaptiveAutoFocus.ts +16 -0
  311. package/src/index.ts +3 -0
  312. package/src/segments/index.ts +2 -0
  313. package/src/segments/segments.ts +74 -0
  314. package/src/segments/useSegmentedMask.ts +361 -0
@@ -0,0 +1,502 @@
1
+ import { Button } from '@cloud-ru/ds-button';
2
+ import { FieldDecorator, FieldDecoratorProps, SIZE, VALIDATION_STATE } from '@cloud-ru/ds-field-decorator';
3
+ import { CheckSVG, CopySVG } from '@cloud-ru/ds-icons/interface/system';
4
+ import { INPUT_MODE, InputPrivate, TYPE } from '@cloud-ru/ds-input-private';
5
+ import { Slider, SliderProps } from '@cloud-ru/ds-slider';
6
+ import { extractSupportProps, useValueControl } from '@cloud-ru/ds-utils';
7
+ import cn from 'classnames';
8
+ import mergeRefs from 'merge-refs';
9
+ import {
10
+ FocusEvent,
11
+ forwardRef,
12
+ KeyboardEvent,
13
+ MouseEvent,
14
+ ReactElement,
15
+ ReactNode,
16
+ useCallback,
17
+ useEffect,
18
+ useMemo,
19
+ useRef,
20
+ useState,
21
+ } from 'react';
22
+
23
+ import { TEST_IDS } from '../../constants';
24
+ import { FieldLayoutPresets, useAdaptiveAutoFocus } from '../../hooks';
25
+ import { copyTextToClipboard, getAcrylicProps } from '../shared';
26
+ import fieldStyles from '../shared/styles.module.scss';
27
+ import styles from './styles.module.scss';
28
+ import { SliderValue, TextInputFormatter } from './types';
29
+ import { generateAllowedValues, getClosestMark, getMarkLabel, getTextFieldValue } from './utils';
30
+
31
+ type SliderMarks = NonNullable<SliderProps['marks']>;
32
+
33
+ type FieldSliderDecoratorProps = Omit<FieldDecoratorProps, 'children'>;
34
+
35
+ type FieldSliderOwnProps = {
36
+ /** CSS-класс корня `FieldDecorator` */
37
+ className?: string;
38
+ /** CSS-класс оболочки поля */
39
+ fieldClassName?: string;
40
+ /** Значение (число или диапазон при `range`; controlled-режим) */
41
+ value?: SliderValue;
42
+ /** Начальное значение (uncontrolled-режим). По умолчанию `min` (или `[min, max]` при `range`). */
43
+ defaultValue?: SliderValue;
44
+ /** Колбек смены значения */
45
+ onChange?(value: SliderValue): void;
46
+ /**
47
+ * Диапазон с двумя ручками. Текстовое поле в этом режиме `readonly`
48
+ * и показывает значение как `min – max`.
49
+ * @default false
50
+ */
51
+ range?: boolean;
52
+ /** Минимум */
53
+ min: number;
54
+ /** Максимум */
55
+ max: number;
56
+ /** Шаг приращения. `null` — снэп только к меткам. */
57
+ step: number | null;
58
+ /** Метки на шкале */
59
+ marks?: SliderMarks;
60
+ /**
61
+ * Показывать линейку с метками
62
+ * @default true
63
+ */
64
+ showScaleBar?: boolean;
65
+ /**
66
+ * Равномерно распределять метки по шкале при нелинейных значениях
67
+ * (например `1 2 4 8 16 32` — равные промежутки вместо логарифмических).
68
+ * @default false
69
+ */
70
+ marksEqualSpacing?: boolean;
71
+ /** Форматирование значения в текстовом поле */
72
+ textInputFormatter?: TextInputFormatter;
73
+ /**
74
+ * Если `true` — текстовое поле принимает любые числа в диапазоне `min..max`,
75
+ * не снэпя к меткам.
76
+ * @default false
77
+ */
78
+ unbindInputFromMarks?: boolean;
79
+ /** Произвольный префикс */
80
+ prefix?: ReactNode;
81
+ /** Произвольный постфикс */
82
+ postfix?: ReactNode;
83
+ /** Иконка-постфикс справа от текстового поля */
84
+ postfixIcon?: ReactElement;
85
+ /**
86
+ * Фон поля (acrylic)
87
+ * @default true
88
+ */
89
+ background?: boolean;
90
+ /**
91
+ * Показывать кнопку копирования значения (видна в readonly, при `!disabled`).
92
+ * @default true
93
+ */
94
+ showCopyButton?: boolean;
95
+ /** Колбек после успешного копирования значения. */
96
+ onCopyButtonClick?(): void;
97
+ /** Поле выключено */
98
+ disabled?: boolean;
99
+ /** Только для чтения */
100
+ readonly?: boolean;
101
+ /** HTML id */
102
+ id?: string;
103
+ /** HTML name */
104
+ name?: string;
105
+ /** Автофокус. На mobile выключается адаптивно (см. `layoutPresets`) */
106
+ autoFocus?: boolean;
107
+ /**
108
+ * Переопределение адаптивных дефолтов по раскладке. Участвует `autoFocus`: на mobile он выключен
109
+ * (открывает клавиатуру без действия). Вернуть на mobile — `layoutPresets={{ mobile: { autoFocus: true } }}`.
110
+ */
111
+ layoutPresets?: FieldLayoutPresets;
112
+ /** Колбек фокуса */
113
+ onFocus?(event: FocusEvent<HTMLInputElement>): void;
114
+ /** Колбек блюра */
115
+ onBlur?(event: FocusEvent<HTMLInputElement>): void;
116
+ };
117
+
118
+ export type FieldSliderProps = FieldSliderDecoratorProps & FieldSliderOwnProps;
119
+
120
+ export const FieldSlider = forwardRef<HTMLInputElement, FieldSliderProps>(function FieldSlider(
121
+ {
122
+ label = '',
123
+ labelTooltip,
124
+ caption,
125
+ hint,
126
+ error,
127
+ validationState = VALIDATION_STATE.Default,
128
+ showHintIcon,
129
+ length,
130
+ required,
131
+ size = SIZE.M,
132
+ className,
133
+ fieldClassName,
134
+ background = true,
135
+ showCopyButton = true,
136
+ onCopyButtonClick,
137
+ value: valueProp,
138
+ defaultValue,
139
+ onChange,
140
+ range = false,
141
+ min,
142
+ max,
143
+ step,
144
+ marks,
145
+ showScaleBar = true,
146
+ marksEqualSpacing,
147
+ textInputFormatter,
148
+ unbindInputFromMarks = false,
149
+ prefix,
150
+ postfix,
151
+ postfixIcon,
152
+ disabled,
153
+ readonly: readOnly,
154
+ id,
155
+ name,
156
+ autoFocus,
157
+ layoutPresets,
158
+ onFocus,
159
+ onBlur,
160
+ 'data-test-id': dataTestId,
161
+ ...rest
162
+ },
163
+ ref,
164
+ ) {
165
+ const localRef = useRef<HTMLInputElement>(null);
166
+ const copyButtonRef = useRef<HTMLButtonElement>(null);
167
+ const [focusVisible, setFocusVisible] = useState(false);
168
+ const [hover, setHover] = useState(false);
169
+ const [copied, setCopied] = useState(false);
170
+ const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
171
+
172
+ const resolvedAutoFocus = useAdaptiveAutoFocus(autoFocus, layoutPresets);
173
+
174
+ useEffect(
175
+ () => () => {
176
+ if (copiedTimerRef.current) {
177
+ clearTimeout(copiedTimerRef.current);
178
+ }
179
+ },
180
+ [],
181
+ );
182
+
183
+ // error форсит validationState='error' поверх любого значения — паритет с сиблингами (FieldStepper и др.).
184
+ const effectiveValidationState = useMemo(
185
+ () => (error ? VALIDATION_STATE.Error : validationState),
186
+ [error, validationState],
187
+ );
188
+
189
+ // controlled/uncontrolled: useValueControl держит значение при отсутствии `value`.
190
+ const [value = range ? [min, max] : min, setValue] = useValueControl<SliderValue>({
191
+ value: valueProp,
192
+ defaultValue,
193
+ });
194
+ const [inputValue, setInputValue] = useState(() => getTextFieldValue(value, textInputFormatter));
195
+
196
+ const allowedKeys = useMemo(() => (marks ? Object.keys(marks) : []), [marks]);
197
+ const hasMarksEqualToValues = useMemo(() => {
198
+ if (!marks) {
199
+ return false;
200
+ }
201
+ return allowedKeys.every(key => key === String(getMarkLabel(marks[key as keyof SliderMarks])));
202
+ }, [allowedKeys, marks]);
203
+
204
+ // Актуальное значение для рантайм-ре-снапа шкалы (см. эффект ниже).
205
+ const valueRef = useRef(value);
206
+ valueRef.current = value;
207
+
208
+ const commitValue = useCallback(
209
+ (next: SliderValue) => {
210
+ // blur/Enter — это явный commit: значение фиксируется и onChange вызывается всегда,
211
+ // в том числе когда введённое снапнулось обратно к текущему (контракт «blur = commit»).
212
+ setValue(next);
213
+ onChange?.(next);
214
+ setInputValue(getTextFieldValue(next, textInputFormatter));
215
+ },
216
+ [onChange, setValue, textInputFormatter],
217
+ );
218
+
219
+ const handleSliderChange = useCallback(
220
+ (next: SliderValue) => {
221
+ commitValue(next);
222
+ },
223
+ [commitValue],
224
+ );
225
+
226
+ const snapAndCommit = useCallback(
227
+ (raw: string) => {
228
+ if (range) {
229
+ return;
230
+ }
231
+ const parsed = parseFloat(raw);
232
+ if (Number.isNaN(parsed)) {
233
+ commitValue(value);
234
+ return;
235
+ }
236
+
237
+ if (parsed <= min) {
238
+ commitValue(min);
239
+ return;
240
+ }
241
+ if (parsed >= max) {
242
+ commitValue(max);
243
+ return;
244
+ }
245
+
246
+ const allowedFromMarks =
247
+ !marks || hasMarksEqualToValues || unbindInputFromMarks
248
+ ? []
249
+ : allowedKeys
250
+ .map(key => parseFloat(String(getMarkLabel(marks[key as keyof SliderMarks]))))
251
+ .filter(mark => !Number.isNaN(mark));
252
+
253
+ if (allowedFromMarks.length === 0) {
254
+ // Снэп к допустимым значениям шага. step<=0/null → снэпа нет, принимаем число как есть.
255
+ if (step === null || step <= 0) {
256
+ commitValue(parsed);
257
+ return;
258
+ }
259
+ const allowedValues = generateAllowedValues(min, max, step);
260
+ if (allowedValues.includes(parsed)) {
261
+ commitValue(parsed);
262
+ return;
263
+ }
264
+ const closest = getClosestMark(parsed, allowedValues, m => m);
265
+ commitValue(closest ? closest.mark : parsed);
266
+ return;
267
+ }
268
+
269
+ const closest = getClosestMark(parsed, allowedFromMarks, m => m);
270
+ commitValue(closest ? closest.mark : parsed);
271
+ },
272
+ [allowedKeys, commitValue, hasMarksEqualToValues, marks, max, min, range, step, unbindInputFromMarks, value],
273
+ );
274
+
275
+ const handleInputChange = useCallback(
276
+ (raw: string) => {
277
+ // В range-режиме поле readonly — правки игнорируем, не трогая идентичность колбека.
278
+ if (range) {
279
+ return;
280
+ }
281
+ // не пускаем нечисловой ввод в поле (паритет с легаси onTextFieldChange)
282
+ const parsed = parseFloat(raw);
283
+ if (raw && Number.isNaN(parsed)) {
284
+ return;
285
+ }
286
+ setInputValue(raw);
287
+ },
288
+ [range],
289
+ );
290
+
291
+ const handleInputBlur = useCallback(
292
+ (event: FocusEvent<HTMLInputElement>) => {
293
+ snapAndCommit(inputValue);
294
+ onBlur?.(event);
295
+ },
296
+ [inputValue, onBlur, snapAndCommit],
297
+ );
298
+
299
+ const handleInputFocus = useCallback(
300
+ (event: FocusEvent<HTMLInputElement>) => {
301
+ setFocusVisible(event.target.matches(':focus-visible'));
302
+ onFocus?.(event);
303
+ },
304
+ [onFocus],
305
+ );
306
+
307
+ const handleMouseEnter = useCallback(() => {
308
+ if (!readOnly) {
309
+ setHover(true);
310
+ }
311
+ }, [readOnly]);
312
+
313
+ const handleMouseLeave = useCallback(() => {
314
+ setHover(false);
315
+ }, []);
316
+
317
+ // Copy-кнопка видна только в readonly при !disabled — паритет с Figma readonly-вариантом
318
+ // fieldSlider (слот buttonCopy). В range-режиме инпут тоже нередактируем, но copy там не
319
+ // показываем: это не readonly-проп, а две ручки диапазона (в легаси copy у range не было).
320
+ // Копируется отображаемый текст значения (`inputValue`, с учётом textInputFormatter).
321
+ const showCopyUi = Boolean(showCopyButton && readOnly && !disabled);
322
+
323
+ const handleCopy = useCallback(
324
+ (event: MouseEvent<HTMLButtonElement>) => {
325
+ event.preventDefault();
326
+ event.stopPropagation();
327
+ copyTextToClipboard(inputValue).then(copied => {
328
+ if (!copied) {
329
+ return;
330
+ }
331
+ onCopyButtonClick?.();
332
+ setCopied(true);
333
+ if (copiedTimerRef.current) {
334
+ clearTimeout(copiedTimerRef.current);
335
+ }
336
+ copiedTimerRef.current = setTimeout(() => setCopied(false), 2000);
337
+ });
338
+ },
339
+ [inputValue, onCopyButtonClick],
340
+ );
341
+
342
+ const handleInputKeyDown = useCallback(
343
+ (event: KeyboardEvent<HTMLInputElement>) => {
344
+ if (event.key === 'Enter') {
345
+ snapAndCommit(inputValue);
346
+ }
347
+ // Клавиатурный путь к copy в readonly (кнопка с tabIndex −1 недостижима по Tab):
348
+ // ArrowRight из инпута → copy, ArrowLeft с copy — назад (handleCopyButtonKeyDown).
349
+ if (event.key === 'ArrowRight' && readOnly && showCopyUi && copyButtonRef.current) {
350
+ event.preventDefault();
351
+ copyButtonRef.current.focus();
352
+ }
353
+ },
354
+ [inputValue, readOnly, showCopyUi, snapAndCommit],
355
+ );
356
+
357
+ const handleCopyButtonKeyDown = useCallback((event: KeyboardEvent<HTMLButtonElement>) => {
358
+ if (event.key === 'ArrowLeft') {
359
+ event.preventDefault();
360
+ localRef.current?.focus();
361
+ }
362
+ }, []);
363
+
364
+ useEffect(() => {
365
+ setInputValue(getTextFieldValue(value, textInputFormatter));
366
+ }, [textInputFormatter, value]);
367
+
368
+ // Рефы на последнюю snap-функцию и значение: пере-привязка по смене шкалы (эффект ниже)
369
+ // не зависит от их идентичности и не зацикливается на каждом onChange.
370
+ const snapAndCommitRef = useRef(snapAndCommit);
371
+ snapAndCommitRef.current = snapAndCommit;
372
+
373
+ // Рантайм-смена шкалы (marks/min/max) пере-привязывает текущее значение к новому допустимому
374
+ // набору (паритет с легаси re-snap effect): после, например, max 100→50 значение 75 уедет к 50.
375
+ // Range управляется слайдером и здесь не трогается.
376
+ useEffect(() => {
377
+ if (range) {
378
+ return;
379
+ }
380
+ snapAndCommitRef.current(String(valueRef.current));
381
+ }, [marks, min, max, range]);
382
+
383
+ const shell = (
384
+ // data-size на корне нужен, чтобы `.sliderWrapper` (сосед `.fieldWrapper`, а не потомок)
385
+ // подхватил size-зависимые токены отступов трека.
386
+ <div className={styles.fieldSlider} data-size={size}>
387
+ <div
388
+ className={cn(fieldStyles.fieldWrapper, fieldClassName)}
389
+ data-size={size}
390
+ data-validation-state={effectiveValidationState}
391
+ data-disabled={disabled || undefined}
392
+ data-readonly={readOnly || undefined}
393
+ data-withbackground={background || undefined}
394
+ data-focusvisible={focusVisible || undefined}
395
+ data-hover={!readOnly && hover ? true : undefined}
396
+ data-test-id={TEST_IDS.fieldSliderField}
397
+ onMouseEnter={handleMouseEnter}
398
+ onMouseLeave={handleMouseLeave}
399
+ >
400
+ <div className={fieldStyles.backgroundWrapper}>
401
+ <div
402
+ className={fieldStyles.materialLayer}
403
+ {...getAcrylicProps({
404
+ validationState: effectiveValidationState,
405
+ disabled,
406
+ readonly: readOnly,
407
+ hover,
408
+ focusVisible,
409
+ })}
410
+ >
411
+ <div className={fieldStyles.acrylicBg} aria-hidden />
412
+ </div>
413
+ <div className={fieldStyles.borderStateLayer} data-state='borderOnBackground' />
414
+ <div className={fieldStyles.focusLayer} />
415
+ </div>
416
+ <div className={fieldStyles.fieldContainer}>
417
+ <div className={fieldStyles.contentWrapper}>
418
+ <div className={fieldStyles.inputLine}>
419
+ {prefix && <span className={fieldStyles.prefix}>{prefix}</span>}
420
+ <div className={fieldStyles.inputArea}>
421
+ <InputPrivate
422
+ ref={mergeRefs(ref, localRef)}
423
+ className={fieldStyles.fieldInput}
424
+ value={inputValue}
425
+ type={TYPE.Text}
426
+ inputMode={INPUT_MODE.Numeric}
427
+ disabled={disabled}
428
+ readonly={range || readOnly}
429
+ onChange={handleInputChange}
430
+ onBlur={handleInputBlur}
431
+ onFocus={handleInputFocus}
432
+ onKeyDown={handleInputKeyDown}
433
+ id={id}
434
+ name={name}
435
+ autoFocus={resolvedAutoFocus}
436
+ data-test-id={TEST_IDS.fieldSliderInput}
437
+ {...extractSupportProps(rest)}
438
+ />
439
+ </div>
440
+ {showCopyUi && (
441
+ <span className={styles.copyButton}>
442
+ <Button
443
+ innerRef={copyButtonRef}
444
+ type='button'
445
+ view='function'
446
+ appearance='neutral'
447
+ size={size}
448
+ icon={copied ? <CheckSVG /> : <CopySVG />}
449
+ onClick={handleCopy}
450
+ onKeyDown={handleCopyButtonKeyDown}
451
+ tabIndex={-1}
452
+ data-test-id={TEST_IDS.fieldSliderCopyButton}
453
+ />
454
+ </span>
455
+ )}
456
+ {postfix && <span className={fieldStyles.postfix}>{postfix}</span>}
457
+ {postfixIcon && <span className={styles.postfixIcon}>{postfixIcon}</span>}
458
+ </div>
459
+ </div>
460
+ </div>
461
+ </div>
462
+ <div className={styles.sliderWrapper}>
463
+ <div className={styles.slider}>
464
+ <Slider
465
+ range={range}
466
+ min={min}
467
+ max={max}
468
+ step={step}
469
+ value={value}
470
+ onChange={handleSliderChange}
471
+ marks={showScaleBar ? marks : undefined}
472
+ marksEqualSpacing={marksEqualSpacing}
473
+ disabled={readOnly || disabled}
474
+ data-test-id={TEST_IDS.fieldSliderHandle}
475
+ />
476
+ </div>
477
+ </div>
478
+ </div>
479
+ );
480
+
481
+ return (
482
+ <FieldDecorator
483
+ className={className}
484
+ label={label}
485
+ labelTooltip={labelTooltip}
486
+ caption={caption}
487
+ hint={hint}
488
+ error={error}
489
+ validationState={validationState}
490
+ showHintIcon={showHintIcon}
491
+ length={length}
492
+ required={required}
493
+ labelFor={id}
494
+ disabled={disabled}
495
+ readonly={readOnly}
496
+ size={size}
497
+ data-test-id={dataTestId}
498
+ >
499
+ {shell}
500
+ </FieldDecorator>
501
+ );
502
+ });
@@ -0,0 +1,2 @@
1
+ export * from './FieldSlider';
2
+ export * from './types';
@@ -0,0 +1,47 @@
1
+ @use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
2
+ @use '@cloud-ru/ds-figma-variables/build/scss/components/field.module' as field;
3
+
4
+ $sizes: 's', 'm', 'l';
5
+
6
+ .fieldSlider {
7
+ display: flex;
8
+ flex-direction: column;
9
+ gap: 0;
10
+ align-self: stretch;
11
+
12
+ width: 100%;
13
+ }
14
+
15
+ .sliderWrapper {
16
+ display: flex;
17
+ align-items: center;
18
+ width: 100%;
19
+
20
+ @each $size in $sizes {
21
+ [data-size='#{$size}'] & {
22
+ padding-right: base.simple-var(field.$field, 'anatomy', 'size', $size, 'sliderWrapper', 'lineWrapper', 'padding-right');
23
+ padding-left: base.simple-var(field.$field, 'anatomy', 'size', $size, 'sliderWrapper', 'lineWrapper', 'padding-left');
24
+ }
25
+ }
26
+ }
27
+
28
+ .slider {
29
+ width: 100%;
30
+ }
31
+
32
+ .postfixIcon {
33
+ display: flex;
34
+ flex-shrink: 0;
35
+ align-items: center;
36
+ justify-content: center;
37
+
38
+ color: base.$sn-theme-color-available-version-textTertiary;
39
+ }
40
+
41
+ // Copy-кнопка (readonly) центрируется в строке значения.
42
+ .copyButton {
43
+ display: flex;
44
+ flex-shrink: 0;
45
+ align-items: center;
46
+ justify-content: center;
47
+ }
@@ -0,0 +1,5 @@
1
+ /** Форматирование значения слайдера в строку текстового поля. */
2
+ export type TextInputFormatter = (value: number) => string;
3
+
4
+ /** Значение слайдера: число или диапазон `[min, max]` при `range`. */
5
+ export type SliderValue = number | number[];
@@ -0,0 +1,13 @@
1
+ export function generateAllowedValues(min: number, max: number, step: number | null): number[] {
2
+ // step <= 0 (или null) дал бы бесконечный цикл — снэп к шкале невозможен, возвращаем только min.
3
+ if (step === null || step <= 0) {
4
+ return [min];
5
+ }
6
+ const values: number[] = [];
7
+ let current = min;
8
+ while (current <= max) {
9
+ values.push(parseFloat(current.toFixed(10)));
10
+ current += step;
11
+ }
12
+ return values;
13
+ }
@@ -0,0 +1,21 @@
1
+ function getDiff(value: number, mark: number): number {
2
+ return Math.abs(mark - value);
3
+ }
4
+
5
+ export function getClosestMark<T>(
6
+ value: number,
7
+ marks: T[],
8
+ getMarkValue: (value: T) => number,
9
+ ): { lowestDiff: number; mark: T } | undefined {
10
+ if (marks.length === 0) {
11
+ return undefined;
12
+ }
13
+ const [first, ...others] = marks;
14
+ return others.reduce(
15
+ (acc, mark) => {
16
+ const diff = getDiff(value, getMarkValue(mark));
17
+ return diff < acc.lowestDiff ? { lowestDiff: diff, mark } : acc;
18
+ },
19
+ { lowestDiff: getDiff(value, getMarkValue(first)), mark: first },
20
+ );
21
+ }
@@ -0,0 +1,7 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ import { isMarkObject } from './isMarkObject';
4
+
5
+ export function getMarkLabel(mark: unknown): ReactNode {
6
+ return isMarkObject(mark) ? mark.label : (mark as ReactNode);
7
+ }
@@ -0,0 +1,8 @@
1
+ import { SliderValue, TextInputFormatter } from '../types';
2
+
3
+ export function getTextFieldValue(value: SliderValue, textInputFormatter?: TextInputFormatter): string {
4
+ if (Array.isArray(value)) {
5
+ return value.map(v => (textInputFormatter ? textInputFormatter(v) : String(v))).join(' – ');
6
+ }
7
+ return textInputFormatter ? textInputFormatter(value) : String(value);
8
+ }
@@ -0,0 +1,5 @@
1
+ export * from './generateAllowedValues';
2
+ export * from './getClosestMark';
3
+ export * from './getMarkLabel';
4
+ export * from './getTextFieldValue';
5
+ export * from './isMarkObject';
@@ -0,0 +1,7 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ type MarkObject = { label: ReactNode };
4
+
5
+ export function isMarkObject(mark: unknown): mark is MarkObject {
6
+ return Boolean(mark && typeof mark === 'object' && 'label' in (mark as Record<string, unknown>));
7
+ }