funda-ui 4.7.101 → 4.7.105

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 (54) hide show
  1. package/Chatbox/index.js +6 -1
  2. package/Checkbox/index.js +10 -1
  3. package/Date/index.js +12 -2
  4. package/Input/index.js +6 -1
  5. package/LiveSearch/index.js +5 -0
  6. package/MultipleCheckboxes/index.js +27 -1
  7. package/NumberInput/index.js +6 -1
  8. package/Radio/index.js +22 -1
  9. package/RangeSlider/index.js +6 -1
  10. package/Stepper/index.css +109 -34
  11. package/Stepper/index.d.ts +1 -1
  12. package/Stepper/index.js +55 -2
  13. package/TagInput/index.js +10 -1
  14. package/Textarea/index.js +6 -1
  15. package/Toast/index.css +23 -75
  16. package/Toast/index.d.ts +3 -34
  17. package/Toast/index.js +652 -175
  18. package/lib/cjs/Chatbox/index.js +6 -1
  19. package/lib/cjs/Checkbox/index.js +10 -1
  20. package/lib/cjs/Date/index.js +12 -2
  21. package/lib/cjs/Input/index.js +6 -1
  22. package/lib/cjs/LiveSearch/index.js +5 -0
  23. package/lib/cjs/MultipleCheckboxes/index.js +27 -1
  24. package/lib/cjs/NumberInput/index.js +6 -1
  25. package/lib/cjs/Radio/index.js +22 -1
  26. package/lib/cjs/RangeSlider/index.js +6 -1
  27. package/lib/cjs/Stepper/index.d.ts +1 -1
  28. package/lib/cjs/Stepper/index.js +55 -2
  29. package/lib/cjs/TagInput/index.js +10 -1
  30. package/lib/cjs/Textarea/index.js +6 -1
  31. package/lib/cjs/Toast/index.d.ts +3 -34
  32. package/lib/cjs/Toast/index.js +652 -175
  33. package/lib/css/Stepper/index.css +109 -34
  34. package/lib/css/Toast/index.css +23 -75
  35. package/lib/esm/Checkbox/index.tsx +12 -1
  36. package/lib/esm/Date/index.tsx +8 -1
  37. package/lib/esm/Input/index.tsx +8 -1
  38. package/lib/esm/LiveSearch/index.tsx +7 -0
  39. package/lib/esm/MultipleCheckboxes/index.tsx +19 -1
  40. package/lib/esm/NumberInput/index.tsx +8 -1
  41. package/lib/esm/Radio/index.tsx +17 -1
  42. package/lib/esm/Stepper/index.scss +135 -36
  43. package/lib/esm/Stepper/index.tsx +51 -3
  44. package/lib/esm/TagInput/index.tsx +8 -1
  45. package/lib/esm/Textarea/index.tsx +8 -1
  46. package/lib/esm/Toast/Item.tsx +52 -11
  47. package/lib/esm/Toast/Toast.tsx +391 -0
  48. package/lib/esm/Toast/ToastContext.tsx +104 -0
  49. package/lib/esm/Toast/__toast.vanilla.js +422 -0
  50. package/lib/esm/Toast/index.scss +24 -96
  51. package/lib/esm/Toast/index.tsx +3 -374
  52. package/lib/esm/Toast/types.ts +60 -0
  53. package/lib/esm/Toast/useToast.tsx +72 -0
  54. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
 
2
+
2
3
  /* ======================================================
3
4
  <!-- Stepper -->
4
5
  /* ====================================================== */
@@ -8,36 +9,69 @@
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
- align-items: center;
27
- margin-bottom: 1.5rem;
31
+ align-items: flex-start;
28
32
  flex-wrap: nowrap;
33
+ position: relative;
34
+
35
+ /* background line */
36
+ &::before {
37
+ content: '';
38
+ position: absolute;
39
+ top: 16px; /* Subtract the height of the title */
40
+ left: 14px;
41
+ right: 0;
42
+ height: 2px;
43
+ background-color: var(--stepper-line-default);
44
+ z-index: 1;
45
+ width: calc(100% - 32px);
46
+ }
47
+
48
+ &::after {
49
+ content: '';
50
+ position: absolute;
51
+ top: 16px; /* Subtract the height of the title */
52
+ left: 14px;
53
+ height: 2px;
54
+ background-color: var(--stepper-line-complete);
55
+ z-index: 2;
56
+ transition: width 0.3s ease-in-out;
57
+ width: 0;
58
+ }
59
+
60
+ &::after {
61
+ width: var(--stepper-progress-width, 0%);
62
+ max-width: calc(100% - 32px);
63
+ }
29
64
  }
