funda-ui 4.7.101 → 4.7.103

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.
package/Stepper/index.css CHANGED
@@ -5,19 +5,23 @@
5
5
  --stepper-color-default: #333;
6
6
  --stepper-color-active: white;
7
7
  --stepper-color-complete: #2563eb;
8
- --stepper-bg-default: white;
9
- --stepper-bg-active: #2563eb;
10
- --stepper-bg-complete: #22c55e;
8
+ --stepper-indicator-default: white;
9
+ --stepper-indicator-active: #2563eb;
10
+ --stepper-indicator-complete: #22c55e;
11
11
  --stepper-border-default: #ccc;
12
12
  --stepper-border-active: #2563eb;
13
13
  --stepper-border-complete: #22c55e;
14
+ --stepper-line-default: #dfdfdf;
15
+ --stepper-line-active: #2563eb;
16
+ --stepper-line-complete: #22c55e;
14
17
  --stepper-indicator-size: 0.875rem;
18
+ --stepper-indicator-offset: 100px;
15
19
  --stepper-title-size: 0.875rem;
16
20
  position: relative;
17
- /* NAvigation Header (only horizontal) */
18
- /* Main Navigation */
19
- /* Each step item (with circle + title) */
20
- /* Step Indicator */
21
+ /* Navigation Header (only horizontal) */
22
+ /* Main Navigation - Each step item (with circle + title) */
23
+ /* Line */
24
+ /* Indicator */
21
25
  /* Title */
22
26
  /* Panels Area */
23
27
  /* Buttons */
@@ -28,15 +32,43 @@
28
32
  align-items: center;
29
33
  margin-bottom: 1.5rem;
30
34
  flex-wrap: nowrap;
35
+ position: relative;
36
+ /* background line */
37
+ }
38
+ .stepper-container .stepper-header::before {
39
+ content: "";
40
+ position: absolute;
41
+ top: calc(50% - 0.875rem); /* Subtract the height of the title */
42
+ left: 14px;
43
+ right: 0;
44
+ height: 2px;
45
+ background-color: var(--stepper-line-default);
46
+ z-index: 1;
47
+ width: calc(100% - var(--stepper-indicator-offset) / 2);
48
+ }
49
+ .stepper-container .stepper-header::after {
50
+ content: "";
51
+ position: absolute;
52
+ top: calc(50% - 0.875rem); /* Subtract the height of the title */
53
+ left: 14px;
54
+ height: 2px;
55
+ background-color: var(--stepper-line-complete);
56
+ z-index: 2;
57
+ transition: width 0.3s ease-in-out;
58
+ width: 0;
59
+ }
60
+ .stepper-container .stepper-header::after {
61
+ width: var(--stepper-progress-width, 0%);
62
+ max-width: calc(100% - var(--stepper-indicator-offset) / 2);
31
63
  }
32
64
  .stepper-container .step-item {
33
65
  flex: none;
34
66
  display: flex;
35
67
  flex-direction: column;
36
68
  align-items: center;
37
- max-width: 100px;
69
+ max-width: var(--stepper-indicator-offset);
38
70
  position: relative;
39
- z-index: 1;
71
+ z-index: 3;
40
72
  }
