funda-ui 4.7.111 → 4.7.115

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.
@@ -0,0 +1,403 @@
1
+ /**
2
+ * History Tracker
3
+ *
4
+ * @usage:
5
+
6
+ const App = () => {
7
+ const {
8
+ history,
9
+ forwardHistory,
10
+ currentUrl,
11
+ firstUrl,
12
+ clearHistory,
13
+ goBack
14
+ } = useHistoryTracker({
15
+ onChange: ({
16
+ isReady,
17
+ history,
18
+ forwardHistory,
19
+ currentUrl,
20
+ firstUrl,
21
+ canGoBack,
22
+ canGoForward
23
+ } : {
24
+ isReady: boolean;
25
+ history: string[];
26
+ forwardHistory: string[];
27
+ currentUrl: string;
28
+ firstUrl: string;
29
+ canGoBack: boolean;
30
+ canGoForward: boolean;
31
+ }) => {
32
+ console.log('--> onChange: ',
33
+ isReady,
34
+ history,
35
+ forwardHistory,
36
+ currentUrl,
37
+ firstUrl,
38
+ canGoBack,
39
+ canGoForward
40
+ );
41
+ }
42
+ });
43
+
44
+ return (
45
+ <div>
46
+
47
+ <div>
48
+ <h3>First URL:</h3>
49
+ <p>{firstUrl}</p>
50
+ </div>
51
+
52
+ <div>
53
+ <h3>Current URL:</h3>
54
+ <p>{currentUrl}</p>
55
+ </div>
56
+
57
+ <div>
58
+ <h3>History ({history.length}):</h3>
59
+ <ul>
60
+ {history.map((url, index) => (
61
+ <li key={index}>{url}</li>
62
+ ))}
63
+ </ul>
64
+ </div>
65
+
66
+ <div>
67
+ <h3>Forward History ({forwardHistory.length}):</h3>
68
+ <ul>
69
+ {forwardHistory.map((url, index) => (
70
+ <li key={index}>{url}</li>
71
+ ))}
72
+ </ul>
73
+ </div>
74
+
75
+
76
+
77
+ <button onClick={clearHistory}>
78
+ Clear History
79
+ </button>
80
+
81
+ <button onClick={async () => {
82
+ try {
83
+ const {
84
+ isReady,
85
+ history,
86
+ forwardHistory,
87
+ canGoBack,
88
+ canGoForward
89
+ } : {
90
+ isReady: boolean;
91
+ history: string[];
92
+ forwardHistory: string[];
93
+ canGoBack: boolean;
94
+ canGoForward: boolean;
95
+ } = await goBack();
96
+
97
+ console.log('--> goBack: ',
98
+ isReady,
99
+ history,
100
+ forwardHistory,
101
+ currentUrl,
102
+ firstUrl,
103
+ canGoBack,
104
+ canGoForward
105
+ );
106
+ } catch (error) {
107
+ console.error('Navigation failed', error);
108
+ }
109
+
110
+ }}>
111
+ Back
112
+ </button>
113
+
114
+ </div>
115
+ );
116
+ };
117
+
118
+ */
119
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
120
+
121
+ export type UseHistoryTrackerChangeFnType = (args: {
122
+ isReady: boolean;
123
+ history: string[];
124
+ forwardHistory: string[],
125
+ currentUrl: string;
126
+ firstUrl: string;
127
+ canGoBack: boolean;
128
+ canGoForward: boolean;
129
+ }) => void;
130
+
131
+
132
+ export type UseHistoryTrackerProps = {
133
+ onChange?: UseHistoryTrackerChangeFnType | null;
134
+ };
135
+
136
+ // Create a secure version of useLayoutEffect that is downgraded to useEffect when SSR
137
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
138
+
139
+ const useHistoryTracker = (props: UseHistoryTrackerProps) => {
140
+ const {
141
+ onChange
142
+ } = props;
143
+
144
+ const [isReady, setIsReady] = useState<boolean>(false);
145
+ const historyRef = useRef<string[]>([]);
146
+ const forwardHistoryRef = useRef<string[]>([]);
147
+ const firstUrlRef = useRef<string>('');
148
+ const [currentUrl, setCurrentUrl] = useState<string>('');
149
+
150
+ const initialize = useCallback(() => {
151
+ if (typeof window === 'undefined') return;
152
+
153
+ const currentLocation = window.location.href as string;
154
+
155
+ // If the history is empty, set the first record
156
+ if (historyRef.current.length === 0) {
157
+ firstUrlRef.current = currentLocation;
158
+ historyRef.current = [currentLocation];
159
+ setCurrentUrl(currentLocation);
160
+
161
+ onChange?.({
162
+ isReady: false,
163
+ history: [currentLocation],
164
+ forwardHistory: [],
165
+ currentUrl: currentLocation,
166
+ firstUrl: currentLocation,
167
+ canGoBack: false,
168
+ canGoForward: false
169
+ });
170
+
171
+ }
172
+
173
+ setIsReady(true);
174
+ }, []);
175
+
176
+ useIsomorphicLayoutEffect(() => {
177
+ initialize();
178
+ }, [initialize]);
179
+
180
+
181
+ const clearHistory = useCallback(() => {
182
+ if (typeof window === 'undefined') return;
183
+
184
+ historyRef.current = [];
185
+ forwardHistoryRef.current = [];
186
+ firstUrlRef.current = '';
187
+ setCurrentUrl('');
188
+
189
+ onChange?.({
190
+ isReady: true,
191
+ history: [],
192
+ forwardHistory: [],
193
+ currentUrl: '',
194
+ firstUrl: '',
195
+ canGoBack: false,
196
+ canGoForward: false
197
+
198
+ });
199
+ }, [onChange]); // only "onChange"
200
+
201
+ const goToHistory = useCallback((index: number) => {
202
+ if (typeof window === 'undefined') return;
203
+ if (index < 0 || index >= historyRef.current.length) return;
204
+
205
+ const targetUrl = historyRef.current[index];
206
+ if (targetUrl && targetUrl !== window.location.href) {
207
+ window.location.href = targetUrl;
208
+ }
209
+ }, []);
210
+
211
+ const goBack = useCallback(() => {
212
+ if (typeof window === 'undefined') return Promise.reject('Window is undefined');
213
+ if (historyRef.current.length <= 1) return Promise.reject('Cannot go back');
214
+
215
+ return new Promise((resolve) => {
216
+ // Moves the current URL into the forward history
217
+ const removedUrl = historyRef.current.pop() as string;
218
+ forwardHistoryRef.current.push(removedUrl);
219
+
220
+ const newCurrentUrl = historyRef.current[historyRef.current.length - 1];
221
+ setCurrentUrl(newCurrentUrl);
222
+
223
+ // Create initial data object
224
+ const data = {
225
+ isReady: true,
226
+ history: [...historyRef.current],
227
+ forwardHistory: [...forwardHistoryRef.current],
228
+ currentUrl: newCurrentUrl,
229
+ firstUrl: firstUrlRef.current,
230
+ canGoBack: canGoBack(),
231
+ canGoForward: canGoForward()
232
+ };
233
+
234
+ // Notify about the history change
235
+ onChange?.(data);
236
+
237
+ // Create one-time listener for popstate
238
+ const handlePopState = () => {
239
+ // Remove the listener after it's called
240
+ window.removeEventListener('popstate', handlePopState);
241
+
242
+ // Get the final data after URL has changed
243
+ const finalData = {
244
+ isReady: true,
245
+ history: [...historyRef.current],
246
+ forwardHistory: [...forwardHistoryRef.current],
247
+ currentUrl: window.location.href,
248
+ firstUrl: firstUrlRef.current,
249
+ canGoBack: canGoBack(),
250
+ canGoForward: canGoForward()
251
+ };
252
+
253
+ resolve(finalData);
254
+ };
255
+
256
+ // Add the listener
257
+ window.addEventListener('popstate', handlePopState);
258
+
259
+ // Trigger the navigation
260
+ window.history.go(-1);
261
+ });
262
+ }, [onChange]);
263
+
264
+ const goForward = useCallback(() => {
265
+ if (typeof window === 'undefined') return Promise.reject('Window is undefined');
266
+ if (forwardHistoryRef.current.length === 0) return Promise.reject('Cannot go forward');
267
+
268
+ return new Promise((resolve) => {
269
+ // Take the URL from the forward history and add it to the main history
270
+ const nextUrl = forwardHistoryRef.current.pop() as string;
271
+ historyRef.current.push(nextUrl);
272
+ setCurrentUrl(nextUrl);
273
+
274
+ // Create initial data object
275
+ const data = {
276
+ isReady: true,
277
+ history: [...historyRef.current],
278
+ forwardHistory: [...forwardHistoryRef.current],
279
+ currentUrl: nextUrl,
280
+ firstUrl: firstUrlRef.current,
281
+ canGoBack: canGoBack(),
282
+ canGoForward: canGoForward()
283
+ };
284
+
285
+ onChange?.(data);
286
+
287
+ // Create one-time listener for popstate
288
+ const handlePopState = () => {
289
+ // Remove the listener after it's called
290
+ window.removeEventListener('popstate', handlePopState);
291
+
292
+ // Get the final data after URL has changed
293
+ const finalData = {
294
+ isReady: true,
295
+ history: [...historyRef.current],
296
+ forwardHistory: [...forwardHistoryRef.current],
297
+ currentUrl: window.location.href,
298
+ firstUrl: firstUrlRef.current,
299
+ canGoBack: canGoBack(),
300
+ canGoForward: canGoForward()
301
+ };
302
+
303
+ resolve(finalData);
304
+ };
305
+
306
+ // Add the listener
307
+ window.addEventListener('popstate', handlePopState);
308
+
309
+ // Trigger the navigation
310
+ window.history.go(1);
311
+ });
312
+ }, [onChange]);
313
+
314
+ const canGoBack = useCallback(() => {
315
+ return historyRef.current.length > 1;
316
+ }, []);
317
+
318
+ const canGoForward = useCallback(() => {
319
+ return forwardHistoryRef.current.length > 0;
320
+ }, []);
321
+
322
+
323
+ const handleUrlChange = useCallback(() => {
324
+ if (typeof window === 'undefined') return;
325
+
326
+ const newUrl = window.location.href;
327
+
328
+ // If the history is empty, set to the first URL
329
+ if (historyRef.current.length === 0) {
330
+ firstUrlRef.current = newUrl;
331
+ }
332
+
333
+ // Avoid recording the same URL
334
+ if (historyRef.current[historyRef.current.length - 1] !== newUrl) {
335
+ historyRef.current.push(newUrl);
336
+
337
+ // Clear the advance history, as new navigation invalidates the advance history
338
+ forwardHistoryRef.current = [];
339
+ setCurrentUrl(newUrl);
340
+
341
+ onChange?.({
342
+ isReady: true,
343
+ history: [...historyRef.current],
344
+ forwardHistory: [...forwardHistoryRef.current],
345
+ currentUrl: newUrl,
346
+ firstUrl: firstUrlRef.current || newUrl, // Make sure there is always a value
347
+ canGoBack: canGoBack(),
348
+ canGoForward: canGoForward()
349
+ });
350
+ }
351
+ }, [onChange]); // only "onChange"
352
+
353
+
354
+ useEffect(() => {
355
+ if (typeof window === 'undefined') return;
356
+
357
+ // Listen for popstate events (browser forward/back)
358
+ window.addEventListener('popstate', handleUrlChange);
359
+
360
+ // Listen for hashchange events
361
+ window.addEventListener('hashchange', handleUrlChange);
362
+
363
+ // Listen for DOM and property changes
364
+ const observer = new MutationObserver((mutations) => {
365
+ mutations.forEach((mutation) => {
366
+ if (mutation.type === 'childList' || mutation.type === 'attributes') {
367
+ handleUrlChange();
368
+ }
369
+ });
370
+ });
371
+
372
+
373
+ observer.observe(document.body, {
374
+ childList: true, // monitor the addition and deletion of child nodes
375
+ subtree: true, // monitor all descendant nodes
376
+ attributes: true, // monitor attribute changes
377
+ attributeFilter: ['href'] // only monitor changes in the href attribute
378
+ });
379
+
380
+ return () => {
381
+ window.removeEventListener('popstate', handleUrlChange);
382
+ window.removeEventListener('hashchange', handleUrlChange);
383
+ observer.disconnect();
384
+ };
385
+ }, [handleUrlChange]);
386
+
387
+
388
+ return {
389
+ isReady,
390
+ history: historyRef.current,
391
+ forwardHistory: forwardHistoryRef.current,
392
+ currentUrl,
393
+ firstUrl: firstUrlRef.current,
394
+ clearHistory,
395
+ goToHistory,
396
+ goBack,
397
+ goForward,
398
+ canGoBack,
399
+ canGoForward
400
+ };
401
+ };
402
+
403
+ export default useHistoryTracker;
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.111",
5
+ "version": "4.7.115",
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",
@@ -1,2 +0,0 @@
1
- declare const useGlobalUrlListener: () => string;
2
- export default useGlobalUrlListener;
@@ -1,157 +0,0 @@
1
- (function webpackUniversalModuleDefinition(root, factory) {
2
- if(typeof exports === 'object' && typeof module === 'object')
3
- module.exports = factory(require("react"));
4
- else if(typeof define === 'function' && define.amd)
5
- define(["react"], factory);
6
- else if(typeof exports === 'object')
7
- exports["RPB"] = factory(require("react"));
8
- else
9
- root["RPB"] = factory(root["React"]);
10
- })(this, (__WEBPACK_EXTERNAL_MODULE__787__) => {
11
- return /******/ (() => { // webpackBootstrap
12
- /******/ "use strict";
13
- /******/ var __webpack_modules__ = ({
14
-
15
- /***/ 787:
16
- /***/ ((module) => {
17
-
18
- module.exports = __WEBPACK_EXTERNAL_MODULE__787__;
19
-
20
- /***/ })
21
-
22
- /******/ });
23
- /************************************************************************/
24
- /******/ // The module cache
25
- /******/ var __webpack_module_cache__ = {};
26
- /******/
27
- /******/ // The require function
28
- /******/ function __webpack_require__(moduleId) {
29
- /******/ // Check if module is in cache
30
- /******/ var cachedModule = __webpack_module_cache__[moduleId];
31
- /******/ if (cachedModule !== undefined) {
32
- /******/ return cachedModule.exports;
33
- /******/ }
34
- /******/ // Create a new module (and put it into the cache)
35
- /******/ var module = __webpack_module_cache__[moduleId] = {
36
- /******/ // no module.id needed
37
- /******/ // no module.loaded needed
38
- /******/ exports: {}
39
- /******/ };
40
- /******/
41
- /******/ // Execute the module function
42
- /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
43
- /******/
44
- /******/ // Return the exports of the module
45
- /******/ return module.exports;
46
- /******/ }
47
- /******/
48
- /************************************************************************/
49
- /******/ /* webpack/runtime/compat get default export */
50
- /******/ (() => {
51
- /******/ // getDefaultExport function for compatibility with non-harmony modules
52
- /******/ __webpack_require__.n = (module) => {
53
- /******/ var getter = module && module.__esModule ?
54
- /******/ () => (module['default']) :
55
- /******/ () => (module);
56
- /******/ __webpack_require__.d(getter, { a: getter });
57
- /******/ return getter;
58
- /******/ };
59
- /******/ })();
60
- /******/
61
- /******/ /* webpack/runtime/define property getters */
62
- /******/ (() => {
63
- /******/ // define getter functions for harmony exports
64
- /******/ __webpack_require__.d = (exports, definition) => {
65
- /******/ for(var key in definition) {
66
- /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
67
- /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
68
- /******/ }
69
- /******/ }
70
- /******/ };
71
- /******/ })();
72
- /******/
73
- /******/ /* webpack/runtime/hasOwnProperty shorthand */
74
- /******/ (() => {
75
- /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
76
- /******/ })();
77
- /******/
78
- /******/ /* webpack/runtime/make namespace object */
79
- /******/ (() => {
80
- /******/ // define __esModule on exports
81
- /******/ __webpack_require__.r = (exports) => {
82
- /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
83
- /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
84
- /******/ }
85
- /******/ Object.defineProperty(exports, '__esModule', { value: true });
86
- /******/ };
87
- /******/ })();
88
- /******/
89
- /************************************************************************/
90
- var __webpack_exports__ = {};
91
- // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
92
- (() => {
93
- __webpack_require__.r(__webpack_exports__);
94
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
95
- /* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
96
- /* harmony export */ });
97
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(787);
98
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
99
- function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
100
- 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."); }
101
- 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); }
102
- function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
103
- function _iterableToArrayLimit(arr, i) { var _i = null == arr ? null : "undefined" != typeof Symbol && arr[Symbol.iterator] || arr["@@iterator"]; if (null != _i) { var _s, _e, _x, _r, _arr = [], _n = !0, _d = !1; try { if (_x = (_i = _i.call(arr)).next, 0 === i) { if (Object(_i) !== _i) return; _n = !1; } else for (; !(_n = (_s = _x.call(_i)).done) && (_arr.push(_s.value), _arr.length !== i); _n = !0); } catch (err) { _d = !0, _e = err; } finally { try { if (!_n && null != _i["return"] && (_r = _i["return"](), Object(_r) !== _r)) return; } finally { if (_d) throw _e; } } return _arr; } }
104
- function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
105
- /**
106
- * Global Url Listener (including micro frontends, frameworks, hashes, etc., applicable to multiple react app)
107
- *
108
- * @usage:
109
-
110
- const App = () => {
111
- const url = useGlobalUrlListener();
112
-
113
- useEffect(() => {
114
- console.log("URL changed:", url);
115
- }, [url]);
116
- };
117
-
118
- */
119
-
120
- var useGlobalUrlListener = function useGlobalUrlListener() {
121
- // Initialize state with empty string to avoid SSR issues
122
- var _useState = (0,react__WEBPACK_IMPORTED_MODULE_0__.useState)(''),
123
- _useState2 = _slicedToArray(_useState, 2),
124
- url = _useState2[0],
125
- setUrl = _useState2[1];
126
- (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(function () {
127
- // Type guard for SSR
128
- if (typeof window === 'undefined') return;
129
-
130
- // Initialize the URL on the client side
131
- setUrl(window.location.href);
132
-
133
- // Create MutationObserver instance
134
- var observer = new MutationObserver(function () {
135
- setUrl(window.location.href);
136
- });
137
-
138
- // Start observing
139
- observer.observe(document, {
140
- subtree: true,
141
- childList: true
142
- });
143
-
144
- // Cleanup function
145
- return function () {
146
- return observer.disconnect();
147
- };
148
- }, []);
149
- return url;
150
- };
151
- /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (useGlobalUrlListener);
152
- })();
153
-
154
- /******/ return __webpack_exports__;
155
- /******/ })()
156
- ;
157
- });
@@ -1,2 +0,0 @@
1
- declare const useGlobalUrlListener: () => string;
2
- export default useGlobalUrlListener;