@cloud-ru/uikit-product-calculator 0.33.3

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 (205) hide show
  1. package/CHANGELOG.md +1508 -0
  2. package/LICENSE +201 -0
  3. package/README.md +85 -0
  4. package/package.json +75 -0
  5. package/src/components/Calculator/Calculator.tsx +79 -0
  6. package/src/components/Calculator/index.ts +1 -0
  7. package/src/components/CalculatorContent/CalculatorContent.tsx +38 -0
  8. package/src/components/CalculatorContent/index.ts +1 -0
  9. package/src/components/CalculatorContent/styles.module.scss +79 -0
  10. package/src/components/Catalog/Catalog.tsx +225 -0
  11. package/src/components/Catalog/components/PrivateCardHeader.tsx +63 -0
  12. package/src/components/Catalog/components/index.ts +1 -0
  13. package/src/components/Catalog/components/styles.module.scss +35 -0
  14. package/src/components/Catalog/index.ts +1 -0
  15. package/src/components/Catalog/styles.module.scss +104 -0
  16. package/src/components/Controls/AlertControl/AlertControl.tsx +44 -0
  17. package/src/components/Controls/AlertControl/index.ts +1 -0
  18. package/src/components/Controls/ArrayControl/ArrayControl.tsx +105 -0
  19. package/src/components/Controls/ArrayControl/index.ts +1 -0
  20. package/src/components/Controls/ArrayControl/styles.module.scss +33 -0
  21. package/src/components/Controls/CarouselControl/CarouselControl.tsx +173 -0
  22. package/src/components/Controls/CarouselControl/index.ts +1 -0
  23. package/src/components/Controls/CarouselControl/styles.module.scss +32 -0
  24. package/src/components/Controls/Control/Control.tsx +205 -0
  25. package/src/components/Controls/Control/index.ts +1 -0
  26. package/src/components/Controls/ObjectControl/ObjectControl.tsx +150 -0
  27. package/src/components/Controls/ObjectControl/index.ts +1 -0
  28. package/src/components/Controls/ObjectControl/styles.module.scss +18 -0
  29. package/src/components/Controls/ObjectDecorator/ObjectDecorator.tsx +23 -0
  30. package/src/components/Controls/ObjectDecorator/index.ts +1 -0
  31. package/src/components/Controls/ObjectDecorator/styles.module.scss +15 -0
  32. package/src/components/Controls/SegmentedControl/SegmentedControl.tsx +79 -0
  33. package/src/components/Controls/SegmentedControl/SegmentedControlCard.tsx +38 -0
  34. package/src/components/Controls/SegmentedControl/index.ts +1 -0
  35. package/src/components/Controls/SegmentedControl/styles.module.scss +8 -0
  36. package/src/components/Controls/SelectControl/SelectControl.tsx +172 -0
  37. package/src/components/Controls/SelectControl/index.ts +1 -0
  38. package/src/components/Controls/SelectControl/styles.module.scss +5 -0
  39. package/src/components/Controls/SliderControl/SliderControl.tsx +92 -0
  40. package/src/components/Controls/SliderControl/index.ts +1 -0
  41. package/src/components/Controls/StepperControl/StepperControl.tsx +160 -0
  42. package/src/components/Controls/StepperControl/index.ts +1 -0
  43. package/src/components/Controls/StepperControl/styles.module.scss +16 -0
  44. package/src/components/Controls/TableControl/TableControl.tsx +162 -0
  45. package/src/components/Controls/TableControl/helperComponents/StepperWithAllowedValues.tsx +50 -0
  46. package/src/components/Controls/TableControl/helperComponents/index.ts +1 -0
  47. package/src/components/Controls/TableControl/helperComponents/styles.module.scss +53 -0
  48. package/src/components/Controls/TableControl/index.ts +1 -0
  49. package/src/components/Controls/TableControl/styles.module.scss +104 -0
  50. package/src/components/Controls/ToggleCard/ToggleCard.tsx +46 -0
  51. package/src/components/Controls/ToggleCard/index.ts +1 -0
  52. package/src/components/Controls/ToggleCard/styles.module.scss +11 -0
  53. package/src/components/Controls/ToggleCardsControl/ToggleCardsControl.tsx +93 -0
  54. package/src/components/Controls/ToggleCardsControl/index.ts +1 -0
  55. package/src/components/Controls/ToggleCardsControl/styles.module.scss +40 -0
  56. package/src/components/Controls/ToggleControl/ToggleControl.tsx +59 -0
  57. package/src/components/Controls/ToggleControl/components/SwitchRow.tsx +98 -0
  58. package/src/components/Controls/ToggleControl/components/index.ts +1 -0
  59. package/src/components/Controls/ToggleControl/components/styles.module.scss +110 -0
  60. package/src/components/Controls/ToggleControl/index.ts +1 -0
  61. package/src/components/Controls/ToggleObjectControl/ToggleObjectControl.tsx +111 -0
  62. package/src/components/Controls/ToggleObjectControl/index.ts +1 -0
  63. package/src/components/Controls/ToggleObjectControl/styles.module.scss +13 -0
  64. package/src/components/Controls/constants.ts +16 -0
  65. package/src/components/Controls/index.tsx +26 -0
  66. package/src/components/Controls/types.ts +181 -0
  67. package/src/components/Controls/utils.ts +49 -0
  68. package/src/components/Disclaimer/Disclaimer.tsx +33 -0
  69. package/src/components/Disclaimer/index.ts +1 -0
  70. package/src/components/Disclaimer/styles.module.scss +14 -0
  71. package/src/components/HeaderContainer/index.tsx +32 -0
  72. package/src/components/HeaderContainer/styles.module.scss +24 -0
  73. package/src/components/PriceSummary/PriceSummary.tsx +158 -0
  74. package/src/components/PriceSummary/components/PricePeriodSelect/PricePeriodSelect.tsx +81 -0
  75. package/src/components/PriceSummary/components/PricePeriodSelect/index.ts +1 -0
  76. package/src/components/PriceSummary/components/ProductCard/ProductCard.tsx +85 -0
  77. package/src/components/PriceSummary/components/ProductCard/index.ts +1 -0
  78. package/src/components/PriceSummary/components/ProductCard/styles.module.scss +7 -0
  79. package/src/components/PriceSummary/components/ProductListActions/ProductListActions.tsx +118 -0
  80. package/src/components/PriceSummary/components/ProductListActions/index.ts +1 -0
  81. package/src/components/PriceSummary/components/ProductListActions/styles.module.scss +38 -0
  82. package/src/components/PriceSummary/components/index.ts +3 -0
  83. package/src/components/PriceSummary/hooks.ts +36 -0
  84. package/src/components/PriceSummary/index.ts +1 -0
  85. package/src/components/PriceSummary/styles.module.scss +110 -0
  86. package/src/components/PriceSummary/utils/getTotalPrice.ts +19 -0
  87. package/src/components/PriceSummary/utils/index.ts +2 -0
  88. package/src/components/PriceSummary/utils/transformToProductCardData.ts +36 -0
  89. package/src/components/ProductHeadline/ProductPageHeadline.tsx +134 -0
  90. package/src/components/ProductHeadline/components/PriceCount.tsx +93 -0
  91. package/src/components/ProductHeadline/components/index.ts +1 -0
  92. package/src/components/ProductHeadline/components/styles.module.scss +52 -0
  93. package/src/components/ProductHeadline/index.ts +1 -0
  94. package/src/components/ProductHeadline/styles.module.scss +32 -0
  95. package/src/components/ProductPage/ProductPage.tsx +105 -0
  96. package/src/components/ProductPage/index.ts +1 -0
  97. package/src/components/ProductPage/styles.module.scss +13 -0
  98. package/src/components/Welcome/Welcome.tsx +138 -0
  99. package/src/components/Welcome/index.ts +1 -0
  100. package/src/components/Welcome/styles.module.scss +155 -0
  101. package/src/components/index.ts +3 -0
  102. package/src/config/config.tsx +27 -0
  103. package/src/config/index.ts +2 -0
  104. package/src/config/platforms/advanced/catalog.ts +82 -0
  105. package/src/config/platforms/advanced/constants.ts +23 -0
  106. package/src/config/platforms/advanced/index.ts +5 -0
  107. package/src/config/platforms/advanced/platform.tsx +13 -0
  108. package/src/config/platforms/advanced/product-config/AdvancedCloudBackup.ts +76 -0
  109. package/src/config/platforms/advanced/product-config/AdvancedCloudContainerEngine.tsx +260 -0
  110. package/src/config/platforms/advanced/product-config/AdvancedCloudServer.tsx +526 -0
  111. package/src/config/platforms/advanced/product-config/AdvancedDataAsYouUse.ts +159 -0
  112. package/src/config/platforms/advanced/product-config/AdvancedDataLakeInsight.ts +134 -0
  113. package/src/config/platforms/advanced/product-config/AdvancedDataWarehouseService.ts +94 -0
  114. package/src/config/platforms/advanced/product-config/AdvancedDcsMemcached.ts +69 -0
  115. package/src/config/platforms/advanced/product-config/AdvancedDcsRedis.ts +220 -0
  116. package/src/config/platforms/advanced/product-config/AdvancedDmsKafka.tsx +186 -0
  117. package/src/config/platforms/advanced/product-config/AdvancedDmsRabbitMq.ts +202 -0
  118. package/src/config/platforms/advanced/product-config/AdvancedDocumentDatabase.tsx +323 -0
  119. package/src/config/platforms/advanced/product-config/AdvancedElasticLoadBalance.tsx +252 -0
  120. package/src/config/platforms/advanced/product-config/AdvancedElasticSearch.ts +365 -0
  121. package/src/config/platforms/advanced/product-config/AdvancedFunctionGraph.tsx +65 -0
  122. package/src/config/platforms/advanced/product-config/AdvancedMapReduce.tsx +394 -0
  123. package/src/config/platforms/advanced/product-config/AdvancedMySqlDataBase.ts +417 -0
  124. package/src/config/platforms/advanced/product-config/AdvancedNetwork.ts +146 -0
  125. package/src/config/platforms/advanced/product-config/AdvancedObjectStorage.ts +232 -0
  126. package/src/config/platforms/advanced/product-config/AdvancedPostgreSqlDatabase.ts +402 -0
  127. package/src/config/platforms/advanced/product-config/AdvancedScalableFile.tsx +161 -0
  128. package/src/config/platforms/advanced/product-config/AdvancedSqlDatabase.ts +380 -0
  129. package/src/config/platforms/advanced/product-config/index.ts +21 -0
  130. package/src/config/platforms/advanced/products.ts +267 -0
  131. package/src/config/platforms/evolution/catalog.tsx +107 -0
  132. package/src/config/platforms/evolution/constants.ts +80 -0
  133. package/src/config/platforms/evolution/index.ts +5 -0
  134. package/src/config/platforms/evolution/platform.tsx +13 -0
  135. package/src/config/platforms/evolution/product-config/EvolutionArenadataDb.ts +151 -0
  136. package/src/config/platforms/evolution/product-config/EvolutionArtifactRegistry.ts +57 -0
  137. package/src/config/platforms/evolution/product-config/EvolutionBareMetal.tsx +172 -0
  138. package/src/config/platforms/evolution/product-config/EvolutionCloudServer.tsx +407 -0
  139. package/src/config/platforms/evolution/product-config/EvolutionCloudServerFreeTier.tsx +185 -0
  140. package/src/config/platforms/evolution/product-config/EvolutionCloudServerGpu.tsx +301 -0
  141. package/src/config/platforms/evolution/product-config/EvolutionContainerApps.ts +84 -0
  142. package/src/config/platforms/evolution/product-config/EvolutionContainerAppsFreeTier.ts +29 -0
  143. package/src/config/platforms/evolution/product-config/EvolutionKubernetes.ts +388 -0
  144. package/src/config/platforms/evolution/product-config/EvolutionManagedMetastore.ts +38 -0
  145. package/src/config/platforms/evolution/product-config/EvolutionManagedRedis.ts +161 -0
  146. package/src/config/platforms/evolution/product-config/EvolutionManagedSpark.ts +360 -0
  147. package/src/config/platforms/evolution/product-config/EvolutionManagedTrino.ts +53 -0
  148. package/src/config/platforms/evolution/product-config/EvolutionMlInference.tsx +703 -0
  149. package/src/config/platforms/evolution/product-config/EvolutionPostgreSql.ts +217 -0
  150. package/src/config/platforms/evolution/product-config/EvolutionPublicIp.ts +39 -0
  151. package/src/config/platforms/evolution/product-config/EvolutionSnatGateway.ts +22 -0
  152. package/src/config/platforms/evolution/product-config/EvolutionStorageS3.ts +223 -0
  153. package/src/config/platforms/evolution/product-config/EvolutionStorageS3FreeTier.ts +97 -0
  154. package/src/config/platforms/evolution/product-config/index.ts +17 -0
  155. package/src/config/platforms/evolution/products.ts +260 -0
  156. package/src/config/platforms/index.ts +3 -0
  157. package/src/config/platforms/vmware/catalog.ts +19 -0
  158. package/src/config/platforms/vmware/constants.ts +6 -0
  159. package/src/config/platforms/vmware/index.ts +5 -0
  160. package/src/config/platforms/vmware/platform.tsx +13 -0
  161. package/src/config/platforms/vmware/product-config/VmWareCloudBackup.ts +56 -0
  162. package/src/config/platforms/vmware/product-config/VmWareVirtualDataCenter.ts +309 -0
  163. package/src/config/platforms/vmware/product-config/VmWareVirtualMachinesBackup.ts +58 -0
  164. package/src/config/platforms/vmware/product-config/VmWareVirtualWorkspaces.tsx +210 -0
  165. package/src/config/platforms/vmware/product-config/index.ts +4 -0
  166. package/src/config/platforms/vmware/products.ts +63 -0
  167. package/src/config/styles.module.scss +9 -0
  168. package/src/config/utils/disk.ts +94 -0
  169. package/src/config/utils/diskPostgreSqlMySQL.ts +94 -0
  170. package/src/config/utils/eip.ts +104 -0
  171. package/src/config/utils/index.ts +52 -0
  172. package/src/config/utils/obs.ts +100 -0
  173. package/src/constants.ts +56 -0
  174. package/src/contexts/CalculatorContext.tsx +193 -0
  175. package/src/contexts/ProductContext.ts +25 -0
  176. package/src/contexts/index.ts +2 -0
  177. package/src/hooks/index.ts +6 -0
  178. package/src/hooks/useAdaptive.ts +20 -0
  179. package/src/hooks/useCatalogCardClick.ts +37 -0
  180. package/src/hooks/useProductClick.ts +14 -0
  181. package/src/hooks/useProductDelete.ts +46 -0
  182. package/src/index.ts +6 -0
  183. package/src/services/constants.ts +5 -0
  184. package/src/services/getFetcherFn.ts +57 -0
  185. package/src/services/getInitPrice.ts +64 -0
  186. package/src/services/getOnDownloadFileClick.ts +37 -0
  187. package/src/services/getOnShareClick.ts +56 -0
  188. package/src/services/index.ts +4 -0
  189. package/src/types/CalculatorType.ts +16 -0
  190. package/src/types/CatalogConfig.ts +13 -0
  191. package/src/types/Category.ts +14 -0
  192. package/src/types/FetcherFn.ts +26 -0
  193. package/src/types/FormValues.ts +4 -0
  194. package/src/types/Platform.ts +24 -0
  195. package/src/types/Price.ts +18 -0
  196. package/src/types/Product.ts +39 -0
  197. package/src/types/ProductState.ts +11 -0
  198. package/src/types/State.ts +7 -0
  199. package/src/types/index.ts +10 -0
  200. package/src/utils/filterNonEmptyArrays.ts +13 -0
  201. package/src/utils/formatNumber.ts +5 -0
  202. package/src/utils/getPrice.ts +38 -0
  203. package/src/utils/index.ts +5 -0
  204. package/src/utils/parseKeyToDataTest.ts +14 -0
  205. package/src/utils/value.ts +11 -0