41
73
  .stepper-container .step-item.step-item--clickable {
42
74
  cursor: pointer;
@@ -44,17 +76,34 @@
44
76
  .stepper-container .step-line {
45
77
  flex: 1;
46
78
  height: 2px;
47
- background-color: #ddd;
79
+ background-color: var(--stepper-line-default);
48
80
  margin: 0 4px;
49
81
  position: relative;
50
82
  top: -10px;
51
83
  z-index: 0;
84
+ overflow: hidden;
85
+ opacity: 0;
86
+ }
87
+ .stepper-container .step-line--active {
88
+ background-color: var(--stepper-line-default);
52
89
  }
53
90
  .stepper-container .step-line--complete {
54
- background-color: var(--stepper-bg-complete);
91
+ background-color: var(--stepper-line-default);
55
92
  }
56
- .stepper-container .step-line--active {
57
- background-color: var(--stepper-bg-complete);
93
+ .stepper-container .step-line::after {
94
+ content: "";
95
+ position: absolute;
96
+ top: 0;
97
+ left: 0;
98
+ width: 100%;
99
+ height: 100%;
100
+ background-color: var(--stepper-line-complete);
101
+ transform: scaleX(0);
102
+ transform-origin: left;
103
+ transition: transform 0.3s ease-in-out;
104
+ }
105
+ .stepper-container .step-line--active::after, .stepper-container .step-line--complete::after {
106
+ transform: scaleX(1);
58
107
  }
59
108
  .stepper-container .step-indicator {
60
109
  width: 32px;
@@ -67,17 +116,17 @@
67
116
  border: 2px solid #ccc;
68
117
  font-size: var(--stepper-indicator-size);
69
118
  /* default */
70
- background-color: var(--stepper-bg-default);
119
+ background-color: var(--stepper-indicator-default);
71
120
  color: var(--stepper-color-default);
72
121
  border-color: var(--stepper-border-default);
73
122
  }
74
123
  .stepper-container .step-indicator--active {
75
- background-color: var(--stepper-bg-active);
124
+ background-color: var(--stepper-indicator-active);
76
125
  color: var(--stepper-color-active);
77
126
  border-color: var(--stepper-border-active);
78
127
  }
79
128
  .stepper-container .step-indicator--complete {
80
- background-color: var(--stepper-bg-complete);
129
+ background-color: var(--stepper-indicator-complete);
81
130
  color: var(--stepper-color-active);
82
131
  border-color: var(--stepper-border-complete);
83
132
  }
@@ -109,19 +158,51 @@
109
158
 
110
159
  /*------ Verticle ------*/
111
160
  .stepper-container.stepper-container--vertical {
161
+ --stepper-indicator-offset: 50px;
112
162
  display: flex;
113
163
  flex-direction: column;
114
- gap: 1rem;
164
+ gap: 1rem; /* line length */
165
+ /* background line */
166
+ /* Layout */
167
+ /* Title */
168
+ /* Panel */
169
+ /* Line */
170
+ }
171
+ .stepper-container.stepper-container--vertical::before {
172
+ content: "";
173
+ position: absolute;
174
+ top: 20px;
175
+ left: 24px;
176
+ width: 2px;
177
+ height: calc(100% - var(--stepper-indicator-offset));
178
+ background-color: var(--stepper-line-default);
179
+ z-index: 1;
180
+ }
181
+ .stepper-container.stepper-container--vertical::after {
182
+ content: "";
183
+ position: absolute;
184
+ top: 20px;
185
+ left: 24px;
186
+ width: 2px;
187
+ background-color: var(--stepper-line-complete);
188
+ z-index: 2;
189
+ transition: height 0.3s ease-in-out;
190
+ height: 0;
191
+ }
192
+ .stepper-container.stepper-container--vertical::after {
193
+ height: var(--stepper-progress-height, 0%);
194
+ max-height: calc(100% - var(--stepper-indicator-offset));
115
195
  }
116
196
  .stepper-container.stepper-container--vertical .vertical-step-row {
117
197
  display: flex;
118
198
  align-items: flex-start;
119
- margin-bottom: 1rem;
120
199
  }
121
200
  .stepper-container.stepper-container--vertical .vertical-step-left {
122
201
  flex-shrink: 0;
123
- width: 50px;
202
+ width: var(--stepper-indicator-offset);
124
203
  position: relative;
204
+ /* Main Navigation - Each step item (with circle + title) */
205
+ /* Line */
125
206
  }
126
207
  .stepper-container.stepper-container--vertical .vertical-step-left .step-item {
127
208
  margin-top: 20px;
@@ -129,6 +210,7 @@
129
210
  .stepper-container.stepper-container--vertical .vertical-step-left .step-line {
130
211
  position: absolute;
131
212
  left: 20px;
213
+ opacity: 0;
132
214
  }
133
215
  .stepper-container.stepper-container--vertical .vertical-step-right {
134
216
  flex: 1;
@@ -144,19 +226,6 @@
144
226
  .stepper-container.stepper-container--vertical .stepper-panel-header {
145
227
  display: block;
146
228
  }
147
- .stepper-container.stepper-container--vertical .stepper-header {
148
- display: flex;
149
- flex-direction: column;
150
- position: relative;
151
- padding-left: 2rem;
152
- }
153
- .stepper-container.stepper-container--vertical .stepper-header .step-item {
154
- flex-direction: row;
155
- max-width: 150px;
156
- }
157
- .stepper-container.stepper-container--vertical .stepper-header .step-item:not(:first-child) {
158
- margin-top: 8px;
159
- }
160
229
  .stepper-container.stepper-container--vertical .step-line {
161
230
  flex: auto;
162
231
  width: 2px;
@@ -165,3 +234,10 @@
165
234
  top: auto;
166
235
  left: -24px;
167
236
  }
237
+ .stepper-container.stepper-container--vertical .step-line::after {
238
+ transform-origin: top;
239
+ transform: scaleY(0);
240
+ }
241
+ .stepper-container.stepper-container--vertical .step-line--active::after, .stepper-container.stepper-container--vertical .step-line--complete::after {
242
+ transform: scaleY(1);
243
+ }
package/Stepper/index.js CHANGED
@@ -326,6 +326,12 @@ __webpack_require__.r(__webpack_exports__);
326
326
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
327
327
  /* harmony import */ var funda_utils_dist_cjs_cls__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(188);
328
328
  /* harmony import */ var funda_utils_dist_cjs_cls__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(funda_utils_dist_cjs_cls__WEBPACK_IMPORTED_MODULE_1__);
329
+ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
330
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
331
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
332
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
333
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
334
+ function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
329
335
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
330
336
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
331
337
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
@@ -373,6 +379,7 @@ var Stepper = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(fun
373
379
  disableCompleteIcon = _props$disableComplet === void 0 ? true : _props$disableComplet,
374
380
  onChange = props.onChange,
375
381
  children = props.children;
382
+ var rootRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
376
383
  var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),
377
384
  _useState2 = _slicedToArray(_useState, 2),
378
385
  isLastStepComplete = _useState2[0],
@@ -485,6 +492,34 @@ var Stepper = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(fun
485
492
  }, panels[activeIndex]));
486
493
  }));
