funda-ui 4.7.125 → 4.7.133

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.
@@ -446,7 +446,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
446
446
  */
447
447
 
448
448
  var useIsMobile = function useIsMobile() {
449
- var breakpoint = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 768;
449
+ var breakpoint = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 600;
450
450
  var _useState = (0, react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),
451
451
  _useState2 = _slicedToArray(_useState, 2),
452
452
  isMobile = _useState2[0],
@@ -460,7 +460,71 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
460
460
  setIsMounted(true);
461
461
  var handleResize = function handleResize() {
462
462
  if (window) {
463
- setIsMobile(window.innerWidth <= breakpoint);
463
+ var detectDeviceType = function detectDeviceType() {
464
+ // 1. First check if window and navigator are available (SSR compatibility)
465
+ if (typeof window === 'undefined' || !navigator) {
466
+ return 'desktop'; // Default to desktop
467
+ }
468
+
469
+ // 2. Get user agent string
470
+ var ua = navigator.userAgent.toLowerCase();
471
+
472
+ // 3. Get platform info
473
+ var platform = navigator.platform.toLowerCase();
474
+
475
+ // 4. Check screen characteristics using window.matchMedia
476
+ var isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
477
+ var isPortrait = window.matchMedia('(orientation: portrait)').matches;
478
+ var isLandscape = window.matchMedia('(orientation: landscape)').matches;
479
+
480
+ // 5. Get screen dimensions
481
+ var screenWidth = window.screen.width;
482
+ var screenHeight = window.screen.height;
483
+ var minScreenSize = Math.min(screenWidth, screenHeight);
484
+ var maxScreenSize = Math.max(screenWidth, screenHeight);
485
+
486
+ // Define device characteristics
487
+ var isTablet =
488
+ // Traditional UA detection
489
+ /ipad/.test(ua) || /android/.test(ua) && !/mobile/.test(ua) || /tablet/.test(ua) || /playbook/.test(ua) || /nexus (7|9|10)/.test(ua) || /sm-t/.test(ua) || /huawei(.*)mediapad/.test(ua) ||
490
+ // Special detection for iPad Pro and newer iPads
491
+ navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ||
492
+ // Screen size characteristics (tablets typically fall within this range)
493
+ minScreenSize >= breakpoint && maxScreenSize <= 1366 && isTouch ||
494
+ // Specific device detection
495
+ /kindle|silk|kftt|kfot|kfjwa|kfjwi|kfsowi|kfthwa|kfthwi|kfapwa|kfapwi/i.test(ua);
496
+ var isMobile = !isTablet && (
497
+ // Prevent tablets from being detected as phones
498
+ // Traditional mobile device detection
499
+ /iphone|ipod|android.*mobile|windows phone|mobi/.test(ua) ||
500
+ // Screen size characteristics (phones typically smaller than 600px)
501
+ minScreenSize < breakpoint && isTouch ||
502
+ // Additional mobile device detection
503
+ /blackberry|\bbb\d+|meego|webos|palm|phone|pocket|mobile|mini|iemobile/i.test(ua));
504
+
505
+ // 6. Comprehensive decision logic
506
+ if (isMobile) {
507
+ // Additional check for small tablets
508
+ if (maxScreenSize >= 1024 && isTouch) {
509
+ return 'tablet';
510
+ }
511
+ return 'mobile';
512
+ }
513
+ if (isTablet) {
514
+ // Additional check for touch-enabled laptops
515
+ if (maxScreenSize > 1366 && /windows/.test(ua)) {
516
+ return 'desktop';
517
+ }
518
+ return 'tablet';
519
+ }
520
+
521
+ // 7. Check for touch-enabled laptops
522
+ if (isTouch && /windows/.test(ua) && maxScreenSize > 1366) {
523
+ return 'desktop';
524
+ }
525
+ return 'desktop';
526
+ };
527
+ setIsMobile(detectDeviceType() === 'mobile');
464
528
  }
465
529
  };
466
530
 
@@ -127,7 +127,7 @@ const App = () => {
127
127
  */
128
128
 
129
129
  var useIsMobile = function useIsMobile() {
130
- var breakpoint = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 768;
130
+ var breakpoint = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 600;
131
131
  var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(false),
132
132
  _useState2 = _slicedToArray(_useState, 2),
133
133
  isMobile = _useState2[0],
@@ -141,7 +141,71 @@ var useIsMobile = function useIsMobile() {
141
141
  setIsMounted(true);
142
142
  var handleResize = function handleResize() {
143
143
  if (window) {
144
- setIsMobile(window.innerWidth <= breakpoint);
144
+ var detectDeviceType = function detectDeviceType() {
145
+ // 1. First check if window and navigator are available (SSR compatibility)
146
+ if (typeof window === 'undefined' || !navigator) {
147
+ return 'desktop'; // Default to desktop
148
+ }
149
+
150
+ // 2. Get user agent string
151
+ var ua = navigator.userAgent.toLowerCase();
152
+
153
+ // 3. Get platform info
154
+ var platform = navigator.platform.toLowerCase();
155
+
156
+ // 4. Check screen characteristics using window.matchMedia
157
+ var isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
158
+ var isPortrait = window.matchMedia('(orientation: portrait)').matches;
159
+ var isLandscape = window.matchMedia('(orientation: landscape)').matches;
160
+
161
+ // 5. Get screen dimensions
162
+ var screenWidth = window.screen.width;
163
+ var screenHeight = window.screen.height;
164
+ var minScreenSize = Math.min(screenWidth, screenHeight);
165
+ var maxScreenSize = Math.max(screenWidth, screenHeight);
166
+
167
+ // Define device characteristics
168
+ var isTablet =
169
+ // Traditional UA detection
170
+ /ipad/.test(ua) || /android/.test(ua) && !/mobile/.test(ua) || /tablet/.test(ua) || /playbook/.test(ua) || /nexus (7|9|10)/.test(ua) || /sm-t/.test(ua) || /huawei(.*)mediapad/.test(ua) ||
171
+ // Special detection for iPad Pro and newer iPads
172
+ navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1 ||
173
+ // Screen size characteristics (tablets typically fall within this range)
174
+ minScreenSize >= breakpoint && maxScreenSize <= 1366 && isTouch ||
175
+ // Specific device detection
176
+ /kindle|silk|kftt|kfot|kfjwa|kfjwi|kfsowi|kfthwa|kfthwi|kfapwa|kfapwi/i.test(ua);
177
+ var isMobile = !isTablet && (
178
+ // Prevent tablets from being detected as phones
179
+ // Traditional mobile device detection
180
+ /iphone|ipod|android.*mobile|windows phone|mobi/.test(ua) ||
181
+ // Screen size characteristics (phones typically smaller than 600px)
182
+ minScreenSize < breakpoint && isTouch ||
183
+ // Additional mobile device detection
184
+ /blackberry|\bbb\d+|meego|webos|palm|phone|pocket|mobile|mini|iemobile/i.test(ua));
185
+
186
+ // 6. Comprehensive decision logic
187
+ if (isMobile) {
188
+ // Additional check for small tablets
189
+ if (maxScreenSize >= 1024 && isTouch) {
190
+ return 'tablet';
191
+ }
192
+ return 'mobile';
193
+ }
194
+ if (isTablet) {
195
+ // Additional check for touch-enabled laptops
196
+ if (maxScreenSize > 1366 && /windows/.test(ua)) {
197
+ return 'desktop';
198
+ }
199
+ return 'tablet';
200
+ }
201
+
202
+ // 7. Check for touch-enabled laptops
203
+ if (isTouch && /windows/.test(ua) && maxScreenSize > 1366) {
204
+ return 'desktop';
205
+ }
206
+ return 'desktop';
207
+ };
208
+ setIsMobile(detectDeviceType() === 'mobile');
145
209
  }
146
210
  };
147
211
 
@@ -29,6 +29,7 @@ export const NumberInput: any;
29
29
  export const Pagination: any;
30
30
  export const Radio: any;
31
31
  export const RangeSlider: any;
32
+ export const Refresher: any;
32
33
  export const RootPortal: any;
33
34
  export const ScrollReveal: any;
34
35
  export const Scrollbar: any;
package/lib/cjs/index.js CHANGED
@@ -31,6 +31,7 @@ exports.NumberInput = _interopRequireDefault(require("./NumberInput")).default;
31
31
  exports.Pagination = _interopRequireDefault(require("./Pagination")).default;
32
32
  exports.Radio = _interopRequireDefault(require("./Radio")).default;
33
33
  exports.RangeSlider = _interopRequireDefault(require("./RangeSlider")).default;
34
+ exports.Refresher = _interopRequireDefault(require("./Refresher")).default;
34
35
  exports.RootPortal = _interopRequireDefault(require("./RootPortal")).default;
35
36
  exports.ScrollReveal = _interopRequireDefault(require("./ScrollReveal")).default;
36
37
  exports.Scrollbar = _interopRequireDefault(require("./Scrollbar")).default;
@@ -0,0 +1,121 @@
1
+ import React, { useState, useRef } from 'react';
2
+
3
+ import useIsMobile from 'funda-utils/dist/cjs/useIsMobile';
4
+
5
+
6
+ interface RefresherProps {
7
+ /** Pulling text */
8
+ pullingText?: React.ReactNode;
9
+ /** Text when reaching the threshold */
10
+ readyToRefreshText?: React.ReactNode;
11
+ /** Refreshing text */
12
+ refreshingText?: React.ReactNode;
13
+ /** The pull distance (px) that triggers the refresh */
14
+ threshold?: number;
15
+ /** The height of the trigger area */
16
+ triggerHeight?: number;
17
+ /** The styles of the trigger area */
18
+ triggerAreaStyle?: React.CSSProperties;
19
+ /** Pull-down is only allowed when the scroll bar of this element is at the top. You can only fire certain actions when the scrollbar is at the top by listening to the scroll event of a given DOM element and determining if its scrollTop is 0. */
20
+ scrollableElementClassName?: string;
21
+ /** Refresh action is async */
22
+ onRefresh: () => Promise<void>;
23
+ children: React.ReactNode;
24
+ }
25
+
26
+ const Refresher = (prpps: RefresherProps) => {
27
+ const {
28
+ onRefresh,
29
+ children,
30
+ pullingText = 'Pull down to refresh',
31
+ readyToRefreshText = 'Release to refresh',
32
+ refreshingText = 'Refreshing...',
33
+ threshold = 100,
34
+ triggerHeight = 50,
35
+ triggerAreaStyle,
36
+ scrollableElementClassName,
37
+ } = prpps;
38
+
39
+ const isMobile = useIsMobile();
40
+ const [pullDistance, setPullDistance] = useState<number>(0);
41
+ const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
42
+ const startY = useRef<number | null>(null);
43
+ const containerRef = useRef<HTMLDivElement>(null);
44
+
45
+ const handleTouchStart = (e: React.TouchEvent) => {
46
+ if (isRefreshing) return;
47
+ // Pull-down is only allowed when the scroll bar is at the top
48
+ const targetScrollableDiv = scrollableElementClassName === null || typeof scrollableElementClassName === 'undefined' ? containerRef.current : document.querySelector(scrollableElementClassName as string);
49
+
50
+
51
+ if (targetScrollableDiv && targetScrollableDiv.scrollTop > 0) return;
52
+ startY.current = e.touches[0].clientY;
53
+ };
54
+
55
+ const handleTouchMove = (e: React.TouchEvent) => {
56
+ if (startY.current === null || isRefreshing) return;
57
+ const distance = e.touches[0].clientY - startY.current;
58
+ if (distance > 0) {
59
+ setPullDistance(distance > threshold ? threshold : distance); // The maximum pull should not exceed "threshold" value
60
+ }
61
+ };
62
+
63
+ const handleTouchEnd = async () => {
64
+ if (isRefreshing) return;
65
+ if (pullDistance >= threshold) {
66
+ setIsRefreshing(true);
67
+ await onRefresh();
68
+ setIsRefreshing(false);
69
+ }
70
+ setPullDistance(0);
71
+ startY.current = null;
72
+ };
73
+
74
+ return (
75
+ <>
76
+ {!isMobile ? <React.Fragment>
77
+ {/** CONTENT */}
78
+ {children}
79
+ </React.Fragment> : <div
80
+ ref={containerRef}
81
+ style={{
82
+ overflow: 'hidden',
83
+ touchAction: 'pan-y',
84
+ marginTop: `-${triggerHeight}px`,
85
+ }}
86
+ onTouchStart={handleTouchStart}
87
+ onTouchMove={handleTouchMove}
88
+ onTouchEnd={handleTouchEnd}
89
+ >
90
+ <div
91
+ style={{
92
+ transform: `translateY(${isRefreshing ? triggerHeight : pullDistance}px)`,
93
+ transition: isRefreshing ? 'transform 0.2s' : 'transform 0.3s',
94
+ }}
95
+ >
96
+
97
+ {/** TRIGGER */}
98
+ <div style={{
99
+ ...triggerAreaStyle,
100
+ height: `${triggerHeight}px`,
101
+ textAlign: 'center',
102
+ lineHeight: '50px'
103
+ }}
104
+ >
105
+ {isRefreshing
106
+ ? refreshingText
107
+ : pullDistance >= threshold
108
+ ? readyToRefreshText
109
+ : pullingText}
110
+ </div>
111
+
112
+ {/** CONTENT */}
113
+ {children}
114
+ </div>
115
+ </div>
116
+ }
117
+ </>
118
+ );
119
+ };
120
+
121
+ export default Refresher;
@@ -24,9 +24,10 @@ const App = () => {
24
24
  import { useEffect, useState } from 'react';
25
25
 
26
26
 
27
- const useIsMobile = (breakpoint: number = 768): boolean => {
28
- const [isMobile, setIsMobile] = useState<boolean>(false);
29
- const [isMounted, setIsMounted] = useState<boolean>(false);
27
+ const useIsMobile = (breakpoint = 600) => {
28
+ const [isMobile, setIsMobile] = useState(false);
29
+ const [isMounted, setIsMounted] = useState(false);
30
+
30
31
 
31
32
  useEffect(() => {
32
33
  // Set the mounted state to true after the component has mounted
@@ -34,7 +35,92 @@ const useIsMobile = (breakpoint: number = 768): boolean => {
34
35
 
35
36
  const handleResize = () => {
36
37
  if (window) {
37
- setIsMobile(window.innerWidth <= breakpoint);
38
+
39
+
40
+ const detectDeviceType = () => {
41
+ // 1. First check if window and navigator are available (SSR compatibility)
42
+ if (typeof window === 'undefined' || !navigator) {
43
+ return 'desktop'; // Default to desktop
44
+ }
45
+
46
+ // 2. Get user agent string
47
+ const ua = navigator.userAgent.toLowerCase();
48
+
49
+ // 3. Get platform info
50
+ const platform = navigator.platform.toLowerCase();
51
+
52
+ // 4. Check screen characteristics using window.matchMedia
53
+ const isTouch = ('ontouchstart' in window) || navigator.maxTouchPoints > 0;
54
+
55
+ const isPortrait = window.matchMedia('(orientation: portrait)').matches;
56
+ const isLandscape = window.matchMedia('(orientation: landscape)').matches;
57
+
58
+ // 5. Get screen dimensions
59
+ const screenWidth = window.screen.width;
60
+ const screenHeight = window.screen.height;
61
+ const minScreenSize = Math.min(screenWidth, screenHeight);
62
+ const maxScreenSize = Math.max(screenWidth, screenHeight);
63
+
64
+ // Define device characteristics
65
+ const isTablet = (
66
+ // Traditional UA detection
67
+ /ipad/.test(ua) ||
68
+ (/android/.test(ua) && !/mobile/.test(ua)) ||
69
+ /tablet/.test(ua) ||
70
+ /playbook/.test(ua) ||
71
+ /nexus (7|9|10)/.test(ua) ||
72
+ /sm-t/.test(ua) ||
73
+ /huawei(.*)mediapad/.test(ua) ||
74
+
75
+ // Special detection for iPad Pro and newer iPads
76
+ (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) ||
77
+
78
+ // Screen size characteristics (tablets typically fall within this range)
79
+ (minScreenSize >= breakpoint && maxScreenSize <= 1366 && isTouch) ||
80
+
81
+ // Specific device detection
82
+ /kindle|silk|kftt|kfot|kfjwa|kfjwi|kfsowi|kfthwa|kfthwi|kfapwa|kfapwi/i.test(ua)
83
+ );
84
+
85
+ const isMobile = (
86
+ !isTablet && ( // Prevent tablets from being detected as phones
87
+ // Traditional mobile device detection
88
+ /iphone|ipod|android.*mobile|windows phone|mobi/.test(ua) ||
89
+
90
+ // Screen size characteristics (phones typically smaller than 600px)
91
+ (minScreenSize < breakpoint && isTouch) ||
92
+
93
+ // Additional mobile device detection
94
+ /blackberry|\bbb\d+|meego|webos|palm|phone|pocket|mobile|mini|iemobile/i.test(ua)
95
+ )
96
+ );
97
+
98
+ // 6. Comprehensive decision logic
99
+ if (isMobile) {
100
+ // Additional check for small tablets
101
+ if (maxScreenSize >= 1024 && isTouch) {
102
+ return 'tablet';
103
+ }
104
+ return 'mobile';
105
+ }
106
+
107
+ if (isTablet) {
108
+ // Additional check for touch-enabled laptops
109
+ if (maxScreenSize > 1366 && /windows/.test(ua)) {
110
+ return 'desktop';
111
+ }
112
+ return 'tablet';
113
+ }
114
+
115
+ // 7. Check for touch-enabled laptops
116
+ if (isTouch && /windows/.test(ua) && maxScreenSize > 1366) {
117
+ return 'desktop';
118
+ }
119
+
120
+ return 'desktop';
121
+ };
122
+
123
+ setIsMobile(detectDeviceType() === 'mobile');
38
124
  }
39
125
  };
40
126
 
package/lib/esm/index.js CHANGED
@@ -27,6 +27,7 @@ export { default as NumberInput } from './NumberInput';
27
27
  export { default as Pagination } from './Pagination';
28
28
  export { default as Radio } from './Radio';
29
29
  export { default as RangeSlider } from './RangeSlider';
30
+ export { default as Refresher } from './Refresher';
30
31
  export { default as RootPortal } from './RootPortal';
31
32
  export { default as ScrollReveal } from './ScrollReveal';
32
33
  export { default as Scrollbar } from './Scrollbar';
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.125",
5
+ "version": "4.7.133",
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",