@@ -0,0 +1,79 @@
1
+ import { useEffect, useMemo } from 'react';
2
+
3
+ import { FieldDecorator } from '@snack-uikit/fields';
4
+ import { ToggleGroup } from '@snack-uikit/toggles';
5
+
6
+ import { FormValues } from '../../../types';
7
+ import { parseKeyToDataTest } from '../../../utils';
8
+ import { CONTROL } from '../constants';
9
+ import { BaseControlWithItems } from '../types';
10
+ import { SegmentedControlCard, SegmentedControlItem } from './SegmentedControlCard';
11
+ import styles from './styles.module.scss';
12
+
13
+ type UiProps = { className?: string; disabled?: boolean };
14
+
15
+ export type SegmentedControl = {
16
+ type: typeof CONTROL.Segmented;
17
+ defaultValue?: string;
18
+ } & BaseControlWithItems<SegmentedControlItem, UiProps>;
19
+
20
+ export type SegmentedControlUiProps = SegmentedControl & {
21
+ value?: string;
22
+ onChange?(value?: string): void;
23
+ watchedValues?: FormValues;
24
+ };
25
+
26
+ export function SegmentedControlUi({
27
+ decoratorProps,
28
+ uiProps,
29
+ value,
30
+ onChange,
31
+ items: itemsProp,
32
+ relateFn,
33
+ watchedValues,
34
+ accessorKey,
35
+ }: SegmentedControlUiProps) {
36
+ const {
37
+ items: relatedItems,
38
+ decoratorProps: relatedDecoratorProps,
39
+ uiProps: relatedUiProps,
40
+ } = useMemo(() => relateFn?.(watchedValues ?? {}) ?? {}, [relateFn, watchedValues]);
41
+
42
+ const items = useMemo(() => relatedItems ?? itemsProp, [itemsProp, relatedItems]);
43
+
44
+ const dataTestAttribute = parseKeyToDataTest('segmented', accessorKey);
45
+
46
+ useEffect(() => {
47
+ if (!items.find(item => String(item?.value) === String(value))) {
48
+ onChange?.(String(items?.[0]?.value));
49
+ }
50
+ }, [value, onChange, items, relatedItems]);
51
+
52
+ const visible = relatedUiProps?.visible ?? uiProps?.visible ?? true;
53
+
54
+ if (!visible) {
55
+ return null;
56
+ }
57
+
58
+ return (
59
+ <FieldDecorator {...decoratorProps} {...relatedDecoratorProps} size='m' data-test-id={dataTestAttribute}>
60
+ <ToggleGroup
61
+ selectionMode='single'
62
+ value={value}
63
+ onChange={value => {
64
+ value && onChange?.(value);
65
+ }}
66
+ >
67
+ <div className={styles.container}>
68
+ {items.map(item => (
69
+ <SegmentedControlCard
70
+ disabled={relatedUiProps?.disabled ?? uiProps?.disabled}
71
+ {...item}
72
+ key={item?.value}
73
+ />
74
+ ))}
75
+ </div>
76
+ </ToggleGroup>
77
+ </FieldDecorator>
78
+ );
79
+ }
@@ -0,0 +1,38 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ import { ChipToggle } from '@snack-uikit/chips';
4
+ import { useToggleGroup } from '@snack-uikit/toggles';
5
+ import { Tooltip } from '@snack-uikit/tooltip';
6
+
7
+ import { AnyType, PRICE_PERIOD } from '../../../types';
8
+
9
+ export type SegmentedControlItem = {
10
+ value: string;
11
+
12
+ label?: string;
13
+ description?: string;
14
+
15
+ disabled?: boolean;
16
+ disabledReason?: ReactNode;
17
+
18
+ dataTestId?: string;
19
+ onChangePeriod?: (value: PRICE_PERIOD, setValue: (arr: [string, AnyType[]]) => void) => void;
20
+ canChangeWholePricePeriod?: boolean;
21
+ };
22
+
23
+ export function SegmentedControlCard({ value, label, disabled, disabledReason, dataTestId }: SegmentedControlItem) {
24
+ const { isChecked, handleClick } = useToggleGroup({ value });
25
+
26
+ return (
27
+ <Tooltip open={disabled && disabled && disabledReason ? undefined : false} tip={disabledReason}>
28
+ <ChipToggle
29
+ size='m'
30
+ checked={isChecked}
31
+ onChange={handleClick}
32
+ disabled={!isChecked && disabled}
33
+ data-test-id={dataTestId}
34
+ label={label ?? value}
35
+ />
36
+ </Tooltip>
37
+ );
38
+ }
@@ -0,0 +1 @@
1
+ export * from './SegmentedControl';
@@ -0,0 +1,8 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as stv;
2
+
3
+ .container {
4
+ display: flex;
5
+ flex-wrap: wrap;
6
+
7
+ gap: stv.$dimension-050m;
8
+ }
@@ -0,0 +1,172 @@
1
+ import { ReactNode, useEffect, useMemo } from 'react';
2
+
3
+ import { AdaptiveFieldSelect } from '@cloud-ru/uikit-product-mobile-fields';
4
+ import { BaseOptionProps, FieldSelectSingleProps } from '@snack-uikit/fields';
5
+
6
+ import { useCalculatorContext } from '../../../contexts';
7
+ import { FormValues, PRICE_PERIOD } from '../../../types';
8
+ import { parseKeyToDataTest } from '../../../utils';
9
+ import { CONTROL } from '../constants';
10
+ import { BaseControlWithItems } from '../types';
11
+ import styles from './styles.module.scss';
12
+
13
+ export type SelectControlItem = {
14
+ value: string;
15
+ label?: string;
16
+ description?: string;
17
+ icon?: ReactNode;
18
+ disabled?: boolean;
19
+ disabledReason?: ReactNode;
20
+ dataTestId?: string;
21
+ onChangePeriod?: (value: PRICE_PERIOD) => void;
22
+ canChangeWholePricePeriod?: boolean;
23
+ };
24
+
25
+ type SelectUiProps = Pick<
26
+ FieldSelectSingleProps,
27
+ | 'placeholder'
28
+ | 'className'
29
+ | 'readonly'
30
+ | 'showClearButton'
31
+ | 'showCopyButton'
32
+ | 'caption'
33
+ | 'showHintIcon'
34
+ | 'searchable'
35
+ | 'noDataState'
36
+ | 'noResultsState'
37
+ | 'disabled'
38
+ >;
39
+
40
+ type SelectControl = BaseControlWithItems<SelectControlItem, SelectUiProps>;
41
+
42
+ export type SelectSingleControl = SelectControl & { type: typeof CONTROL.SelectSingle } & {
43
+ defaultValue?: string;
44
+ };
45
+
46
+ type SelectSingleUiProps = SelectSingleControl & {
47
+ value?: string;
48
+ onChange?(value?: string): void;
49
+ watchedValues?: FormValues;
50
+ };
51
+
52
+ export type SelectMultipleControl = SelectControl & { type: typeof CONTROL.SelectMultiple } & {
53
+ defaultValue?: string[];
54
+ };
55
+
56
+ type SelectMultipleUiProps = SelectMultipleControl & {
57
+ value?: string[];
58
+ onChange?(value?: string[]): void;
59
+ watchedValues?: FormValues;
60
+ };
61
+
62
+ function convertItemsToOptions(items: SelectControlItem[]): BaseOptionProps[] {
63
+ return items.map(item => ({
64
+ value: item.value,
65
+ disabled: item.disabled,
66
+ option: item.label ?? item.value,
67
+ beforeContent: item.icon,
68
+ }));
69
+ }
70
+
71
+ export function SelectSingleUi({
72
+ decoratorProps,
73
+ uiProps,
74
+ value,
75
+ onChange,
76
+ items: itemsProp,
77
+ watchedValues,
78
+ relateFn,
79
+ accessorKey,
80
+ }: SelectSingleUiProps) {
81
+ const { layoutType } = useCalculatorContext();
82
+
83
+ const {
84
+ items: relatedItems,
85
+ decoratorProps: relatedDecoratorProps,
86
+ uiProps: relatedUiProps,
87
+ } = useMemo(() => relateFn?.(watchedValues ?? {}) ?? {}, [relateFn, watchedValues]);
88
+
89
+ const items = useMemo(() => relatedItems ?? itemsProp, [itemsProp, relatedItems]);
90
+
91
+ const dataTestAttribute = parseKeyToDataTest('select', accessorKey);
92
+
93
+ useEffect(() => {
94
+ if (!items.find(item => String(item.value) === String(value))) {
95
+ onChange?.(String(items?.[0]?.value));
96
+ }
97
+ }, [value, onChange, items, relatedItems]);
98
+
99
+ const visible = relatedUiProps?.visible ?? uiProps?.visible ?? true;
100
+
101
+ if (!visible) {
102
+ return null;
103
+ }
104
+
105
+ return (
106
+ <div>
107
+ {!decoratorProps.label && <div className={styles.zeroLabel} />}
108
+ <AdaptiveFieldSelect
109
+ searchable={false}
110
+ showClearButton={false}
111
+ layoutType={layoutType}
112
+ widthStrategy='eq'
113
+ {...decoratorProps}
114
+ {...relatedDecoratorProps}
115
+ size='m'
116
+ selection='single'
117
+ value={value}
118
+ onChange={onChange}
119
+ options={convertItemsToOptions(items)}
120
+ data-test-id={dataTestAttribute}
121
+ {...uiProps}
122
+ {...relatedUiProps}
123
+ />
124
+ </div>
125
+ );
126
+ }
127
+
128
+ export function SelectMultipleUi({
129
+ decoratorProps,
130
+ uiProps,
131
+ value,
132
+ onChange,
133
+ items: itemsProp,
134
+ relateFn,
135
+ watchedValues,
136
+ }: SelectMultipleUiProps) {
137
+ const { layoutType } = useCalculatorContext();
138
+
139
+ const {
140
+ items: relatedItems,
141
+ decoratorProps: relatedDecoratorProps,
142
+ uiProps: relatedUiProps,
143
+ } = useMemo(() => relateFn?.(watchedValues ?? {}) ?? {}, [relateFn, watchedValues]);
144
+
145
+ const items = useMemo(() => relatedItems ?? itemsProp, [itemsProp, relatedItems]);
146
+
147
+ const visible = relatedUiProps?.visible ?? uiProps?.visible ?? true;
148
+
149
+ if (!visible) {
150
+ return null;
151
+ }
152
+
153
+ return (
154
+ <div>
155
+ {!decoratorProps.label && <div className={styles.zeroLabel} />}
156
+ <AdaptiveFieldSelect
157
+ layoutType={layoutType}
158
+ searchable={false}
159
+ showClearButton={false}
160
+ {...decoratorProps}
161
+ {...relatedDecoratorProps}
162
+ selection='multiple'
163
+ size='m'
164
+ value={value}
165
+ onChange={onChange}
166
+ options={convertItemsToOptions(items)}
167
+ {...uiProps}
168
+ {...relatedUiProps}
169
+ />
170
+ </div>
171
+ );
172
+ }
@@ -0,0 +1 @@
1
+ export * from './SelectControl';
@@ -0,0 +1,5 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as stv;
2
+
3
+ .zeroLabel {
4
+ height: stv.$dimension-3m;
5
+ }
@@ -0,0 +1,92 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import { FieldSlider, FieldSliderProps } from '@snack-uikit/fields';
4
+
5
+ import { FormValues } from '../../../types';
6
+ import { parseKeyToDataTest } from '../../../utils';
7
+ import { CONTROL } from '../constants';
8
+ import { BaseControlWithItems } from '../types';
9
+
10
+ type SliderUiProps = Pick<
11
+ FieldSliderProps,
12
+ 'step' | 'className' | 'showScaleBar' | 'tipFormatter' | 'postfixIcon' | 'disabled' | 'postfix'
13
+ >;
14
+
15
+ export type SliderControl = {
16
+ type: typeof CONTROL.Slider;
17
+ defaultValue?: string;
18
+ accessorKey?: string;
19
+ } & BaseControlWithItems<number, SliderUiProps>;
20
+
21
+ export type SliderControlUiProps = SliderControl & {
22
+ onChange?(value: string): void;
23
+ watchedValues?: FormValues;
24
+ value?: string;
25
+ };
26
+
27
+ export function SliderControlUi({
28
+ decoratorProps,
29
+ uiProps,
30
+ onChange,
31
+ items: itemsProp,
32
+ watchedValues,
33
+ relateFn,
34
+ accessorKey,
35
+ value,
36
+ }: SliderControlUiProps) {
37
+ const {
38
+ items: relatedItems,
39
+ decoratorProps: relatedDecoratorProps,
40
+ uiProps: relatedUiProps,
41
+ } = useMemo(() => relateFn?.(watchedValues ?? {}) ?? {}, [relateFn, watchedValues]);
42
+
43
+ const items = useMemo(() => relatedItems ?? itemsProp, [itemsProp, relatedItems]);
44
+
45
+ const dataTestAttribute = parseKeyToDataTest('slider', accessorKey);
46
+
47
+ const { marks } = useMemo(() => {
48
+ const marks = items
49
+ .sort((a, b) => a - b)
50
+ .reduce(
51
+ (res, item, idx) => {
52
+ res[idx + 1] = item;
53
+ return res;
54
+ },
55
+ {} as FieldSliderProps['marks'],
56
+ );
57
+
58
+ return { marks };
59
+ }, [items]);
60
+
61
+ const textInputValueFormatter = (value: number): string => String(marks[value]);
62
+
63
+ const visible = relatedUiProps?.visible ?? uiProps?.visible ?? true;
64
+
65
+ if (!visible) {
66
+ return null;
67
+ }
68
+
69
+ const currentValue = value ? items.findIndex(item => String(item) === value) + 1 : 1;
70
+
71
+ return (
72
+ <FieldSlider
73
+ range={false}
74
+ size='m'
75
+ value={currentValue}
76
+ onChange={newValue => {
77
+ const valueByKey = String(marks[newValue as number]);
78
+ onChange?.(valueByKey);
79
+ }}
80
+ marks={marks}
81
+ min={1}
82
+ max={items.length}
83
+ step={null}
84
+ textInputFormatter={textInputValueFormatter}
85
+ data-test-id={dataTestAttribute}
86
+ {...uiProps}
87
+ {...relatedUiProps}
88
+ {...decoratorProps}
89
+ {...relatedDecoratorProps}
90
+ />
91
+ );
92
+ }
@@ -0,0 +1 @@
1
+ export * from './SliderControl';
@@ -0,0 +1,160 @@
1
+ import { useEffect, useMemo, useState } from 'react';
2
+
3
+ import { FieldDecorator, FieldStepper, FieldStepperProps } from '@snack-uikit/fields';
4
+
5
+ import { FormValues } from '../../../types';
6
+ import { parseKeyToDataTest } from '../../../utils';
7
+ import { formatNumber } from '../../../utils/formatNumber';
8
+ import { CONTROL } from '../constants';
9
+ import { BaseControl, WithVisible } from '../types';
10
+ import styles from './styles.module.scss';
11
+
12
+ type StepperUiProps = Pick<
13
+ FieldStepperProps,
14
+ 'step' | 'min' | 'max' | 'allowMoreThanLimits' | 'disabled' | 'postfix' | 'size'
15
+ > & {
16
+ showHint?: boolean;
17
+ /**
18
+ * Множитель для значения
19
+ * Например: надо реальное значение это 3 млн, а отображать надо как 3
20
+ */
21
+ multiplier?: number;
22
+ /** Массив допустимых значений. Если указан, то step игнорируется */
23
+ allowedValues?: number[];
24
+ };
25
+
26
+ export type StepperControl = {
27
+ type: typeof CONTROL.Stepper;
28
+ defaultValue?: number;
29
+ uiProps: WithVisible<StepperUiProps>;
30
+ } & Omit<BaseControl<StepperUiProps>, 'uiProps'>;
31
+
32
+ export type StepperControlUiProps = StepperControl & {
33
+ value?: number;
34
+ onChange?(value: number): void;
35
+ watchedValues?: FormValues;
36
+ };
37
+
38
+ export function StepperControlUi({
39
+ decoratorProps,
40
+ uiProps,
41
+ value,
42
+ onChange,
43
+ watchedValues,
44
+ relateFn,
45
+ accessorKey,
46
+ }: StepperControlUiProps) {
47
+ const [innerValue, setInnerValue] = useState<number>(value ?? 0);
48
+ const [selectedIdx, setSelectedIdx] = useState<number>(0);
49
+
50
+ const dataTestAttribute = parseKeyToDataTest('stepper', accessorKey);
51
+
52
+ useEffect(() => {
53
+ setInnerValue(value ?? 0);
54
+ }, [value]);
55
+
56
+ const { decoratorProps: relatedDecoratorProps, uiProps: relatedUiProps } = useMemo(
57
+ () => relateFn?.(watchedValues ?? {}) ?? {},
58
+ [relateFn, watchedValues],
59
+ );
60
+
61
+ const { min, max, multiplier = 1, step = 1, allowedValues } = { ...uiProps, ...relatedUiProps };
62
+
63
+ useEffect(() => {
64
+ if (allowedValues) {
65
+ const idx = allowedValues.findIndex(item => item === value);
66
+ setSelectedIdx(idx >= 0 ? idx : 0);
67
+ }
68
+ }, [allowedValues, value]);
69
+
70
+ useEffect(() => {
71
+ if (min && Number((value ?? 0) / multiplier) < min) {
72
+ onChange?.(min * multiplier);
73
+
74
+ return;
75
+ }
76
+
77
+ if (max && Number((value ?? 0) / multiplier) > max) {
78
+ onChange?.(max * multiplier);
79
+
80
+ return;
81
+ }
82
+ }, [value, onChange, min, max, multiplier]);
83
+
84
+ const visible = relatedUiProps?.visible ?? uiProps?.visible ?? true;
85
+ const showHint = relatedUiProps?.showHint ?? uiProps.showHint ?? true;
86
+
87
+ const handleValueChange = (rawInnerValue?: number) => {
88
+ const innerValue = Number(rawInnerValue ?? 0);
89
+
90
+ if (allowedValues) {
91
+ onChange?.(innerValue);
92
+ return;
93
+ }
94
+
95
+ if (min && innerValue / multiplier <= min) {
96
+ onChange?.(min * multiplier);
97
+ setInnerValue(min);
98
+ return;
99
+ }
100
+
101
+ if (max && innerValue / multiplier >= max) {
102
+ onChange?.(max * multiplier);
103
+ setInnerValue(max);
104
+ return;
105
+ }
106
+
107
+ onChange?.(innerValue);
108
+ };
109
+
110
+ if (!visible) {
111
+ return null;
112
+ }
113
+
114
+ return (
115
+ <FieldDecorator {...decoratorProps} {...relatedDecoratorProps} size='m'>
116
+ <div>
117
+ <FieldStepper
118
+ size='m'
119
+ value={(innerValue ?? 0) / multiplier}
120
+ postfix={uiProps.postfix}
121
+ onChange={value => {
122
+ if (allowedValues) {
123
+ const currentIdx = selectedIdx;
124
+ const isIncrement = value > innerValue / multiplier;
125
+
126
+ const newIdx = isIncrement
127
+ ? Math.min(currentIdx + 1, allowedValues.length - 1)
128
+ : Math.max(currentIdx - 1, 0);
129
+
130
+ setSelectedIdx(newIdx);
131
+ handleValueChange(allowedValues[newIdx] * multiplier);
132
+ return;
133
+ }
134
+
135
+ if (Math.abs(value - innerValue / multiplier) === step) {
136
+ handleValueChange?.(value * multiplier);
137
+ return;
138
+ }
139
+
140
+ setInnerValue?.(value * multiplier);
141
+ }}
142
+ onBlur={() => {
143
+ handleValueChange(innerValue);
144
+ }}
145
+ allowMoreThanLimits={false}
146
+ data-test-id={dataTestAttribute}
147
+ {...uiProps}
148
+ {...relatedUiProps}
149
+ />
150
+ </div>
151
+
152
+ {showHint && (
153
+ <div className={styles.hintWrapper}>
154
+ <span className={styles.hint}>{formatNumber(min)}</span>
155
+ <span className={styles.hint}>{formatNumber(max)}</span>
156
+ </div>
157
+ )}
158
+ </FieldDecorator>
159
+ );
160
+ }
@@ -0,0 +1 @@
1
+ export * from './StepperControl';
@@ -0,0 +1,16 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as stv;
2
+
3
+ .hintWrapper {
4
+ display: flex;
5
+ justify-content: space-between;
6
+ align-items: flex-start;
7
+ flex: 1;
8
+ box-sizing: border-box;
9
+ padding: stv.$dimension-050m;
10
+ }
11
+
12
+ .hint {
13
+ @include stv.composite-var(stv.$sans-body-m);
14
+
15
+ color: stv.$sys-neutral-text-support;
16
+ }