funda-ui 4.7.103 → 4.7.107

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 (55) hide show
  1. package/Chatbox/index.js +8 -3
  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 +7 -8
  11. package/Stepper/index.d.ts +1 -1
  12. package/Stepper/index.js +7 -1
  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 +8 -3
  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 +7 -1
  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 +7 -8
  34. package/lib/css/Toast/index.css +23 -75
  35. package/lib/esm/Chatbox/index.tsx +2 -2
  36. package/lib/esm/Checkbox/index.tsx +12 -1
  37. package/lib/esm/Date/index.tsx +8 -1
  38. package/lib/esm/Input/index.tsx +8 -1
  39. package/lib/esm/LiveSearch/index.tsx +7 -0
  40. package/lib/esm/MultipleCheckboxes/index.tsx +19 -1
  41. package/lib/esm/NumberInput/index.tsx +8 -1
  42. package/lib/esm/Radio/index.tsx +17 -1
  43. package/lib/esm/Stepper/index.scss +7 -8
  44. package/lib/esm/Stepper/index.tsx +2 -2
  45. package/lib/esm/TagInput/index.tsx +8 -1
  46. package/lib/esm/Textarea/index.tsx +8 -1
  47. package/lib/esm/Toast/Item.tsx +52 -11
  48. package/lib/esm/Toast/Toast.tsx +391 -0
  49. package/lib/esm/Toast/ToastContext.tsx +104 -0
  50. package/lib/esm/Toast/__toast.vanilla.js +422 -0
  51. package/lib/esm/Toast/index.scss +24 -96
  52. package/lib/esm/Toast/index.tsx +3 -374
  53. package/lib/esm/Toast/types.ts +60 -0
  54. package/lib/esm/Toast/useToast.tsx +72 -0
  55. package/package.json +1 -1