487
494
  };
495
+
496
+ // Calculate the width/height of the progress line
497
+ var calculateProgressStyle = function calculateProgressStyle() {
498
+ if (!panels.length || rootRef.current === null) return {};
499
+ var stepItems = rootRef.current.querySelectorAll('.step-item');
500
+ if (!stepItems.length) return {};
501
+ if (isVertical) {
502
+ var totalHeight = stepItems[0].clientHeight * (panels.length - 1);
503
+ var progress = activeIndex / (panels.length - 1) * 100;
504
+ return {
505
+ '--stepper-progress-height': "".concat(progress, "%")
506
+ };
507
+ } else {
508
+ var firstItem = stepItems[0];
509
+ var lastItem = stepItems[stepItems.length - 1];
510
+ if (!firstItem || !lastItem) return {};
511
+ var firstCenter = firstItem.offsetLeft + firstItem.clientWidth / 2;
512
+ var lastCenter = lastItem.offsetLeft + lastItem.clientWidth / 2;
513
+ var totalWidth = lastCenter - firstCenter;
514
+ var currentItem = stepItems[activeIndex];
515
+ if (!currentItem) return {};
516
+ var currentCenter = currentItem.offsetLeft + currentItem.clientWidth / 2;
517
+ var _progress = (currentCenter - firstCenter) / totalWidth * 100;
518
+ return {
519
+ '--stepper-progress-width': "".concat(_progress, "%")
520
+ };
521
+ }
522
+ };
488
523
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {
489
524
  // Only trigger onChange if values actually changed from previous values
490
525
  if (prevActiveIndexRef.current !== activeIndex || prevIsLastStepCompleteRef.current !== isLastStepComplete) {
@@ -493,11 +528,23 @@ var Stepper = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(fun
493
528
  onChange === null || onChange === void 0 ? void 0 : onChange(activeIndex, isLastStepComplete);
494
529
  }
495
530
  }, [activeIndex, isLastStepComplete]);
531
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {
532
+ // Force a recalculation of the progress line
533
+ var timer = setTimeout(function () {
534
+ setActiveIndex(function (prev) {
535
+ return prev;
536
+ });
537
+ }, 0);
538
+ return function () {
539
+ return clearTimeout(timer);
540
+ };
541
+ }, []);
496
542
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
543
+ ref: rootRef,
497
544
  className: (0,funda_utils_dist_cjs_cls__WEBPACK_IMPORTED_MODULE_1__.combinedCls)('stepper-container', (0,funda_utils_dist_cjs_cls__WEBPACK_IMPORTED_MODULE_1__.clsWrite)(wrapperClassName, ''), {
498
545
  'stepper-container--vertical': isVertical
499
546
  }),
500
- style: style
547
+ style: _objectSpread(_objectSpread({}, style), calculateProgressStyle())
501
548
  }, !isVertical && horizontalPanelsGenerator(), isVertical && verticalPanelsGenerator());
502
549
  });
503
550
 
@@ -326,6 +326,12 @@ __webpack_require__.r(__webpack_exports__);
326
326
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
327
327
  /* harmony import */ var funda_utils_dist_cjs_cls__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(188);
328
328
  /* harmony import */ var funda_utils_dist_cjs_cls__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(funda_utils_dist_cjs_cls__WEBPACK_IMPORTED_MODULE_1__);
329
+ function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); }
330
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
331
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
332
+ function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
333
+ function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
334
+ function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
329
335
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
330
336
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
331
337
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
@@ -373,6 +379,7 @@ var Stepper = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(fun
373
379
  disableCompleteIcon = _props$disableComplet === void 0 ? true : _props$disableComplet,
374
380
  onChange = props.onChange,
375
381
  children = props.children;
382
+ var rootRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
376
383
  var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),
377
384
  _useState2 = _slicedToArray(_useState, 2),
378
385
  isLastStepComplete = _useState2[0],
@@ -485,6 +492,34 @@ var Stepper = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(fun
485
492
  }, panels[activeIndex]));
486
493
  }));
487
494
  };