30
65
 
31
- /* Main Navigation */
32
- /* Each step item (with circle + title) */
66
+ /* Main Navigation - Each step item (with circle + title) */
33
67
  .step-item {
34
68
  flex: none;
35
69
  display: flex;
36
70
  flex-direction: column;
37
71
  align-items: center;
38
- max-width: 100px;
72
+ max-width: var(--stepper-indicator-offset);
39
73
  position: relative;
40
- z-index: 1;
74
+ z-index: 3;
41
75
 
42
76
  &.step-item--clickable {
43
77
  cursor: pointer;
@@ -45,26 +79,49 @@
45
79
 
46
80
  }
47
81
 
82
+
83
+ /* Line */
48
84
  .step-line {
49
85
  flex: 1;
50
86
  height: 2px;
51
- background-color: #ddd;
87
+ background-color: var(--stepper-line-default);
52
88
  margin: 0 4px;
53
89
  position: relative;
54
90
  top: -10px;
55
91
  z-index: 0;
92
+ overflow: hidden;
93
+ opacity: 0;
94
+
95
+ &--active {
96
+ background-color: var(--stepper-line-default);
97
+ }
56
98
 
57
99
  &--complete {
58
- background-color: var(--stepper-bg-complete);
100
+ background-color: var(--stepper-line-default);
59
101
  }
102
+
103
+ &::after {
104
+ content: '';
105
+ position: absolute;
106
+ top: 0;
107
+ left: 0;
108
+ width: 100%;
109
+ height: 100%;
110
+ background-color: var(--stepper-line-complete);
111
+ transform: scaleX(0);
112
+ transform-origin: left;
113
+ transition: transform 0.3s ease-in-out;
114
+ }
60
115
 
61
- &--active {
62
- background-color: var(--stepper-bg-complete);
116
+ &--active::after,
117
+ &--complete::after {
118
+ transform: scaleX(1);
63
119
  }
64
-
65
120
  }
66
121
 
67
- /* Step Indicator */
122
+
123
+
124
+ /* Indicator */
68
125
  .step-indicator {
69
126
  width: 32px;
70
127
  height: 32px;
@@ -77,18 +134,18 @@
77
134
  font-size: var(--stepper-indicator-size);
78
135
 
79
136
  /* default */
80
- background-color: var(--stepper-bg-default);
137
+ background-color: var(--stepper-indicator-default);
81
138
  color: var(--stepper-color-default);
82
139
  border-color: var(--stepper-border-default);
83
140
 
84
141
  &--active {
85
- background-color: var(--stepper-bg-active);
142
+ background-color: var(--stepper-indicator-active);
86
143
  color: var(--stepper-color-active);
87
144
  border-color: var(--stepper-border-active);
88
145
  }
89
146
 
90
147
  &--complete {
91
- background-color: var(--stepper-bg-complete);
148
+ background-color: var(--stepper-indicator-complete);
92
149
  color: var(--stepper-color-active);
93
150
  border-color: var(--stepper-border-complete);
94
151
  }
@@ -143,28 +200,65 @@
143
200
 
144
201
  /*------ Verticle ------*/
145
202
  .stepper-container.stepper-container--vertical {
203
+
204
+ --stepper-indicator-offset: 50px;
205
+
146
206
  display: flex;
147
207
  flex-direction: column;
148
- gap: 1rem;
208
+ gap: 1rem; /* line length */
209
+
210
+ /* background line */
211
+ &::before {
212
+ content: '';
213
+ position: absolute;
214
+ top: 20px;
215
+ left: 24px;
216
+ width: 2px;
217
+ height: calc(100% - 32px);
218
+ background-color: var(--stepper-line-default);
219
+ z-index: 1;
220
+ }
221
+
222
+ &::after {
223
+ content: '';
224
+ position: absolute;
225
+ top: 20px;
226
+ left: 24px;
227
+ width: 2px;
228
+ background-color: var(--stepper-line-complete);
229
+ z-index: 2;
230
+ transition: height 0.3s ease-in-out;
231
+ height: 0;
232
+ }
233
+
234
+ &::after {
235
+ height: var(--stepper-progress-height, 0%);
236
+ max-height: calc(100% - 32px);
237
+ }
149
238
 
150
239
 
240
+ /* Layout */
151
241
  .vertical-step-row {
152
242
  display: flex;
153
243
  align-items: flex-start;
154
- margin-bottom: 1rem;
155
244
  }
156
245
  .vertical-step-left {
157
246
  flex-shrink: 0;
158
- width: 50px;
247
+ width: var(--stepper-indicator-offset);
159
248
  position: relative;
160
249
 
250
+ /* Main Navigation - Each step item (with circle + title) */
161
251
  .step-item {
162
252
  margin-top: 20px;
163
253
  }
254
+
255
+ /* Line */
164
256
  .step-line {
165
257
  position: absolute;
166
258
  left: 20px;
259
+ opacity: 0;
167
260
  }
261
+
168
262
  }
169
263
 
170
264
  .vertical-step-right {
@@ -175,26 +269,20 @@
175
269
  top: 1.5rem;
176
270
  }
177
271
 
272
+ /* Title */
178
273
  .step-title {
179
274
  display: none;
180
275
  margin-left: .3rem;
181
276
  }
277
+
278
+ /* Panel */
182
279
  .stepper-panel-header {
183
280
  display: block;
184
281
  }
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
- }
282
+
283
+
284
+
285
+ /* Line */
198
286
  .step-line {
199
287
  flex: auto;
200
288
  width: 2px;
@@ -202,5 +290,16 @@
202
290
  margin-top: 4px;
203
291
  top: auto;
204
292
  left: -24px;
293
+
294
+ &::after {
295
+ transform-origin: top;
296
+ transform: scaleY(0);
297
+ }
298
+
299
+ &--active::after,
300
+ &--complete::after {
301
+ transform: scaleY(1);
302
+ }
303
+
205
304
  }
206
305
  }
@@ -2,8 +2,9 @@ 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
- header: React.ReactNode;
7
+ header?: React.ReactNode;
7
8
  children?: React.ReactNode;
8
9
  style?: React.CSSProperties;
9
10
  }
@@ -28,7 +29,7 @@ interface StepperRef {
28
29
 
29
30
  const StepperPanel: React.FC<StepperPanelProps> = (props) => {
30
31
  const {
31
- header,
32
+ header = <><svg width="1rem" height="1rem"><rect fill="none"/></svg></>,
32
33
  children,
33
34
  style
34
35
  } = props;
@@ -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()}
@@ -102,16 +102,23 @@ const TagInput = forwardRef((props: TagInputProps, externalRef: any) => {
102
102
  control: () => {
103
103
  return valRef.current;
104
104
  },
105
+ getLatestVal: () => {
106
+ return VALUE_BY_BRACKETS ? convertArrToValByBrackets(items.map((item: any) => item.content)) : items.map((item: any) => item.content).join(',');
107
+ },
105
108
  clear: (cb?: any) => {
106
109
  initDefaultValue('');
107
110
  cb?.();
111
+
112
+ onChange?.(inputRef.current, items, '');
108
113
  },
109
114
  set: (value: string, cb?: any) => {
110
115
  initDefaultValue(`${value}`);
111
116
  cb?.();
117
+
118
+ onChange?.(inputRef.current, items, `${value}`);
112
119
  }
113
120
  }),
114
- [contentRef],
121
+ [contentRef, items],
115
122
  );
116
123
 
117
124
 
@@ -297,13 +297,20 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
297
297
  control: () => {
298
298
  return valRef.current;
299
299
  },
300
+ getLatestVal: () => {
301
+ return changedVal || '';
302
+ },
300
303
  clear: (cb?: any) => {
301
304
  setChangedVal('');
302
305
  cb?.();
306
+
307
+ onChange?.(null, valRef.current, '');
303
308
  },
304
309
  set: (value: string, cb?: any) => {
305
310
  setChangedVal(`${value}`);
306
311
  cb?.();
312
+
313
+ onChange?.(null, valRef.current, `${value}`);
307
314
  },
308
315
  resetHeight: () => {
309
316
  reset();
@@ -314,7 +321,7 @@ const Textarea = forwardRef((props: TextareaProps, externalRef: any) => {
314
321
  }, 0);
315
322
  },
316
323
  }),