@@ -1,374 +1,3 @@
1
- import React, { useEffect, useState, useRef, useCallback } from 'react';
2
-
3
- import useComId from 'funda-utils/dist/cjs/useComId';
4
-
5
- import RootPortal from 'funda-root-portal';
6
-
7
-
8
- import Item from './Item';
9
-
10
-
11
-
12
- export type ToastProps = {
13
- /** The class name of the toast wrapper. */
14
- wrapperClassName?: string;
15
- /** Specify data of toasts as a JSON string format. */
16
- data: any[any];
17
- /** Automatically hide multiple items */
18
- autoHideMultiple?: boolean;
19
- /** The direction of the toast. */
20
- direction?: string;
21
- /** Set an automatic closing time, multiple items will be accumulated in order.
22
- * Amount of time measured in milliseconds. If false or without this attribute, Auto-Close will be disabled.
23
- */
24
- autoCloseTime?: boolean | number;
25
- /** You can not close pop-win when it is enabled */
26
- lock?: boolean;
27
- /** Whether to use cascading styles */
28
- cascading?: boolean;
29
- /** Self-defined class name for body*/
30
- schemeBody?: string;
31
- /** Self-defined class name for header */
32
- schemeHeader?: string;
33
- /** Set the color of the close button */
34
- closeBtnColor?: string;
35
- /** Disable the close button. */
36
- closeDisabled?: boolean;
37
- /** */
38
- async?: boolean;
39
- /** -- */
40
- id?: string;
41
- onClose?: (e: HTMLDivElement, currentIndex: number, data: HTMLDivElement[]) => void;
42
- };
43
-
44
-
45
- const Toast = (props: ToastProps) => {
46
- const {
47
- wrapperClassName,
48
- async,
49
- autoHideMultiple,
50
- direction,
51
- autoCloseTime,
52
- lock,
53
- cascading,
54
- data,
55
- schemeBody,
56
- schemeHeader,
57
- closeBtnColor,
58
- closeDisabled,
59
- id,
60
- onClose
61
- } = props;
62
-
63
-
64
- const ANIM_SPEED = 300;
65
- const DEFAULT_AUTO_CLOSE_TIME = 3000;
66
- const uniqueID = useComId();
67
- const idRes = id || uniqueID;
68
- const rootRef = useRef<any>(null);
69
- const depth: number = autoHideMultiple ? data.slice(-2).length + 1 : data.length + 1;
70
- const cascadingEnabled = typeof cascading === 'undefined' ? true : cascading;
71
-
72
-
73
- // force display
74
- const [initPopRoot, setInitPopRoot] = useState<boolean>(false);
75
-
76
- // auto close
77
- const AUTO_CLOSE_TIME: any = typeof (autoCloseTime) === 'undefined' || autoCloseTime === false ? false : autoCloseTime;
78
-
79
- // progress animation
80
- const PROGRESS_TRANSITION_TIME: any = typeof (autoCloseTime) === 'undefined' || autoCloseTime === false ? DEFAULT_AUTO_CLOSE_TIME : autoCloseTime;
81
-
82
-
83
- const progressPausedRef = useRef<any[]>(data.map((v: any) => false));
84
- const progressObjRef = useRef<any[]>([]);
85
- const progressIntervalRef = useRef<any[]>(data.map((v: any) => null));
86
- const startProgressTimer = useCallback((el: any, i: number) => {
87
-
88
- // init progress
89
- let progressCurrentChunk = 100 / (PROGRESS_TRANSITION_TIME / 100);
90
- el.firstChild.style.width = 100 + '%';
91
- el.firstChild.ariaValueNow = 100;
92
-
93
-
94
- // animation
95
- progressIntervalRef.current[i] = setInterval(() => {
96
- // console.log('toast setInterval');
97
-
98
- if (!progressPausedRef.current[i]) {
99
-
100
- const progPercent = 100 - progressCurrentChunk;
101
-
102
-
103
- el.firstChild.style.width = progPercent + '%';
104
- el.firstChild.ariaValueNow = progPercent;
105
- progressCurrentChunk++;
106
-
107
-
108
- //
109
- if (progPercent === 0 || progPercent < 1) { // may be 0.xxx
110
- el.classList.add('complete');
111
-
112
- // stop current animation
113
- stopProgressTimer(i);
114
-
115
- // hide toast item
116
- const currentItem = el.closest('.toast-container');
117
- handleClose(null, i as never, currentItem as never);
118
- }
119
- }
120
-
121
- }, PROGRESS_TRANSITION_TIME / 100);
122
-
123
-
124
- }, []);
125
-
126
-
127
-
128
- const clearAllProgressTimer = useCallback((curIndex: number | undefined = undefined) => {
129
- if (typeof curIndex === 'undefined') {
130
- data.forEach((item: any, i: number) => {
131
- clearInterval(progressIntervalRef.current[i]);
132
- progressIntervalRef.current[i] = null;
133
- });
134
- } else {
135
- data.forEach((item: any, i: number) => {
136
- if (i === curIndex) {
137
- clearInterval(progressIntervalRef.current[i]);
138
- progressIntervalRef.current[i] = null;
139
- }
140
- });
141
- }
142
-
143
- }, [data]);
144
-
145
-
146
-
147
- const stopProgressTimer = useCallback((index: number) => {
148
- clearInterval(progressIntervalRef.current[index]);
149
- progressIntervalRef.current[index] = null;
150
-
151
- }, [data]);
152
-
153
-
154
- function progressAnimBegin() {
155
- data.forEach((item: any, i: number) => {
156
- const el = progressObjRef.current[i];
157
- if (el !== null && typeof el !== 'undefined') startProgressTimer(el, i);
158
- });
159
- }
160
-
161
-
162
- function handleProgressPaused(e: any) {
163
- const _currentIndex = e.currentTarget.dataset.index;
164
- progressPausedRef.current[_currentIndex] = true;
165
- }
166
-
167
- function handleProgressStart(e: any) {
168
- const _currentIndex = e.currentTarget.dataset.index;
169
- progressPausedRef.current[_currentIndex] = false;
170
- }
171
-
172
- //
173
- function init() {
174
- if (rootRef.current === null) return;
175
-
176
- const $toast = rootRef.current;
177
-
178
- // Automatically hide multiple items
179
- // It creates a transition animation effect with multiple records and only one displayed.
180
- //------------------------------------------
181
- if (autoHideMultiple) {
182
-
183
- const _list: HTMLDivElement[] = [].slice.call($toast.querySelectorAll('.toast-container'));
184
-
185
-
186
- if (_list.length === 2) {
187
- _list.forEach((node: any, i: number) => {
188
- node.classList.remove('auto-anim-switch', 'auto-anim-switch--initfirst', 'auto-anim-switch--first');
189
-
190
- if (i !== _list.length - 1) {
191
- node.classList.add('auto-anim-switch--initfirst'); // top element of source code
192
- } else {
193
- node.classList.add('auto-anim-switch--initfirst'); // bottom element of source code
194
- }
195
-
196
- });
197
-
198
-
199
- setTimeout(() => {
200
- _list.forEach((node: any, i: number) => {
201
- if (i !== _list.length - 1) {
202
- node.classList.add('auto-anim-switch');
203
- } else {
204
- node.classList.add('auto-anim-switch--initfirst', 'auto-anim-switch--first');
205
- }
206
- });
207
- }, ANIM_SPEED/2);
208
- } else {
209
-
210
- _list.forEach((node: any, i: number) => {
211
- if (i !== _list.length - 1) {
212
- node.classList.add('auto-anim-switch');
213
- } else {
214
- node.classList.add('auto-anim-switch--initfirst', 'auto-anim-switch--first');
215
- }
216
- });
217
- }
218
-
219
-
220
- }
221
-
222
-
223
- // Initialize data
224
- //--------------
225
- if ( $toast.dataset.async == 'true' ) {
226
- const _list: HTMLDivElement[] = [].slice.call($toast.querySelectorAll('.toast-container'));
227
- _list.forEach((node: any, i: number) => {
228
- node.classList.remove('hide-end');
229
- // rearrange
230
- if (cascadingEnabled) node.style.transform = `perspective(100px) translateZ(-${2 * i}px) translateY(${35 * i}px)`;
231
-
232
- });
233
-
234
- }
235
-
236
- }
237
-
238
-
239
- function autoClose() {
240
-
241
- // Auto hide
242
- //--------------
243
- if (AUTO_CLOSE_TIME !== false) {
244
-
245
- // start animation
246
- progressAnimBegin();
247
- }
248
-
249
- }
250
-
251
- function handleClose(e: any, index: number, currentItem: HTMLDivElement) {
252
- if (typeof e !== 'undefined' && e !== null) e.preventDefault();
253
- if (rootRef.current === null) return;
254
-
255
- const curIndex = Number(index);
256
-
257
- const _list: HTMLDivElement[] = [].slice.call(rootRef.current.querySelectorAll('.toast-container'));
258
- currentItem.classList.add('hide-start');
259
-
260
- //Let the removed animation show
261
- setTimeout(() => {
262
-
263
- _list.forEach((node: any, i: number) => {
264
- node.classList.remove('hide-start');
265
- });
266
-
267
- // remove current
268
- currentItem.classList.add('hide-end');
269
-
270
- // rearrange
271
- if (cascadingEnabled) {
272
- _list.filter((node: any) => !node.classList.contains('hide-end')).forEach((node: any, k: number) => {
273
- node.style.transform = `perspective(100px) translateZ(-${2 * k}px) translateY(${35 * k}px)`;
274
- });
275
- }
276
-
277
- // stop all animations or current animation
278
- if (_list.length === 1 || autoHideMultiple) {
279
- clearAllProgressTimer();
280
- } else {
281
- clearAllProgressTimer(curIndex);
282
- }
283
-
284
-
285
- //
286
- onClose?.(rootRef.current, curIndex, _list.filter((node: HTMLDivElement) => !node.classList.contains('hide-end') ));
287
-
288
- }, ANIM_SPEED);
289
-
290
-
291
- }
292
-
293
-
294
- useEffect(() => {
295
-
296
- if (initPopRoot) {
297
-
298
- // Initialize Toast Item
299
- //------------------------------------------
300
- init();
301
-
302
-
303
- // Initialize Progress
304
- //------------------------------------------
305
- autoClose();
306
-
307
-
308
- // Remove the global list of events, especially as scroll and interval.
309
- //--------------
310
- return () => {
311
-
312
- // Stop all animations
313
- clearAllProgressTimer();
314
-
315
- }
316
- }
317
-
318
-
319
- }, [data, initPopRoot]);
320
-
321
-
322
- useEffect(() => {
323
- setInitPopRoot(true);
324
- }, []);
325
-
326
-
327
-
328
- return (
329
- <>
330
- <RootPortal show={initPopRoot} containerClassName="Toast">
331
-
332
- <div
333
- id={`toasts__wrapper-${idRes}`}
334
- data-async={async ? async : false}
335
- className={`toasts__wrapper toasts__wrapper--${direction ? direction : 'bottom-center'} ${cascadingEnabled ? 'toasts__wrapper--cascading' : ''} ${wrapperClassName || ''}`}
336
- ref={rootRef}
337
- >
338
- <div className="toasts">
339
- {(autoHideMultiple ? data.slice(-2) : data).map((item: any, i: number) => {
340
- return <Item
341
- ref={el => progressObjRef.current[i] = el}
342
- onlyOne={data.length === 1 ? true : false}
343
- depth={depth - i}
344
- key={i}
345
- index={i}
346
- title={item.title}
347
- note={item.note}
348
- theme={item.theme}
349
- lock={lock}
350
- cascading={cascadingEnabled}
351
- schemeBody={schemeBody}
352
- schemeHeader={schemeHeader}
353
- closeBtnColor={closeBtnColor}
354
- closeDisabled={closeDisabled}
355
- message={item.message}
356
- autoCloseTime={AUTO_CLOSE_TIME}
357
- evStart={handleProgressStart}
358
- evPause={handleProgressPaused}
359
- evClose={handleClose}
360
- />
361
-
362
- })}
363
- </div>
364
-
365
- </div>
366
- </RootPortal>
367
-
368
-
369
- </>
370
- )
371
- };
372
-
373
-
374
- export default Toast;
1
+ export { useToast } from './useToast';
2
+ export { ToastProvider } from './ToastContext';
3
+ export type { ToastOptions } from './types';
@@ -0,0 +1,60 @@
1
+ export interface ToastOptions {
2
+ /** +++++++++++++++++++++++++++++++++++++++++++++++++ */
3
+ /** IDENTIFIER */
4
+ /** +++++++++++++++++++++++++++++++++++++++++++++++++ */
5
+ actionId?: string | number | null | undefined;
6
+
7
+ /** +++++++++++++++++++++++++++++++++++++++++++++++++ */
8
+ /** GENERIC */
9
+ /** +++++++++++++++++++++++++++++++++++++++++++++++++ */
10
+ wrapperClassName?: string;
11
+ /** Automatically hide multiple items */
12
+ onlyShowOne?: boolean;
13
+ /** Allow data to be reversed */
14
+ reverseDisplay?: boolean;
15
+ /** The direction of the toast. */
16
+ direction?: 'bottom-left' | 'bottom-center' | 'bottom-right' | 'top-left' | 'top-center' | 'top-right' | 'vertical-center';
17
+ /** Set an automatic closing time, multiple items will be accumulated in order.
18
+ * Amount of time measured in milliseconds. If false or without this attribute, Auto-Close will be disabled.
19
+ */
20
+ autoCloseTime?: boolean | number;
21
+ /** You can not close pop-win when it is enabled */
22
+ lock?: boolean;
23
+ /** Whether to use cascading styles */
24
+ cascading?: boolean;
25
+ /** Self-defined class name for body*/
26
+ schemeBody?: string;
27
+ /** Self-defined class name for header */
28
+ schemeHeader?: string;
29
+ /** Set the color of the close button */
30
+ closeBtnColor?: string;
31
+ /** Disable the close button. */
32
+ closeDisabled?: boolean;
33
+ /** -- */
34
+ onClose?: (e: HTMLDivElement, currentIndex: number, displayedElements: HTMLDivElement[]) => void;
35
+
36
+
37
+ /** +++++++++++++++++++++++++++++++++++++++++++++++++ */
38
+ /** ITEM */
39
+ /** +++++++++++++++++++++++++++++++++++++++++++++++++ */
40
+ title?: string;
41
+ note?: string;
42
+ theme?: 'primary' | 'secondary' | 'success' | 'info' | 'warning' | 'danger' | 'light' | 'dark' | undefined;
43
+ message?: React.ReactNode;
44
+
45
+
46
+ /** +++++++++++++++++++++++++++++++++++++++++++++++++ */
47
+ /** AUTO */
48
+ /** +++++++++++++++++++++++++++++++++++++++++++++++++ */
49
+ id?: string;
50
+ }
51
+
52
+
53
+ // Configure the interface globally
54
+ export interface ToastGlobalConfig {
55
+ defaultWrapperClassName?: string;
56
+ defaultOnlyShowOne?: boolean;
57
+ defaultDirection?: ToastOptions['direction'];
58
+ defaultCascading?: boolean;
59
+ defaultReverseDisplay?: boolean;
60
+ }
@@ -0,0 +1,72 @@
1
+ import { useCallback } from 'react';
2
+ import { useToastContext } from './ToastContext';
3
+ import type { ToastOptions, ToastGlobalConfig } from './types';
4
+
5
+
6
+ export interface ToastItem extends ToastOptions {
7
+ id: string;
8
+ }
9
+
10
+
11
+ export const useToast = () => {
12
+ const { state, dispatch } = useToastContext();
13
+
14
+ const show = useCallback((options: ToastOptions): ToastItem => {
15
+ const id = Math.random().toString(36).substring(2, 9);
16
+ const toast: ToastItem = {
17
+ id,
18
+
19
+ // Configure the interface globally, but overrides are allowed to be overridden by the instance configuration.
20
+ wrapperClassName: state.config.defaultWrapperClassName,
21
+ direction: state.config.defaultDirection,
22
+ onlyShowOne: state.config.defaultOnlyShowOne,
23
+ cascading: state.config.defaultCascading,
24
+ reverseDisplay: state.config.defaultReverseDisplay,
25
+
26
+
27
+ ...options,
28
+ };
29
+ dispatch({
30
+ type: 'ADD_TOAST',
31
+ payload: toast,
32
+ });
33
+ return toast; // Returns the full toast object
34
+ }, [dispatch, state.config]);
35
+
36
+ const updateConfig = useCallback((config: Partial<ToastGlobalConfig>) => {
37
+ dispatch({
38
+ type: 'UPDATE_CONFIG',
39
+ payload: config
40
+ });
41
+ }, [dispatch]);
42
+
43
+
44
+ const close = useCallback((id: string) => {
45
+ dispatch({ type: 'REMOVE_TOAST', payload: id });
46
+ }, [dispatch]);
47
+
48
+ const closeByIndex = useCallback((index: number) => {
49
+ const toast = state.toasts[index];
50
+ if (toast) {
51
+ dispatch({ type: 'REMOVE_TOAST', payload: toast.id });
52
+ }
53
+ }, [state.toasts, dispatch]);
54
+
55
+ const closeAll = useCallback(() => {
56
+ dispatch({ type: 'REMOVE_ALL' });
57
+ }, [dispatch]);
58
+
59
+ // Get all current toasts
60
+ const getToasts = useCallback((): ToastItem[] => {
61
+ return state.toasts;
62
+ }, [state.toasts]);
63
+
64
+ return {
65
+ show,
66
+ close,
67
+ closeByIndex,
68
+ closeAll,
69
+ getToasts,
70
+ updateConfig
71
+ };
72
+ };
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.103",
5
+ "version": "4.7.107",
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",