495
+
496
+ // Calculate the width/height of the progress line
497
+ var calculateProgressStyle = function calculateProgressStyle() {
498
+ if (!panels.length || rootRef.current === null) return {};
499
+ var stepItems = rootRef.current.querySelectorAll('.step-item');
500
+ if (!stepItems.length) return {};
501
+ if (isVertical) {
502
+ var totalHeight = stepItems[0].clientHeight * (panels.length - 1);
503
+ var progress = activeIndex / (panels.length - 1) * 100;
504
+ return {
505
+ '--stepper-progress-height': "".concat(progress, "%")
506
+ };
507
+ } else {
508
+ var firstItem = stepItems[0];
509
+ var lastItem = stepItems[stepItems.length - 1];
510
+ if (!firstItem || !lastItem) return {};
511
+ var firstCenter = firstItem.offsetLeft + firstItem.clientWidth / 2;
512
+ var lastCenter = lastItem.offsetLeft + lastItem.clientWidth / 2;
513
+ var totalWidth = lastCenter - firstCenter;
514
+ var currentItem = stepItems[activeIndex];
515
+ if (!currentItem) return {};
516
+ var currentCenter = currentItem.offsetLeft + currentItem.clientWidth / 2;
517
+ var _progress = (currentCenter - firstCenter) / totalWidth * 100;
518
+ return {
519
+ '--stepper-progress-width': "".concat(_progress, "%")
520
+ };
521
+ }
522
+ };
488
523
  (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {
489
524
  // Only trigger onChange if values actually changed from previous values
490
525
  if (prevActiveIndexRef.current !== activeIndex || prevIsLastStepCompleteRef.current !== isLastStepComplete) {
@@ -493,11 +528,23 @@ var Stepper = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.forwardRef)(fun
493
528
  onChange === null || onChange === void 0 ? void 0 : onChange(activeIndex, isLastStepComplete);
494
529
  }
495
530
  }, [activeIndex, isLastStepComplete]);
531
+ (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {
532
+ // Force a recalculation of the progress line
533
+ var timer = setTimeout(function () {
534
+ setActiveIndex(function (prev) {
535
+ return prev;
536
+ });
537
+ }, 0);
538
+ return function () {
539
+ return clearTimeout(timer);
540
+ };
541
+ }, []);
496
542
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", {
543
+ ref: rootRef,
497
544
  className: (0,funda_utils_dist_cjs_cls__WEBPACK_IMPORTED_MODULE_1__.combinedCls)('stepper-container', (0,funda_utils_dist_cjs_cls__WEBPACK_IMPORTED_MODULE_1__.clsWrite)(wrapperClassName, ''), {
498
545
  'stepper-container--vertical': isVertical
499
546
  }),
500
- style: style
547
+ style: _objectSpread(_objectSpread({}, style), calculateProgressStyle())
501
548
  }, !isVertical && horizontalPanelsGenerator(), isVertical && verticalPanelsGenerator());
502
549
  });
503
550
 
@@ -5,19 +5,23 @@
5
5
  --stepper-color-default: #333;
6
6
  --stepper-color-active: white;
7
7
  --stepper-color-complete: #2563eb;
8
- --stepper-bg-default: white;
9
- --stepper-bg-active: #2563eb;
10
- --stepper-bg-complete: #22c55e;
8
+ --stepper-indicator-default: white;
9
+ --stepper-indicator-active: #2563eb;
10
+ --stepper-indicator-complete: #22c55e;
11
11
  --stepper-border-default: #ccc;
12
12
  --stepper-border-active: #2563eb;
13
13
  --stepper-border-complete: #22c55e;
14
+ --stepper-line-default: #dfdfdf;
15
+ --stepper-line-active: #2563eb;
16
+ --stepper-line-complete: #22c55e;
14
17
  --stepper-indicator-size: 0.875rem;
18
+ --stepper-indicator-offset: 100px;
15
19
  --stepper-title-size: 0.875rem;
16
20
  position: relative;
17
- /* NAvigation Header (only horizontal) */
18
- /* Main Navigation */
19
- /* Each step item (with circle + title) */
20
- /* Step Indicator */
21
+ /* Navigation Header (only horizontal) */
22
+ /* Main Navigation - Each step item (with circle + title) */
23
+ /* Line */
24
+ /* Indicator */
21
25
  /* Title */
22
26
  /* Panels Area */
23
27
  /* Buttons */
@@ -28,15 +32,43 @@
28
32
  align-items: center;
29
33
  margin-bottom: 1.5rem;
30
34
  flex-wrap: nowrap;
35
+ position: relative;
36
+ /* background line */
37
+ }
38
+ .stepper-container .stepper-header::before {
39
+ content: "";
40
+ position: absolute;
41
+ top: calc(50% - 0.875rem); /* Subtract the height of the title */
42
+ left: 14px;
43
+ right: 0;
44
+ height: 2px;
45
+ background-color: var(--stepper-line-default);
46
+ z-index: 1;
47
+ width: calc(100% - var(--stepper-indicator-offset) / 2);
48
+ }
49
+ .stepper-container .stepper-header::after {
50
+ content: "";
51
+ position: absolute;
52
+ top: calc(50% - 0.875rem); /* Subtract the height of the title */
53
+ left: 14px;
54
+ height: 2px;
55
+ background-color: var(--stepper-line-complete);
56
+ z-index: 2;
57
+ transition: width 0.3s ease-in-out;
58
+ width: 0;
59
+ }
60
+ .stepper-container .stepper-header::after {
61
+ width: var(--stepper-progress-width, 0%);
62
+ max-width: calc(100% - var(--stepper-indicator-offset) / 2);
31
63
  }