317
- [contentRef, reset]
324
+ [contentRef, reset, changedVal]
318
325
  );
319
326
 
320
327
  const propExist = (p: any) => {
@@ -1,13 +1,20 @@
1
1
  import React, { useRef, forwardRef } from 'react';
2
2
 
3
+ import { clsWrite, combinedCls } from 'funda-utils/dist/cjs/cls';
4
+
5
+
3
6
  export interface ItemProps extends React.ComponentPropsWithoutRef<any> {
4
- onlyOne?: boolean;
7
+ depth: number;
5
8
  index: number;
9
+ isNew: boolean;
10
+ uniqueID: string;
11
+
12
+ //
13
+ onlyOne?: boolean;
6
14
  title?: string | React.ReactNode | boolean;
7
15
  note?: string | React.ReactNode | boolean;
8
16
  theme?: string | undefined;
9
17
  message?: string | React.ReactNode;
10
- depth: number;
11
18
  schemeBody?: string;
12
19
  schemeHeader?: string;
13
20
  closeBtnColor?: string;
@@ -24,13 +31,17 @@ export interface ItemProps extends React.ComponentPropsWithoutRef<any> {
24
31
  const Item = forwardRef((props: ItemProps, externalRef: any) => {
25
32
 
26
33
  const {
27
- onlyOne,
34
+ depth,
28
35
  index,
36
+ isNew,
37
+ uniqueID,
38
+
39
+ //
40
+ onlyOne,
29
41
  title,
30
42
  note,
31
43
  theme,
32
44
  message,
33
- depth,
34
45
  lock,
35
46
  cascading,
36
47
  schemeBody,
@@ -43,16 +54,25 @@ const Item = forwardRef((props: ItemProps, externalRef: any) => {
43
54
  evClose
44
55
  } = props;
45
56
 
46
-
57
+
47
58
  const containerRef = useRef<HTMLDivElement>(null);
59
+ const hideTitle: boolean = (title === '' || title === false) && (note === '' || note === false);
48
60
 
49
61
  return (
50
62
  <>
51
63
 
52
64
  <div
53
65
  ref={containerRef}
54
- className={`toast-container ${onlyOne ? 'only-one' : ''}`}
66
+ id={`toast-${uniqueID}`}
67
+ data-toast-id={uniqueID}
68
+ data-new={isNew}
55
69
  data-index={index}
70
+ className={combinedCls(
71
+ 'toast-container',
72
+ {
73
+ 'only-one': onlyOne
74
+ }
75
+ )}
56
76
  style={cascading ? {
57
77
  transform: `perspective(100px) translateZ(-${2 * index}px) translateY(${35 * index}px)`,
58
78
  zIndex: depth
@@ -64,9 +84,20 @@ const Item = forwardRef((props: ItemProps, externalRef: any) => {
64
84
  >
65
85
 
66
86
  {/* Bootstrap toast */}
67
- <div className={`toast fade show ${schemeBody ? schemeBody : ''} ${theme ? `bg-${theme}` : ''}`} role="alert">
68
- {(title === '' || title === false) && (note === '' || note === false) ? null : <>
69
- <div className={`toast-header ${schemeHeader ? schemeHeader : ''}`}>
87
+ <div
88
+ className={combinedCls(
89
+ `toast fade show ${theme ? `bg-${theme}` : ''}`,
90
+ clsWrite(schemeBody, '')
91
+ )}
92
+ role="alert"
93
+ >
94
+ {hideTitle ? null : <>
95
+ <div
96
+ className={combinedCls(
97
+ 'toast-header',
98
+ clsWrite(schemeHeader, '')
99
+ )}
100
+ >
70
101
  <strong className="me-auto">{title === '' || title === false ? '' : <>{title}</>}</strong>
71
102
  <small className="text-muted">{note === '' || note === false ? '' : <>{note}</>}</small>
72
103
  {!lock ? <>{!closeDisabled ? <button
@@ -88,7 +119,7 @@ const Item = forwardRef((props: ItemProps, externalRef: any) => {
88
119
  <div className="toast-body">
89
120
  {message}
90
121
 
91
- {(title === '' || title === false) && (note === '' || note === false) ? <>
122
+ {hideTitle ? <>
92
123
  {!closeDisabled ? <button
93
124
  data-close="1"
94
125
  data-index={index}
@@ -105,7 +136,17 @@ const Item = forwardRef((props: ItemProps, externalRef: any) => {
105
136
 
106
137
 
107
138
  {/* PROGRESS */}
108
- <div ref={externalRef} data-progress-index={index} className={`progress active toast-progress ${autoCloseTime === false ? 'd-none' : ''}`} role="progressbar">
139
+ <div
140
+ ref={externalRef}
141
+ data-progress-index={index}
142
+ className={combinedCls(
143
+ 'progress active toast-progress',
144
+ {
145
+ 'd-none': autoCloseTime === false
146
+ }
147
+ )}
148
+ role="progressbar"
149
+ >
109
150
  <div className="progress-bar"></div>
110
151
  </div>
111
152
  {/* /PROGRESS */}