@cloud-ru/ds-stepper 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 (124) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +201 -0
  3. package/README.md +214 -0
  4. package/dist/cjs/components/Stepper/Stepper.d.ts +10 -0
  5. package/dist/cjs/components/Stepper/Stepper.js +17 -0
  6. package/dist/cjs/components/Stepper/index.d.ts +1 -0
  7. package/dist/cjs/components/Stepper/index.js +17 -0
  8. package/dist/cjs/components/index.d.ts +1 -0
  9. package/dist/cjs/components/index.js +20 -0
  10. package/dist/cjs/constants.d.ts +16 -0
  11. package/dist/cjs/constants.js +25 -0
  12. package/dist/cjs/context.d.ts +3 -0
  13. package/dist/cjs/context.js +18 -0
  14. package/dist/cjs/helperComponents/DesktopStep/DesktopStep.d.ts +12 -0
  15. package/dist/cjs/helperComponents/DesktopStep/DesktopStep.js +25 -0
  16. package/dist/cjs/helperComponents/DesktopStep/index.d.ts +1 -0
  17. package/dist/cjs/helperComponents/DesktopStep/index.js +17 -0
  18. package/dist/cjs/helperComponents/DesktopStep/styles.module.css +169 -0
  19. package/dist/cjs/helperComponents/DesktopStepper/DesktopStepper.d.ts +5 -0
  20. package/dist/cjs/helperComponents/DesktopStepper/DesktopStepper.js +35 -0
  21. package/dist/cjs/helperComponents/DesktopStepper/index.d.ts +1 -0
  22. package/dist/cjs/helperComponents/DesktopStepper/index.js +17 -0
  23. package/dist/cjs/helperComponents/DesktopStepper/styles.module.css +6 -0
  24. package/dist/cjs/helperComponents/MobileStep/MobileStep.d.ts +8 -0
  25. package/dist/cjs/helperComponents/MobileStep/MobileStep.js +28 -0
  26. package/dist/cjs/helperComponents/MobileStep/index.d.ts +1 -0
  27. package/dist/cjs/helperComponents/MobileStep/index.js +17 -0
  28. package/dist/cjs/helperComponents/MobileStep/styles.module.css +114 -0
  29. package/dist/cjs/helperComponents/MobileStepper/MobileStepper.d.ts +5 -0
  30. package/dist/cjs/helperComponents/MobileStepper/MobileStepper.js +37 -0
  31. package/dist/cjs/helperComponents/MobileStepper/index.d.ts +1 -0
  32. package/dist/cjs/helperComponents/MobileStepper/index.js +17 -0
  33. package/dist/cjs/helperComponents/MobileStepper/styles.module.css +32 -0
  34. package/dist/cjs/helperComponents/StepIcon/StepIcon.d.ts +10 -0
  35. package/dist/cjs/helperComponents/StepIcon/StepIcon.js +30 -0
  36. package/dist/cjs/helperComponents/StepIcon/index.d.ts +1 -0
  37. package/dist/cjs/helperComponents/StepIcon/index.js +17 -0
  38. package/dist/cjs/helperComponents/StepIcon/styles.module.css +35 -0
  39. package/dist/cjs/helperComponents/index.d.ts +5 -0
  40. package/dist/cjs/helperComponents/index.js +21 -0
  41. package/dist/cjs/hooks/useStepperController.d.ts +25 -0
  42. package/dist/cjs/hooks/useStepperController.js +124 -0
  43. package/dist/cjs/index.d.ts +5 -0
  44. package/dist/cjs/index.js +21 -0
  45. package/dist/cjs/package.json +3 -0
  46. package/dist/cjs/style.css +356 -0
  47. package/dist/cjs/types.d.ts +51 -0
  48. package/dist/cjs/types.js +2 -0
  49. package/dist/cjs/utils.d.ts +6 -0
  50. package/dist/cjs/utils.js +10 -0
  51. package/dist/esm/components/Stepper/Stepper.d.ts +10 -0
  52. package/dist/esm/components/Stepper/Stepper.js +14 -0
  53. package/dist/esm/components/Stepper/index.d.ts +1 -0
  54. package/dist/esm/components/Stepper/index.js +1 -0
  55. package/dist/esm/components/index.d.ts +1 -0
  56. package/dist/esm/components/index.js +4 -0
  57. package/dist/esm/constants.d.ts +16 -0
  58. package/dist/esm/constants.js +20 -0
  59. package/dist/esm/context.d.ts +3 -0
  60. package/dist/esm/context.js +14 -0
  61. package/dist/esm/helperComponents/DesktopStep/DesktopStep.d.ts +12 -0
  62. package/dist/esm/helperComponents/DesktopStep/DesktopStep.js +19 -0
  63. package/dist/esm/helperComponents/DesktopStep/index.d.ts +1 -0
  64. package/dist/esm/helperComponents/DesktopStep/index.js +1 -0
  65. package/dist/esm/helperComponents/DesktopStep/styles.module.css +169 -0
  66. package/dist/esm/helperComponents/DesktopStepper/DesktopStepper.d.ts +5 -0
  67. package/dist/esm/helperComponents/DesktopStepper/DesktopStepper.js +29 -0
  68. package/dist/esm/helperComponents/DesktopStepper/index.d.ts +1 -0
  69. package/dist/esm/helperComponents/DesktopStepper/index.js +1 -0
  70. package/dist/esm/helperComponents/DesktopStepper/styles.module.css +6 -0
  71. package/dist/esm/helperComponents/MobileStep/MobileStep.d.ts +8 -0
  72. package/dist/esm/helperComponents/MobileStep/MobileStep.js +22 -0
  73. package/dist/esm/helperComponents/MobileStep/index.d.ts +1 -0
  74. package/dist/esm/helperComponents/MobileStep/index.js +1 -0
  75. package/dist/esm/helperComponents/MobileStep/styles.module.css +114 -0
  76. package/dist/esm/helperComponents/MobileStepper/MobileStepper.d.ts +5 -0
  77. package/dist/esm/helperComponents/MobileStepper/MobileStepper.js +31 -0
  78. package/dist/esm/helperComponents/MobileStepper/index.d.ts +1 -0
  79. package/dist/esm/helperComponents/MobileStepper/index.js +1 -0
  80. package/dist/esm/helperComponents/MobileStepper/styles.module.css +32 -0
  81. package/dist/esm/helperComponents/StepIcon/StepIcon.d.ts +10 -0
  82. package/dist/esm/helperComponents/StepIcon/StepIcon.js +24 -0
  83. package/dist/esm/helperComponents/StepIcon/index.d.ts +1 -0
  84. package/dist/esm/helperComponents/StepIcon/index.js +1 -0
  85. package/dist/esm/helperComponents/StepIcon/styles.module.css +35 -0
  86. package/dist/esm/helperComponents/index.d.ts +5 -0
  87. package/dist/esm/helperComponents/index.js +5 -0
  88. package/dist/esm/hooks/useStepperController.d.ts +25 -0
  89. package/dist/esm/hooks/useStepperController.js +121 -0
  90. package/dist/esm/index.d.ts +5 -0
  91. package/dist/esm/index.js +5 -0
  92. package/dist/esm/style.css +356 -0
  93. package/dist/esm/types.d.ts +51 -0
  94. package/dist/esm/types.js +1 -0
  95. package/dist/esm/utils.d.ts +6 -0
  96. package/dist/esm/utils.js +6 -0
  97. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  98. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  99. package/package.json +47 -0
  100. package/src/components/Stepper/Stepper.tsx +18 -0
  101. package/src/components/Stepper/index.ts +1 -0
  102. package/src/components/index.ts +4 -0
  103. package/src/constants.ts +23 -0
  104. package/src/context.tsx +18 -0
  105. package/src/helperComponents/DesktopStep/DesktopStep.tsx +73 -0
  106. package/src/helperComponents/DesktopStep/index.ts +1 -0
  107. package/src/helperComponents/DesktopStep/styles.module.scss +103 -0
  108. package/src/helperComponents/DesktopStepper/DesktopStepper.tsx +52 -0
  109. package/src/helperComponents/DesktopStepper/index.ts +1 -0
  110. package/src/helperComponents/DesktopStepper/styles.module.scss +6 -0
  111. package/src/helperComponents/MobileStep/MobileStep.tsx +46 -0
  112. package/src/helperComponents/MobileStep/index.ts +1 -0
  113. package/src/helperComponents/MobileStep/styles.module.scss +156 -0
  114. package/src/helperComponents/MobileStepper/MobileStepper.tsx +71 -0
  115. package/src/helperComponents/MobileStepper/index.ts +1 -0
  116. package/src/helperComponents/MobileStepper/styles.module.scss +36 -0
  117. package/src/helperComponents/StepIcon/StepIcon.tsx +47 -0
  118. package/src/helperComponents/StepIcon/index.ts +1 -0
  119. package/src/helperComponents/StepIcon/styles.module.scss +54 -0
  120. package/src/helperComponents/index.ts +5 -0
  121. package/src/hooks/useStepperController.ts +201 -0
  122. package/src/index.ts +5 -0
  123. package/src/types.ts +59 -0
  124. package/src/utils.ts +9 -0