32
64
  .stepper-container .step-item {
33
65
  flex: none;
34
66
  display: flex;
35
67
  flex-direction: column;
36
68
  align-items: center;
37
- max-width: 100px;
69
+ max-width: var(--stepper-indicator-offset);
38
70
  position: relative;
39
- z-index: 1;
71
+ z-index: 3;
40
72
  }
41
73
  .stepper-container .step-item.step-item--clickable {
42
74
  cursor: pointer;
@@ -44,17 +76,34 @@
44
76
  .stepper-container .step-line {
45
77
  flex: 1;
46
78
  height: 2px;
47
- background-color: #ddd;
79
+ background-color: var(--stepper-line-default);
48
80
  margin: 0 4px;
49
81
  position: relative;
50
82
  top: -10px;
51
83
  z-index: 0;
84
+ overflow: hidden;
85
+ opacity: 0;
86
+ }
87
+ .stepper-container .step-line--active {
88
+ background-color: var(--stepper-line-default);
52
89
  }
53
90
  .stepper-container .step-line--complete {
54
- background-color: var(--stepper-bg-complete);
91
+ background-color: var(--stepper-line-default);
55
92
  }
56
- .stepper-container .step-line--active {
57
- background-color: var(--stepper-bg-complete);
93
+ .stepper-container .step-line::after {
94
+ content: "";
95
+ position: absolute;
96
+ top: 0;
97
+ left: 0;
98
+ width: 100%;
99
+ height: 100%;
100
+ background-color: var(--stepper-line-complete);
101
+ transform: scaleX(0);
102
+ transform-origin: left;
103
+ transition: transform 0.3s ease-in-out;
104
+ }
105
+ .stepper-container .step-line--active::after, .stepper-container .step-line--complete::after {
106
+ transform: scaleX(1);
58
107
  }
59
108
  .stepper-container .step-indicator {
60
109
  width: 32px;
@@ -67,17 +116,17 @@
67
116
  border: 2px solid #ccc;
68
117
  font-size: var(--stepper-indicator-size);
69
118
  /* default */
70
- background-color: var(--stepper-bg-default);
119
+ background-color: var(--stepper-indicator-default);
71
120
  color: var(--stepper-color-default);
72
121
  border-color: var(--stepper-border-default);
73
122
  }
74
123
  .stepper-container .step-indicator--active {
75
- background-color: var(--stepper-bg-active);
124
+ background-color: var(--stepper-indicator-active);
76
125
  color: var(--stepper-color-active);
77
126
  border-color: var(--stepper-border-active);
78
127
  }
79
128
  .stepper-container .step-indicator--complete {
80
- background-color: var(--stepper-bg-complete);
129
+ background-color: var(--stepper-indicator-complete);
81
130
  color: var(--stepper-color-active);
82
131
  border-color: var(--stepper-border-complete);
83
132
  }
@@ -109,19 +158,51 @@
109
158
 
110
159
  /*------ Verticle ------*/
111
160
  .stepper-container.stepper-container--vertical {
161
+ --stepper-indicator-offset: 50px;
112
162
  display: flex;
113
163
  flex-direction: column;
114
- gap: 1rem;
164
+ gap: 1rem; /* line length */
165
+ /* background line */
166
+ /* Layout */
167
+ /* Title */
168
+ /* Panel */
169
+ /* Line */
170
+ }
171
+ .stepper-container.stepper-container--vertical::before {
172
+ content: "";
173
+ position: absolute;
174
+ top: 20px;
175
+ left: 24px;
176
+ width: 2px;
177
+ height: calc(100% - var(--stepper-indicator-offset));
178
+ background-color: var(--stepper-line-default);
179
+ z-index: 1;
180
+ }
181
+ .stepper-container.stepper-container--vertical::after {
182
+ content: "";
183
+ position: absolute;
184
+ top: 20px;
185
+ left: 24px;
186
+ width: 2px;
187
+ background-color: var(--stepper-line-complete);
188
+ z-index: 2;
189
+ transition: height 0.3s ease-in-out;
190
+ height: 0;
191
+ }
192
+ .stepper-container.stepper-container--vertical::after {
193
+ height: var(--stepper-progress-height, 0%);
194
+ max-height: calc(100% - var(--stepper-indicator-offset));
115
195
  }
116
196
  .stepper-container.stepper-container--vertical .vertical-step-row {
117
197
  display: flex;
118
198
  align-items: flex-start;
119
- margin-bottom: 1rem;
120
199
  }
121
200
  .stepper-container.stepper-container--vertical .vertical-step-left {
122
201
  flex-shrink: 0;
123
- width: 50px;
202
+ width: var(--stepper-indicator-offset);
124
203
  position: relative;
204
+ /* Main Navigation - Each step item (with circle + title) */
205
+ /* Line */
125
206
  }
126
207
  .stepper-container.stepper-container--vertical .vertical-step-left .step-item {
127
208
  margin-top: 20px;
@@ -129,6 +210,7 @@
129
210
  .stepper-container.stepper-container--vertical .vertical-step-left .step-line {
130
211
  position: absolute;
131
212
  left: 20px;
213
+ opacity: 0;
132
214
  }
133
215
  .stepper-container.stepper-container--vertical .vertical-step-right {
134
216
  flex: 1;
@@ -144,19 +226,6 @@
144
226
  .stepper-container.stepper-container--vertical .stepper-panel-header {
145
227
  display: block;
146
228
  }
147
- .stepper-container.stepper-container--vertical .stepper-header {
148
- display: flex;
149
- flex-direction: column;
150
- position: relative;
151
- padding-left: 2rem;
152
- }
153
- .stepper-container.stepper-container--vertical .stepper-header .step-item {
154
- flex-direction: row;
155
- max-width: 150px;
156
- }
157
- .stepper-container.stepper-container--vertical .stepper-header .step-item:not(:first-child) {
158
- margin-top: 8px;
159
- }
160
229
  .stepper-container.stepper-container--vertical .step-line {
161
230
  flex: auto;
162
231
  width: 2px;
@@ -165,3 +234,10 @@
165
234
  top: auto;
166
235
  left: -24px;
167
236
  }
237
+ .stepper-container.stepper-container--vertical .step-line::after {
238
+ transform-origin: top;
239
+ transform: scaleY(0);
240
+ }
241
+ .stepper-container.stepper-container--vertical .step-line--active::after, .stepper-container.stepper-container--vertical .step-line--complete::after {
242
+ transform: scaleY(1);
243
+ }
@@ -1,4 +1,5 @@
1
1
 
2
+
2
3
  /* ======================================================
3
4
  <!-- Stepper -->
4
5
  /* ====================================================== */
@@ -8,36 +9,70 @@
8
9
  --stepper-color-default: #333;
9
10
  --stepper-color-active: white;
10
11
  --stepper-color-complete: #2563eb;
11
- --stepper-bg-default: white;
12
- --stepper-bg-active: #2563eb;
13
- --stepper-bg-complete: #22c55e;
12
+ --stepper-indicator-default: white;
13
+ --stepper-indicator-active: #2563eb;
14
+ --stepper-indicator-complete: #22c55e;
14
15
  --stepper-border-default: #ccc;
15
16
  --stepper-border-active: #2563eb;
16
17
  --stepper-border-complete: #22c55e;
18
+ --stepper-line-default: #dfdfdf;
19
+ --stepper-line-active: #2563eb;
20
+ --stepper-line-complete: #22c55e;
17
21
  --stepper-indicator-size: 0.875rem;
22
+ --stepper-indicator-offset: 100px;
18
23
  --stepper-title-size: 0.875rem;
19
-
24
+
20
25
 
21
26
  position: relative;
22
27
 
23
- /* NAvigation Header (only horizontal) */
28
+ /* Navigation Header (only horizontal) */
24
29
  .stepper-header {
25
30
  display: flex;
26
31
  align-items: center;
27
32
  margin-bottom: 1.5rem;
28
33
  flex-wrap: nowrap;
34
+ position: relative;
35
+
36
+ /* background line */
37
+ &::before {
38
+ content: '';
39
+ position: absolute;
40
+ top: calc(50% - 0.875rem); /* Subtract the height of the title */
41
+ left: 14px;
42
+ right: 0;
43
+ height: 2px;
44
+ background-color: var(--stepper-line-default);
45
+ z-index: 1;
46
+ width: calc(100% - calc(var(--stepper-indicator-offset)/2));
47
+ }
48
+
49
+ &::after {
50
+ content: '';
51
+ position: absolute;
52
+ top: calc(50% - 0.875rem); /* Subtract the height of the title */
53
+ left: 14px;
54
+ height: 2px;
55
+ background-color: var(--stepper-line-complete);
56
+ z-index: 2;
57
+ transition: width 0.3s ease-in-out;
58
+ width: 0;
59
+ }
60
+
61
+ &::after {
62
+ width: var(--stepper-progress-width, 0%);
63
+ max-width: calc(100% - calc(var(--stepper-indicator-offset)/2));
64
+ }
29
65
  }
30
66
 
31
- /* Main Navigation */
32
- /* Each step item (with circle + title) */
67
+ /* Main Navigation - Each step item (with circle + title) */
33
68
  .step-item {
34
69
  flex: none;
35
70
  display: flex;
36
71
  flex-direction: column;
37
72
  align-items: center;
38
- max-width: 100px;
73
+ max-width: var(--stepper-indicator-offset);
39
74
  position: relative;
40
- z-index: 1;
75
+ z-index: 3;
41
76
 
42
77
  &.step-item--clickable {
43
78
  cursor: pointer;
@@ -45,26 +80,49 @@
45
80
 
46
81
  }
47
82
 
83
+
84
+ /* Line */
48
85
  .step-line {
49
86
  flex: 1;
50
87
  height: 2px;
51
- background-color: #ddd;
88
+ background-color: var(--stepper-line-default);
52
89
  margin: 0 4px;
53
90
  position: relative;
54
91
  top: -10px;
55
92
  z-index: 0;
93
+ overflow: hidden;
94
+ opacity: 0;
95
+
96
+ &--active {
97
+ background-color: var(--stepper-line-default);
98
+ }
56
99
 
57
100
  &--complete {
58
- background-color: var(--stepper-bg-complete);
101
+ background-color: var(--stepper-line-default);
59
102
  }
103
+
104
+ &::after {
105
+ content: '';
106
+ position: absolute;
107
+ top: 0;
108
+ left: 0;
109
+ width: 100%;
110
+ height: 100%;
111
+ background-color: var(--stepper-line-complete);
112
+ transform: scaleX(0);
113
+ transform-origin: left;
114
+ transition: transform 0.3s ease-in-out;
115
+ }
60
116
 
61
- &--active {
62
- background-color: var(--stepper-bg-complete);
117
+ &--active::after,
118
+ &--complete::after {
119
+ transform: scaleX(1);
63
120
  }
64
-
65
121
  }
66
122
 
67
- /* Step Indicator */
123
+
124
+
125
+ /* Indicator */
68
126
  .step-indicator {
69
127
  width: 32px;
70
128
  height: 32px;
@@ -77,18 +135,18 @@
77
135
  font-size: var(--stepper-indicator-size);
78
136
 
79
137
  /* default */
80
- background-color: var(--stepper-bg-default);
138
+ background-color: var(--stepper-indicator-default);
81
139
  color: var(--stepper-color-default);
82
140
  border-color: var(--stepper-border-default);
83
141
 
84
142
  &--active {
85
- background-color: var(--stepper-bg-active);
143
+ background-color: var(--stepper-indicator-active);
86
144
  color: var(--stepper-color-active);
87
145
  border-color: var(--stepper-border-active);
88
146
  }
89
147
 
90
148
  &--complete {
91
- background-color: var(--stepper-bg-complete);
149
+ background-color: var(--stepper-indicator-complete);
92
150
  color: var(--stepper-color-active);
93
151
  border-color: var(--stepper-border-complete);
94
152
  }
@@ -143,28 +201,65 @@
143
201
 
144
202
  /*------ Verticle ------*/
145
203
  .stepper-container.stepper-container--vertical {
204
+
205
+ --stepper-indicator-offset: 50px;
206
+
146
207
  display: flex;
147
208
  flex-direction: column;
148
- gap: 1rem;
209
+ gap: 1rem; /* line length */
210
+
211
+ /* background line */
212
+ &::before {
213
+ content: '';
214
+ position: absolute;
215
+ top: 20px;
216
+ left: 24px;
217
+ width: 2px;
218
+ height: calc(100% - calc(var(--stepper-indicator-offset)));
219
+ background-color: var(--stepper-line-default);
220
+ z-index: 1;
221
+ }
222
+
223
+ &::after {
224
+ content: '';
225
+ position: absolute;
226
+ top: 20px;
227
+ left: 24px;
228
+ width: 2px;
229
+ background-color: var(--stepper-line-complete);
230
+ z-index: 2;
231
+ transition: height 0.3s ease-in-out;
232
+ height: 0;
233
+ }
234
+
235
+ &::after {
236
+ height: var(--stepper-progress-height, 0%);
237
+ max-height: calc(100% - calc(var(--stepper-indicator-offset)));
238
+ }
149
239
 
150
240
 
241
+ /* Layout */
151
242
  .vertical-step-row {
152
243
  display: flex;
153
244
  align-items: flex-start;
154
- margin-bottom: 1rem;
155
245
  }
156
246
  .vertical-step-left {
157
247
  flex-shrink: 0;
158
- width: 50px;
248
+ width: var(--stepper-indicator-offset);
159
249
  position: relative;
160
250
 
251
+ /* Main Navigation - Each step item (with circle + title) */
161
252
  .step-item {
162
253
  margin-top: 20px;
163
254
  }
255
+
256
+ /* Line */
164
257
  .step-line {
165
258
  position: absolute;
166
259
  left: 20px;
260
+ opacity: 0;
167
261
  }
262
+
168
263
  }
169
264
 
170
265
  .vertical-step-right {
@@ -175,26 +270,20 @@
175
270
  top: 1.5rem;
176
271
  }
177
272
 
273
+ /* Title */
178
274
  .step-title {
179
275
  display: none;
180
276
  margin-left: .3rem;
181
277
  }
278
+
279
+ /* Panel */
182
280
  .stepper-panel-header {
183
281
  display: block;
184
282
  }
185
- .stepper-header {
186
- display: flex;
187
- flex-direction: column;
188
- position: relative;
189
- padding-left: 2rem;
190
- .step-item {
191
- flex-direction: row;
192
- max-width: 150px;
193
- &:not(:first-child) {
194
- margin-top: 8px;
195
- }
196
- }
197
- }
283
+
284
+
285
+
286
+ /* Line */
198
287
  .step-line {
199
288
  flex: auto;
200
289
  width: 2px;
@@ -202,5 +291,16 @@
202
291
  margin-top: 4px;
203
292
  top: auto;
204
293
  left: -24px;
294
+
295
+ &::after {
296
+ transform-origin: top;
297
+ transform: scaleY(0);
298
+ }
299
+
300
+ &--active::after,
301
+ &--complete::after {
302
+ transform: scaleY(1);
303
+ }
304
+
205
305
  }
206
306
  }
@@ -2,6 +2,7 @@ import React, { useRef, useEffect, forwardRef, useImperativeHandle, useState } f
2
2
 
3
3
  import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
4
4
 
5
+
5
6
  interface StepperPanelProps {
6
7
  header: React.ReactNode;
7
8
  children?: React.ReactNode;
@@ -55,6 +56,7 @@ const Stepper = forwardRef<StepperRef, StepperProps>((props, ref) => {
55
56
  } = props;
56
57
 
57
58
 
59
+ const rootRef = useRef<HTMLDivElement>(null);
58
60
  const [isLastStepComplete, setIsLastStepComplete] = useState<boolean>(false);
59
61
  const [activeIndex, setActiveIndex] = useState<number>(initialStep);
60
62
  const panels = React.Children.toArray(children) as React.ReactElement<StepperPanelProps>[];
@@ -230,6 +232,39 @@ const Stepper = forwardRef<StepperRef, StepperProps>((props, ref) => {
230
232
  </>;
231
233
  };
232
234
 
235
+ // Calculate the width/height of the progress line
236
+ const calculateProgressStyle = () => {
237
+ if (!panels.length || rootRef.current === null) return {};
238
+
239
+ const stepItems = rootRef.current.querySelectorAll('.step-item');
240
+ if (!stepItems.length) return {};
241
+
242
+ if (isVertical) {
243
+ const totalHeight = stepItems[0].clientHeight * (panels.length - 1);
244
+ const progress = (activeIndex / (panels.length - 1)) * 100;
245
+ return {
246
+ '--stepper-progress-height': `${progress}%`
247
+ } as React.CSSProperties;
248
+ } else {
249
+ const firstItem = stepItems[0] as HTMLDivElement;
250
+ const lastItem = stepItems[stepItems.length - 1] as HTMLDivElement;
251
+ if (!firstItem || !lastItem) return {};
252
+
253
+ const firstCenter = firstItem.offsetLeft + (firstItem.clientWidth / 2);
254
+ const lastCenter = lastItem.offsetLeft + (lastItem.clientWidth / 2);
255
+ const totalWidth = lastCenter - firstCenter;
256
+
257
+ const currentItem = stepItems[activeIndex] as HTMLDivElement;
258
+ if (!currentItem) return {};
259
+
260
+ const currentCenter = currentItem.offsetLeft + (currentItem.clientWidth / 2);
261
+ const progress = ((currentCenter - firstCenter) / totalWidth) * 100;
262
+
263
+ return {
264
+ '--stepper-progress-width': `${progress}%`
265
+ } as React.CSSProperties;
266
+ }
267
+ };
233
268
 
234
269
  useEffect(() => {
235
270
  // Only trigger onChange if values actually changed from previous values
@@ -243,8 +278,18 @@ const Stepper = forwardRef<StepperRef, StepperProps>((props, ref) => {
243
278
  }
244
279
  }, [activeIndex, isLastStepComplete]);
245
280
 
281
+
282
+ useEffect(() => {
283
+ // Force a recalculation of the progress line
284
+ const timer = setTimeout(() => {
285
+ setActiveIndex(prev => prev);
286
+ }, 0);
287
+ return () => clearTimeout(timer);
288
+ }, []);
289
+
246
290
  return (
247
291
  <div
292
+ ref={rootRef}
248
293
  className={combinedCls(
249
294
  'stepper-container',
250
295
  clsWrite(wrapperClassName, ''),
@@ -252,7 +297,10 @@ const Stepper = forwardRef<StepperRef, StepperProps>((props, ref) => {
252
297
  'stepper-container--vertical': isVertical
253
298
  }
254
299
  )}
255
- style={style}
300
+ style={{
301
+ ...style,
302
+ ...calculateProgressStyle()
303
+ }}
256
304
  >
257
305
  {!isVertical && horizontalPanelsGenerator()}
258
306
  {isVertical && verticalPanelsGenerator()}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "UIUX Lab",
3
3
  "email": "uiuxlab@gmail.com",
4
4
  "name": "funda-ui",
5
- "version": "4.7.101",
5
+ "version": "4.7.103",
6
6
  "description": "React components using pure Bootstrap 5+ which does not contain any external style and script libraries.",
7
7
  "repository": {
8
8
  "type": "git",