@@ -0,0 +1,25 @@
1
+ import { StepData, StepState, StepsValidator, StepViewData } from '../types';
2
+ export type UseStepperControllerParams = {
3
+ steps: StepData[];
4
+ defaultCurrentStepIndex?: number;
5
+ validator?: StepsValidator;
6
+ onChangeCurrentStep?: (newValue: number, prevValue: number) => void;
7
+ onCompleteChange?: (isCompleted: boolean) => void;
8
+ allowFreeNavigation: boolean;
9
+ };
10
+ export type UseStepperControllerResult = {
11
+ stepsView: StepViewData[];
12
+ currentStepIndex: number;
13
+ currentStepState: StepState;
14
+ isCompleted: boolean;
15
+ stepCount: number;
16
+ goNext: (stepIndex?: number) => void;
17
+ goPrev: (stepIndex?: number) => void;
18
+ resetValidation: () => void;
19
+ setValidator: (validator: StepsValidator) => void;
20
+ };
21
+ /**
22
+ * Общий контроллер состояния шагов — используется одинаково в Stepper и
23
+ * MobileStepper. Порт из @snack-uikit/stepper без изменений поведения.
24
+ */
25
+ export declare function useStepperController({ steps, defaultCurrentStepIndex, validator: validatorProp, onChangeCurrentStep, onCompleteChange, allowFreeNavigation, }: UseStepperControllerParams): UseStepperControllerResult;
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useStepperController = useStepperController;
4
+ const react_1 = require("react");
5
+ const constants_1 = require("../constants");
6
+ const DEFAULT_VALIDATOR = async () => true;
7
+ /**
8
+ * Общий контроллер состояния шагов — используется одинаково в Stepper и
9
+ * MobileStepper. Порт из @snack-uikit/stepper без изменений поведения.
10
+ */
11
+ function useStepperController({ steps, defaultCurrentStepIndex = 0, validator: validatorProp = DEFAULT_VALIDATOR, onChangeCurrentStep, onCompleteChange, allowFreeNavigation, }) {
12
+ const isCompletedByDefault = defaultCurrentStepIndex === steps.length - 1;
13
+ const [currentStepState, setCurrentStepState] = (0, react_1.useState)(isCompletedByDefault ? constants_1.STEP_STATE.Completed : constants_1.STEP_STATE.Current);
14
+ const [currentStepIndex, setCurrentStepIndexValue] = (0, react_1.useState)(defaultCurrentStepIndex);
15
+ const [isCompleted, setIsCompleted] = (0, react_1.useState)(isCompletedByDefault);
16
+ const [stepsValidator, setStepsValidator] = (0, react_1.useState)();
17
+ (0, react_1.useEffect)(() => {
18
+ onCompleteChange?.(isCompleted);
19
+ }, [isCompleted, onCompleteChange]);
20
+ const setCurrentStepIndex = (0, react_1.useCallback)((newValue) => {
21
+ setCurrentStepIndexValue(prevValue => {
22
+ if (prevValue !== newValue) {
23
+ onChangeCurrentStep?.(newValue, prevValue);
24
+ }
25
+ return newValue;
26
+ });
27
+ }, [onChangeCurrentStep]);
28
+ const goNext = (0, react_1.useCallback)((newStepIndexUnsafety = currentStepIndex + 1) => {
29
+ if (currentStepIndex >= steps.length || isCompleted || newStepIndexUnsafety <= currentStepIndex) {
30
+ return;
31
+ }
32
+ const newStepIndex = Math.min(steps.length - 1, newStepIndexUnsafety);
33
+ setCurrentStepState(constants_1.STEP_STATE.Loading);
34
+ const validator = stepsValidator?.value || validatorProp;
35
+ validator(currentStepIndex, newStepIndex)
36
+ .catch(() => false)
37
+ .then(isValid => {
38
+ if (!isValid) {
39
+ setCurrentStepState(constants_1.STEP_STATE.Rejected);
40
+ return;
41
+ }
42
+ if (currentStepIndex === steps.length - 1) {
43
+ setCurrentStepState(constants_1.STEP_STATE.Completed);
44
+ setIsCompleted(true);
45
+ }
46
+ else {
47
+ setCurrentStepIndex(newStepIndex);
48
+ setCurrentStepState(constants_1.STEP_STATE.Current);
49
+ }
50
+ });
51
+ }, [currentStepIndex, isCompleted, setCurrentStepIndex, steps.length, stepsValidator?.value, validatorProp]);
52
+ const moveToStep = (0, react_1.useCallback)((stepIndex) => {
53
+ if (isCompleted) {
54
+ setIsCompleted(false);
55
+ }
56
+ setCurrentStepIndex(stepIndex);
57
+ setCurrentStepState(constants_1.STEP_STATE.Current);
58
+ }, [isCompleted, setCurrentStepIndex]);
59
+ const goToStep = (0, react_1.useCallback)((newStepIndex) => {
60
+ if (newStepIndex < 0 || newStepIndex >= steps.length) {
61
+ return;
62
+ }
63
+ if (newStepIndex === currentStepIndex) {
64
+ return;
65
+ }
66
+ moveToStep(newStepIndex);
67
+ }, [currentStepIndex, steps, moveToStep]);
68
+ const goPrev = (0, react_1.useCallback)((index = currentStepIndex - 1) => {
69
+ if (currentStepIndex === 0 || index < 0 || index > currentStepIndex) {
70
+ return;
71
+ }
72
+ if (index === currentStepIndex && !isCompleted) {
73
+ return;
74
+ }
75
+ moveToStep(index);
76
+ }, [currentStepIndex, isCompleted, moveToStep]);
77
+ const getStepOnClick = (0, react_1.useCallback)((stepIndex) => {
78
+ if (allowFreeNavigation) {
79
+ return () => goToStep(stepIndex);
80
+ }
81
+ if (stepIndex < currentStepIndex || (stepIndex === currentStepIndex && isCompleted)) {
82
+ return () => goPrev(stepIndex);
83
+ }
84
+ if (stepIndex === currentStepIndex + 1) {
85
+ return () => goNext();
86
+ }
87
+ return undefined;
88
+ }, [allowFreeNavigation, currentStepIndex, goNext, goPrev, goToStep, isCompleted]);
89
+ const stepsView = (0, react_1.useMemo)(() => steps.map((step, index) => {
90
+ const number = index + 1;
91
+ let state = constants_1.STEP_STATE.Waiting;
92
+ if (index < currentStepIndex) {
93
+ state = constants_1.STEP_STATE.Completed;
94
+ }
95
+ else if (index === currentStepIndex) {
96
+ state = currentStepState;
97
+ }
98
+ return {
99
+ ...step,
100
+ number,
101
+ state,
102
+ onClick: getStepOnClick(index),
103
+ };
104
+ }), [steps, currentStepIndex, currentStepState, getStepOnClick]);
105
+ const resetValidation = (0, react_1.useCallback)(() => {
106
+ if (currentStepState === constants_1.STEP_STATE.Rejected) {
107
+ setCurrentStepState(constants_1.STEP_STATE.Current);
108
+ }
109
+ }, [currentStepState]);
110
+ const setValidator = (0, react_1.useCallback)((validator) => {
111
+ setStepsValidator({ value: validator });
112
+ }, []);
113
+ return {
114
+ stepsView,
115
+ currentStepIndex,
116
+ currentStepState,
117
+ isCompleted,
118
+ stepCount: steps.length,
119
+ goNext,
120
+ goPrev,
121
+ resetValidation,
122
+ setValidator,
123
+ };
124
+ }
@@ -0,0 +1,5 @@
1
+ export * from './components';
2
+ export * from './constants';
3
+ export * from './context';
4
+ export * from './types';
5
+ export * from './utils';
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./components"), exports);
18
+ __exportStar(require("./constants"), exports);
19
+ __exportStar(require("./context"), exports);
20
+ __exportStar(require("./types"), exports);
21
+ __exportStar(require("./utils"), exports);
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,356 @@
1
+ @charset "UTF-8";
2
+ .step{
3
+ display:flex;
4
+ flex:1 1 0;
5
+ flex-direction:column;
6
+ gap:var(--sn-stepper-anatomy-stepRow-gap, var(--sn-primitive-dimension-4, 4px));
7
+ align-items:flex-start;
8
+ box-sizing:border-box;
9
+ min-width:0;
10
+ }
11
+
12
+ .track{
13
+ display:flex;
14
+ gap:var(--sn-stepper-anatomy-step-gap, var(--sn-primitive-dimension-8, 8px));
15
+ align-items:center;
16
+ box-sizing:border-box;
17
+ width:100%;
18
+ padding-right:var(--sn-stepper-anatomy-step-paddingHorizontal, var(--sn-primitive-dimension-4, 4px));
19
+ padding-left:var(--sn-stepper-anatomy-step-paddingHorizontal, var(--sn-primitive-dimension-4, 4px));
20
+ }
21
+
22
+ .statusContainer{
23
+ all:unset;
24
+ cursor:pointer;
25
+ position:relative;
26
+ display:inline-flex;
27
+ flex-shrink:0;
28
+ align-items:center;
29
+ justify-content:center;
30
+ box-sizing:border-box;
31
+ border-radius:50%;
32
+ outline:none;
33
+ }
34
+ .statusContainer .stateLayer{
35
+ position:absolute;
36
+ inset:0;
37
+ border-radius:inherit;
38
+ }
39
+ .statusContainer .stateLayer[data-state=borderOnBackground]{
40
+ border-color:var(--sn-theme-color-material-stateLayer-neutral-border-default-borderColor, var(--sn-brand-color-state-neutralBorder-forLightMode-default-borderColor, rgba(159, 166, 198, 0.3019607843)));
41
+ border-style:solid;
42
+ border-width:var(--sn-primitive-strokeWeight-strokeRegular, 1px);
43
+ }
44
+ .statusContainer .stateLayer[data-state=emptyNeutralOnBackground]{
45
+ background:var(--sn-theme-color-material-stateLayer-neutral-onBackground-defaultEmpty-filled, var(--sn-brand-color-state-neutral-forLightMode-default-background, rgba(120, 123, 138, 0)));
46
+ }
47
+ .statusContainer .stateLayer[data-state=activatedOnBackground]{
48
+ background:var(--sn-theme-color-material-stateLayer-activated-default-filled, var(--sn-brand-color-state-activated-default-background, var(--sn-brandRole-color-brandA-backgroundActivatedDefault, rgba(56, 159, 116, 0.1490196078))));
49
+ }
50
+ .statusContainer .stateLayer[data-state=versionOnColor]{
51
+ background:var(--sn-theme-color-material-stateLayer-versionOnColor-default-filled, rgba(0, 0, 0, 0.0509803922));
52
+ }
53
+ .statusContainer .stateLayer[data-state=emptyVersionOnColor]{
54
+ background:var(--sn-theme-color-material-stateLayer-versionOnColor-defaultEmpty-filled, rgba(0, 0, 0, 0));
55
+ }
56
+ .statusContainer .stateLayer[data-state=inversionOnColor]{
57
+ background:var(--sn-theme-color-material-stateLayer-inversionOnColor-default-filled, rgba(255, 255, 255, 0.1019607843));
58
+ }
59
+ .statusContainer .stateLayer[data-state=emptyInversionOnColor]{
60
+ background:var(--sn-theme-color-material-stateLayer-inversionOnColor-defaultEmpty-filled, rgba(255, 255, 255, 0));
61
+ }
62
+ .statusContainer .stateLayer[data-state=emptyDarkOnAccent]{
63
+ background:var(--sn-theme-color-material-stateLayer-darkOnAccent-defaultEmpty-filled, rgba(0, 0, 0, 0));
64
+ }
65
+ @media (hover: hover){
66
+ .statusContainer:hover:not([data-disabled], [data-loading]) .stateLayer[data-state=borderOnBackground]{
67
+ border-color:var(--sn-theme-color-material-stateLayer-neutral-border-hovered-borderColor, var(--sn-brand-color-state-neutralBorder-forLightMode-hovered-borderColor, rgba(159, 166, 198, 0.6509803922)));
68
+ }
69
+ .statusContainer:hover:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyNeutralOnBackground]{
70
+ background:var(--sn-theme-color-material-stateLayer-neutral-onBackground-hovered-filled, var(--sn-brand-color-state-neutral-forLightMode-hovered-background, rgba(188, 195, 220, 0.1490196078)));
71
+ }
72
+ .statusContainer:hover:not([data-disabled], [data-loading]) .stateLayer[data-state=activatedOnBackground]{
73
+ background:var(--sn-theme-color-material-stateLayer-activated-hovered-filled, var(--sn-brand-color-state-activated-hovered-background, var(--sn-brandRole-color-brandA-backgroundActivatedHovered, rgba(56, 159, 116, 0.3490196078))));
74
+ }
75
+ .statusContainer:hover:not([data-disabled], [data-loading]) .stateLayer[data-state=versionOnColor]{
76
+ background:var(--sn-theme-color-material-stateLayer-versionOnColor-hovered-filled, rgba(0, 0, 0, 0.1019607843));
77
+ }
78
+ .statusContainer:hover:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyVersionOnColor]{
79
+ background:var(--sn-theme-color-material-stateLayer-versionOnColor-hovered-filled, rgba(0, 0, 0, 0.1019607843));
80
+ }
81
+ .statusContainer:hover:not([data-disabled], [data-loading]) .stateLayer[data-state=inversionOnColor]{
82
+ background:var(--sn-theme-color-material-stateLayer-inversionOnColor-hovered-filled, rgba(255, 255, 255, 0.1490196078));
83
+ }
84
+ .statusContainer:hover:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyInversionOnColor]{
85
+ background:var(--sn-theme-color-material-stateLayer-inversionOnColor-hovered-filled, rgba(255, 255, 255, 0.1490196078));
86
+ }
87
+ .statusContainer:hover:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyDarkOnAccent]{
88
+ background:var(--sn-theme-color-material-stateLayer-darkOnAccent-hovered-filled, rgba(0, 0, 0, 0.0588235294));
89
+ }
90
+ }
91
+ .statusContainer{
92
+ }
93
+ .statusContainer[data-loading] .stateLayer[data-state=borderOnBackground], .statusContainer:active:not([data-disabled], [data-loading]) .stateLayer[data-state=borderOnBackground], .statusContainer[data-pressed=true]:not([data-disabled], [data-loading]) .stateLayer[data-state=borderOnBackground]{
94
+ border-color:var(--sn-theme-color-material-stateLayer-neutral-border-pressed-borderColor, var(--sn-brand-color-state-neutralBorder-forLightMode-pressed-borderColor, rgba(159, 166, 198, 0.8)));
95
+ }
96
+ .statusContainer[data-loading] .stateLayer[data-state=emptyNeutralOnBackground], .statusContainer:active:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyNeutralOnBackground], .statusContainer[data-pressed=true]:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyNeutralOnBackground]{
97
+ background:var(--sn-theme-color-material-stateLayer-neutral-onBackground-pressed-filled, var(--sn-brand-color-state-neutral-forLightMode-pressed-background, rgba(188, 195, 220, 0.3019607843)));
98
+ }
99
+ .statusContainer[data-loading] .stateLayer[data-state=activatedOnBackground], .statusContainer:active:not([data-disabled], [data-loading]) .stateLayer[data-state=activatedOnBackground], .statusContainer[data-pressed=true]:not([data-disabled], [data-loading]) .stateLayer[data-state=activatedOnBackground]{
100
+ background:var(--sn-theme-color-material-stateLayer-activated-pressed-filled, var(--sn-brand-color-state-activated-pressed-background, var(--sn-brandRole-color-brandA-backgroundActivatedPressed, rgba(56, 159, 116, 0.4509803922))));
101
+ }
102
+ .statusContainer[data-loading] .stateLayer[data-state=versionOnColor], .statusContainer:active:not([data-disabled], [data-loading]) .stateLayer[data-state=versionOnColor], .statusContainer[data-pressed=true]:not([data-disabled], [data-loading]) .stateLayer[data-state=versionOnColor]{
103
+ background:var(--sn-theme-color-material-stateLayer-versionOnColor-pressed-filled, rgba(0, 0, 0, 0.1490196078));
104
+ }
105
+ .statusContainer[data-loading] .stateLayer[data-state=emptyVersionOnColor], .statusContainer:active:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyVersionOnColor], .statusContainer[data-pressed=true]:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyVersionOnColor]{
106
+ background:var(--sn-theme-color-material-stateLayer-versionOnColor-pressed-filled, rgba(0, 0, 0, 0.1490196078));
107
+ }
108
+ .statusContainer[data-loading] .stateLayer[data-state=inversionOnColor], .statusContainer:active:not([data-disabled], [data-loading]) .stateLayer[data-state=inversionOnColor], .statusContainer[data-pressed=true]:not([data-disabled], [data-loading]) .stateLayer[data-state=inversionOnColor]{
109
+ background:var(--sn-theme-color-material-stateLayer-inversionOnColor-pressed-filled, rgba(255, 255, 255, 0.0509803922));
110
+ }
111
+ .statusContainer[data-loading] .stateLayer[data-state=emptyInversionOnColor], .statusContainer:active:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyInversionOnColor], .statusContainer[data-pressed=true]:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyInversionOnColor]{
112
+ background:var(--sn-theme-color-material-stateLayer-inversionOnColor-pressed-filled, rgba(255, 255, 255, 0.0509803922));
113
+ }
114
+ .statusContainer[data-loading] .stateLayer[data-state=emptyDarkOnAccent], .statusContainer:active:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyDarkOnAccent], .statusContainer[data-pressed=true]:not([data-disabled], [data-loading]) .stateLayer[data-state=emptyDarkOnAccent]{
115
+ background:var(--sn-theme-color-material-stateLayer-darkOnAccent-pressed-filled, rgba(0, 0, 0, 0.1215686275));
116
+ }
117
+ .statusContainer:disabled{
118
+ cursor:default;
119
+ }
120
+ .statusContainer:focus-visible{
121
+ outline:var(--sn-primitive-strokeWeight-strokeSemiBold, 2px) solid var(--sn-theme-color-available-complementary, #000000);
122
+ outline-offset:var(--sn-primitive-strokeWeight-strokeSemiBold, 2px);
123
+ }
124
+
125
+ .stateLayer{
126
+ border-radius:inherit;
127
+ }
128
+
129
+ .icon{
130
+ pointer-events:none;
131
+ }
132
+
133
+ .tail{
134
+ flex:1 1 auto;
135
+ min-width:16px;
136
+ height:var(--sn-stepper-anatomy-size-m-progressWrapper-progressLine-height, var(--sn-primitive-dimension-2, 2px));
137
+ }
138
+ .tail .tailLine{
139
+ width:100%;
140
+ height:100%;
141
+ border-radius:var(--sn-stepper-anatomy-size-m-progressWrapper-progressLine-borderRadius, var(--sn-primitive-dimension-full, 10000px));
142
+ background-color:var(--sn-theme-color-neutral-decor, var(--sn-brand-color-neutral-80, var(--sn-primitive-color-gray-80, #e3e5ed)));
143
+ }
144
+ .tail[data-completed] .tailLine{
145
+ background-color:var(--sn-theme-color-primary-accent, var(--sn-brand-color-primary-55, var(--sn-brandRole-color-brandA-55, var(--sn-primitive-color-brandA-55, #389f74))));
146
+ }
147
+
148
+ .content{
149
+ display:flex;
150
+ flex-direction:column;
151
+ min-width:0;
152
+ padding-right:var(--sn-stepper-anatomy-stepRow-contentWrapper-stepText-paddingHorizontal, var(--sn-stepper-anatomy-step-paddingHorizontal, var(--sn-primitive-dimension-4, 4px)));
153
+ padding-left:var(--sn-stepper-anatomy-stepRow-contentWrapper-stepText-paddingHorizontal, var(--sn-stepper-anatomy-step-paddingHorizontal, var(--sn-primitive-dimension-4, 4px)));
154
+ }
155
+
156
+ .title{
157
+ overflow:hidden;
158
+ color:var(--sn-theme-color-available-version-textMain, var(--sn-brand-color-text-version-main, #41424e));
159
+ text-overflow:ellipsis;
160
+ white-space:nowrap;
161
+ }
162
+
163
+ .description{
164
+ overflow:hidden;
165
+ display:-webkit-box;
166
+ -webkit-box-orient:vertical;
167
+ -webkit-line-clamp:2;
168
+ color:var(--sn-theme-color-available-version-textTertiary, var(--sn-brand-color-text-version-tertiary, rgba(65, 66, 78, 0.5490196078)));
169
+ }
170
+ .stepper{
171
+ display:flex;
172
+ flex-direction:row;
173
+ align-items:flex-start;
174
+ width:100%;
175
+ }
176
+ .step{
177
+ all:unset;
178
+ cursor:pointer;
179
+ position:relative;
180
+ display:flex;
181
+ align-items:center;
182
+ justify-content:center;
183
+ width:100%;
184
+ min-height:24px;
185
+ outline:none;
186
+ }
187
+ .step:disabled{
188
+ cursor:default;
189
+ }
190
+ .step:focus-visible{
191
+ border-radius:2px;
192
+ outline:var(--sn-primitive-strokeWeight-strokeSemiBold, 2px) solid var(--sn-theme-color-available-complementary, #000000);
193
+ outline-offset:var(--sn-primitive-strokeWeight-strokeSemiBold, 2px);
194
+ }
195
+
196
+ .track{
197
+ position:relative;
198
+ width:100%;
199
+ height:var(--sn-stepper-anatomy-size-s-progressWrapper-progressLine-height, var(--sn-primitive-dimension-4, 4px));
200
+ border-radius:var(--sn-stepper-anatomy-size-s-progressWrapper-progressLine-borderRadius, var(--sn-primitive-dimension-full, 10000px));
201
+ background-color:var(--sn-theme-color-neutral-decor, var(--sn-brand-color-neutral-80, var(--sn-primitive-color-gray-80, #e3e5ed)));
202
+ transition:background-color 150ms ease;
203
+ }
204
+
205
+ .status{
206
+ pointer-events:none;
207
+ position:absolute;
208
+ top:50%;
209
+ left:50%;
210
+ transform:translate(-50%, -50%);
211
+ display:none;
212
+ align-items:center;
213
+ justify-content:center;
214
+ width:var(--sn-stepper-anatomy-size-s-stepStatus-width, var(--sn-primitive-dimension-24, 24px));
215
+ height:var(--sn-stepper-anatomy-size-s-stepStatus-height, var(--sn-primitive-dimension-24, 24px));
216
+ border-radius:50%;
217
+ }
218
+
219
+ .track::after,
220
+ .status::after{
221
+ pointer-events:none;
222
+ content:"";
223
+ position:absolute;
224
+ inset:0;
225
+ border-radius:inherit;
226
+ background-color:var(--sn-theme-color-material-stateLayer-neutral-onBackground-defaultEmpty-filled, var(--sn-brand-color-state-neutral-forLightMode-default-background, rgba(120, 123, 138, 0)));
227
+ transition:background-color 150ms ease;
228
+ }
229
+
230
+ .step[data-state=completed] .track::after,
231
+ .step[data-state=completed] .status::after{
232
+ background-color:var(--sn-theme-color-material-stateLayer-darkOnAccent-defaultEmpty-filled, rgba(0, 0, 0, 0));
233
+ }
234
+
235
+ @media (hover: hover){
236
+ .step:not(:disabled):hover .track::after,
237
+ .step:not(:disabled):hover .status::after{
238
+ background-color:var(--sn-theme-color-material-stateLayer-neutral-onBackground-hovered-filled, var(--sn-brand-color-state-neutral-forLightMode-hovered-background, rgba(188, 195, 220, 0.1490196078)));
239
+ }
240
+ }
241
+ .step:not(:disabled):active .track::after,
242
+ .step:not(:disabled):active .status::after{
243
+ background-color:var(--sn-theme-color-material-stateLayer-neutral-onBackground-pressed-filled, var(--sn-brand-color-state-neutral-forLightMode-pressed-background, rgba(188, 195, 220, 0.3019607843)));
244
+ }
245
+
246
+ @media (hover: hover){
247
+ .step[data-state=completed]:not(:disabled):hover .track::after,
248
+ .step[data-state=completed]:not(:disabled):hover .status::after{
249
+ background-color:var(--sn-theme-color-material-stateLayer-darkOnAccent-hovered-filled, rgba(0, 0, 0, 0.0588235294));
250
+ }
251
+ }
252
+ .step[data-state=completed]:not(:disabled):active .track::after,
253
+ .step[data-state=completed]:not(:disabled):active .status::after{
254
+ background-color:var(--sn-theme-color-material-stateLayer-darkOnAccent-pressed-filled, rgba(0, 0, 0, 0.1215686275));
255
+ }
256
+
257
+ .step[data-state=current] .track{
258
+ background-color:var(--sn-theme-color-primary-accent, var(--sn-brand-color-primary-55, var(--sn-brandRole-color-brandA-55, var(--sn-primitive-color-brandA-55, #389f74))));
259
+ }
260
+
261
+ .step[data-state=completed] .track{
262
+ background-color:var(--sn-theme-color-primary-decor, var(--sn-brand-color-primary-80, var(--sn-brandRole-color-brandA-80, var(--sn-primitive-color-brandA-80, #caeadb))));
263
+ }
264
+
265
+ .step[data-state=completed] .status{
266
+ display:flex;
267
+ color:var(--sn-theme-color-primary-accent, var(--sn-brand-color-primary-55, var(--sn-brandRole-color-brandA-55, var(--sn-primitive-color-brandA-55, #389f74))));
268
+ background-color:var(--sn-theme-color-primary-decor, var(--sn-brand-color-primary-80, var(--sn-brandRole-color-brandA-80, var(--sn-primitive-color-brandA-80, #caeadb))));
269
+ }
270
+
271
+ .step[data-state=loading] .track{
272
+ background-color:var(--sn-theme-color-neutral-decor, var(--sn-brand-color-neutral-80, var(--sn-primitive-color-gray-80, #e3e5ed)));
273
+ }
274
+
275
+ .step[data-state=loading] .status{
276
+ display:flex;
277
+ color:var(--sn-theme-color-neutral-text, var(--sn-brand-color-neutral-45, var(--sn-primitive-color-gray-45, #81869c)));
278
+ background-color:var(--sn-theme-color-neutral-background, var(--sn-brand-color-neutral-90, var(--sn-primitive-color-gray-90, #eeeff3)));
279
+ }
280
+
281
+ .step[data-state=rejected] .track{
282
+ background-color:var(--sn-theme-color-red-accent, var(--sn-primitive-color-red-55, #d52e33));
283
+ }
284
+
285
+ .step[data-state=rejected] .status{
286
+ display:flex;
287
+ color:var(--sn-theme-color-red-onAccent, var(--sn-primitive-color-red-99, #fffaf7));
288
+ background-color:var(--sn-theme-color-red-accent, var(--sn-primitive-color-red-55, #d52e33));
289
+ }
290
+ .stepper{
291
+ display:flex;
292
+ flex-direction:column;
293
+ gap:var(--sn-stepper-anatomy-stepRow-gap, var(--sn-primitive-dimension-4, 4px));
294
+ width:100%;
295
+ }
296
+
297
+ .steps{
298
+ display:grid;
299
+ gap:var(--sn-stepper-anatomy-step-gap, var(--sn-primitive-dimension-8, 8px));
300
+ width:100%;
301
+ }
302
+
303
+ .content{
304
+ display:flex;
305
+ flex-direction:column;
306
+ }
307
+
308
+ .title{
309
+ overflow:hidden;
310
+ color:var(--sn-theme-color-available-version-textMain, var(--sn-brand-color-text-version-main, #41424e));
311
+ text-overflow:ellipsis;
312
+ white-space:nowrap;
313
+ }
314
+
315
+ .description{
316
+ overflow:hidden;
317
+ display:-webkit-box;
318
+ -webkit-box-orient:vertical;
319
+ -webkit-line-clamp:2;
320
+ color:var(--sn-theme-color-available-version-textTertiary, var(--sn-brand-color-text-version-tertiary, rgba(65, 66, 78, 0.5490196078)));
321
+ }
322
+ .icon{
323
+ display:flex;
324
+ align-items:center;
325
+ justify-content:center;
326
+ box-sizing:border-box;
327
+ width:var(--sn-stepper-anatomy-size-m-stepStatus-width, var(--sn-primitive-dimension-32, 32px));
328
+ height:var(--sn-stepper-anatomy-size-m-stepStatus-height, var(--sn-primitive-dimension-32, 32px));
329
+ border:var(--sn-stepper-anatomy-size-m-stepStatus-borderWidth, var(--sn-primitive-strokeWeight-strokeRegular, 1px)) solid transparent;
330
+ border-radius:50%;
331
+ color:var(--sn-theme-color-available-version-textTertiary, var(--sn-brand-color-text-version-tertiary, rgba(65, 66, 78, 0.5490196078)));
332
+ background-color:var(--sn-theme-color-neutral-background1Level, var(--sn-brand-color-neutral-95, var(--sn-primitive-color-gray-95, #fdfdfd)));
333
+ transition:background-color 150ms ease, color 150ms ease, border-color 150ms ease;
334
+ }
335
+ .icon[data-state=waiting]{
336
+ border-color:var(--sn-theme-color-available-borderColor, var(--sn-brand-color-state-neutralBorder-forLightMode-default-borderColor, rgba(159, 166, 198, 0.3019607843)));
337
+ color:var(--sn-theme-color-available-version-textTertiary, var(--sn-brand-color-text-version-tertiary, rgba(65, 66, 78, 0.5490196078)));
338
+ background-color:var(--sn-theme-color-neutral-background1Level, var(--sn-brand-color-neutral-95, var(--sn-primitive-color-gray-95, #fdfdfd)));
339
+ }
340
+ .icon[data-state=current]{
341
+ color:var(--sn-theme-color-primary-onAccent, var(--sn-brand-color-primary-onAccentLight, var(--sn-brandRole-color-brandA-onAccentLight, var(--sn-primitive-color-brandA-99, #fbfffc))));
342
+ background-color:var(--sn-theme-color-primary-accent, var(--sn-brand-color-primary-55, var(--sn-brandRole-color-brandA-55, var(--sn-primitive-color-brandA-55, #389f74))));
343
+ }
344
+ .icon[data-state=loading]{
345
+ border-color:var(--sn-theme-color-available-borderColor, var(--sn-brand-color-state-neutralBorder-forLightMode-default-borderColor, rgba(159, 166, 198, 0.3019607843)));
346
+ color:var(--sn-theme-color-neutral-text, var(--sn-brand-color-neutral-45, var(--sn-primitive-color-gray-45, #81869c)));
347
+ background-color:var(--sn-theme-color-neutral-background, var(--sn-brand-color-neutral-90, var(--sn-primitive-color-gray-90, #eeeff3)));
348
+ }
349
+ .icon[data-state=completed]{
350
+ color:var(--sn-theme-color-primary-accent, var(--sn-brand-color-primary-55, var(--sn-brandRole-color-brandA-55, var(--sn-primitive-color-brandA-55, #389f74))));
351
+ background-color:var(--sn-theme-color-primary-decor, var(--sn-brand-color-primary-80, var(--sn-brandRole-color-brandA-80, var(--sn-primitive-color-brandA-80, #caeadb))));
352
+ }
353
+ .icon[data-state=rejected]{
354
+ color:var(--sn-theme-color-red-onAccent, var(--sn-primitive-color-red-99, #fffaf7));
355
+ background-color:var(--sn-theme-color-red-accent, var(--sn-primitive-color-red-55, #d52e33));
356
+ }
@@ -0,0 +1,51 @@
1
+ import { ValueOf } from '@cloud-ru/ds-utils';
2
+ import { ReactElement } from 'react';
3
+ import { STEP_STATE } from './constants';
4
+ export type StepState = ValueOf<typeof STEP_STATE>;
5
+ export type StepsValidator = (prevStepIndex: number, newStepIndex: number) => Promise<boolean>;
6
+ export type StepData = {
7
+ title: string;
8
+ description?: string;
9
+ };
10
+ export type StepViewData = StepData & {
11
+ onClick?: () => void;
12
+ state: StepState;
13
+ number: number;
14
+ };
15
+ export type StepperApi = {
16
+ stepper: ReactElement;
17
+ goNext: (stepIndex?: number) => void;
18
+ goPrev: (stepIndex?: number) => void;
19
+ resetValidation: () => void;
20
+ setValidator: (validator: StepsValidator) => void;
21
+ isCompleted: boolean;
22
+ currentStepIndex: number;
23
+ stepCount: number;
24
+ };
25
+ /** Псевдоним `StepperApi` для обратной совместимости. */
26
+ export type StepperState = StepperApi;
27
+ /** Публичные пропсы адаптивного `Stepper` (единые для desktop/mobile поверхностей). */
28
+ export type StepperProps = {
29
+ /** Массив шагов */
30
+ steps: StepData[];
31
+ /** Индекс текущего шага по-дефолту */
32
+ defaultCurrentStepIndex?: number;
33
+ /** Валидатор шагов. Выполняется при смене шага. Принимает первым аргументом индекс текущего, вторым — индекс нового шага. Возвращает Promise<boolean>: false → шаг помечается как Rejected. */
34
+ validator?: StepsValidator;
35
+ /** CSS-класс */
36
+ className?: string;
37
+ /**
38
+ * Render function. Принимает `stepper` — JSX-элемент степпера, а также api:
39
+ * `goNext`, `goPrev`, `resetValidation`, `setValidator`, `isCompleted`,
40
+ * `currentStepIndex`, `stepCount`.
41
+ */
42
+ children: (params: StepperApi) => ReactElement;
43
+ /** Колбек смены текущего степа */
44
+ onChangeCurrentStep?: (newValue: number, prevValue: number) => void;
45
+ /** Колбек изменения завершённости */
46
+ onCompleteChange?: (isCompleted: boolean) => void;
47
+ /** data-test-id */
48
+ 'data-test-id'?: string;
49
+ /** Позволяет свободно переключаться между разными шагами без валидации */
50
+ allowFreeNavigation?: boolean;
51
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Собирает data-test-id для вложенных элементов: `${root}${postfix}`.
3
+ * Возвращает undefined если либо postfix, либо testId пустые — так родитель
4
+ * может передать атрибут только когда сам получил тест-ид.
5
+ */
6
+ export declare const getTestIdBuilder: (postfix?: string) => (testId?: string) => string | undefined;
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTestIdBuilder = void 0;
4
+ /**
5
+ * Собирает data-test-id для вложенных элементов: `${root}${postfix}`.
6
+ * Возвращает undefined если либо postfix, либо testId пустые — так родитель
7
+ * может передать атрибут только когда сам получил тест-ид.
8
+ */
9
+ const getTestIdBuilder = (postfix) => (testId) => postfix && testId ? `${testId}${postfix}` : undefined;
10
+ exports.getTestIdBuilder = getTestIdBuilder;
@@ -0,0 +1,10 @@
1
+ import { StepperProps } from '../../types.js';
2
+ /**
3
+ * Адаптивный Stepper. Раскладку берёт из `AdaptiveProvider` (контекст): на `mobile` рендерит
4
+ * компактный мобильный индикатор, иначе — desktop-степпер. Публичный API единый; форс платформы —
5
+ * через `<AdaptiveProvider layoutType=…>` / `withLayoutType` (см. `@cloud-ru/ds-adaptive`), пропа `layoutType` нет.
6
+ */
7
+ export declare function Stepper(props: StepperProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare namespace Stepper {
9
+ var displayName: string;
10
+ }
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { isMobileLayout, useAdaptiveLayout } from '@cloud-ru/ds-adaptive';
3
+ import { DesktopStepper } from '../../helperComponents/DesktopStepper/index.js';
4
+ import { MobileStepper } from '../../helperComponents/MobileStepper/index.js';
5
+ /**
6
+ * Адаптивный Stepper. Раскладку берёт из `AdaptiveProvider` (контекст): на `mobile` рендерит
7
+ * компактный мобильный индикатор, иначе — desktop-степпер. Публичный API единый; форс платформы —
8
+ * через `<AdaptiveProvider layoutType=…>` / `withLayoutType` (см. `@cloud-ru/ds-adaptive`), пропа `layoutType` нет.
9
+ */
10
+ export function Stepper(props) {
11
+ const { layoutType } = useAdaptiveLayout();
12
+ return isMobileLayout(layoutType) ? _jsx(MobileStepper, { ...props }) : _jsx(DesktopStepper, { ...props });
13
+ }
14
+ Stepper.displayName = 'Stepper';
@@ -0,0 +1 @@
1
+ export * from './Stepper.js';
@@ -0,0 +1 @@
1
+ export * from './Stepper.js';
@@ -0,0 +1 @@
1
+ export * from './Stepper/index.js';
@@ -0,0 +1,4 @@
1
+ // Публичная точка входа — адаптивный `Stepper` (раскладку берёт из AdaptiveProvider).
2
+ // `DesktopStepper` / `MobileStepper` — internal-поверхности, наружу не реэкспортятся.
3
+ // `StepperProps` — публичный тип, живёт в `src/types.ts` (реэкспорт через корневой `src/index.ts`).
4
+ export * from './Stepper/index.js';