@openwebf/react-router 0.24.2 → 1.0.1

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/dist/index.js CHANGED
@@ -36,14 +36,347 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
36
36
  };
37
37
 
38
38
  /**
39
- * Router management module
39
+ * Platform detection and abstraction layer
40
40
  *
41
- * Encapsulates routing navigation functionality with route guard mechanism for permission checks
41
+ * Detects whether running in WebF or standard browser environment
42
+ * and provides a unified interface for platform-specific APIs.
43
+ *
44
+ * WebF types are provided by @openwebf/webf-enterprise-typings package.
45
+ * In browser environments, the router works without WebF types.
42
46
  */
43
- function getHybridHistory() {
47
+ /**
48
+ * Get the WebF hybridHistory object if available
49
+ * Types come from @openwebf/webf-enterprise-typings peer dependency
50
+ */
51
+ function getWebFHybridHistory() {
44
52
  var _a;
45
53
  return (_a = globalThis === null || globalThis === void 0 ? void 0 : globalThis.webf) === null || _a === void 0 ? void 0 : _a.hybridHistory;
46
54
  }
55
+ /**
56
+ * Detect the current platform
57
+ */
58
+ function detectPlatform() {
59
+ // Check for WebF's hybridHistory API
60
+ if (getWebFHybridHistory()) {
61
+ return 'webf';
62
+ }
63
+ return 'browser';
64
+ }
65
+ /**
66
+ * Check if running in WebF environment
67
+ */
68
+ function isWebF() {
69
+ return detectPlatform() === 'webf';
70
+ }
71
+ /**
72
+ * Check if running in browser environment
73
+ */
74
+ function isBrowser() {
75
+ return detectPlatform() === 'browser';
76
+ }
77
+ /**
78
+ * Get the current platform type (evaluated once at module load)
79
+ */
80
+ const platform = detectPlatform();
81
+
82
+ /**
83
+ * Browser History Adapter
84
+ *
85
+ * Provides a unified history API that mimics WebF's hybridHistory
86
+ * but uses the standard browser History API under the hood.
87
+ *
88
+ * Supports SSR/Node.js environments by providing memory-based fallback.
89
+ */
90
+ /**
91
+ * Check if we're in a browser environment
92
+ */
93
+ function hasWindow() {
94
+ return typeof window !== 'undefined' && typeof window.history !== 'undefined';
95
+ }
96
+ /**
97
+ * Generate a unique key for history entries
98
+ */
99
+ function generateKey() {
100
+ return Math.random().toString(36).substring(2, 10);
101
+ }
102
+ /**
103
+ * Browser History implementation that provides WebF-like navigation APIs
104
+ */
105
+ class BrowserHistoryAdapter {
106
+ constructor() {
107
+ this._stack = [];
108
+ this._currentIndex = -1;
109
+ this._listeners = new Set();
110
+ this._initialized = false;
111
+ this._initialize();
112
+ }
113
+ _initialize() {
114
+ if (this._initialized)
115
+ return;
116
+ this._initialized = true;
117
+ // SSR/Node.js fallback - use memory-based history
118
+ if (!hasWindow()) {
119
+ const key = generateKey();
120
+ this._stack = [{ path: '/', state: null, key }];
121
+ this._currentIndex = 0;
122
+ return;
123
+ }
124
+ // Initialize with current location
125
+ const initialPath = window.location.pathname || '/';
126
+ const initialState = window.history.state;
127
+ if (initialState === null || initialState === void 0 ? void 0 : initialState.key) {
128
+ // Restore from existing state
129
+ this._stack = [{ path: initialState.path, state: initialState.state, key: initialState.key }];
130
+ this._currentIndex = 0;
131
+ }
132
+ else {
133
+ // Fresh start
134
+ const key = generateKey();
135
+ const entry = { path: initialPath, state: null, key };
136
+ this._stack = [entry];
137
+ this._currentIndex = 0;
138
+ // Replace current state with our tracked state
139
+ window.history.replaceState({ path: initialPath, state: null, key, index: 0 }, '', initialPath);
140
+ }
141
+ // Listen to popstate events
142
+ window.addEventListener('popstate', this._handlePopState.bind(this));
143
+ }
144
+ _handlePopState(event) {
145
+ const state = event.state;
146
+ if (state === null || state === void 0 ? void 0 : state.key) {
147
+ // Find the entry in our stack
148
+ const index = this._stack.findIndex((e) => e.key === state.key);
149
+ if (index !== -1) {
150
+ this._currentIndex = index;
151
+ }
152
+ }
153
+ // Dispatch custom event for Routes component
154
+ this._dispatchRouterChangeEvent((state === null || state === void 0 ? void 0 : state.path) || window.location.pathname, state === null || state === void 0 ? void 0 : state.state, 'didPopNext');
155
+ }
156
+ _dispatchRouterChangeEvent(path, state, kind) {
157
+ if (typeof document === 'undefined')
158
+ return;
159
+ const event = new CustomEvent('hybridrouterchange', {
160
+ bubbles: true,
161
+ composed: true,
162
+ detail: { path, state, kind },
163
+ });
164
+ event.path = path;
165
+ event.state = state;
166
+ event.kind = kind;
167
+ document.dispatchEvent(event);
168
+ }
169
+ /**
170
+ * Get the current state
171
+ */
172
+ get state() {
173
+ var _a, _b;
174
+ return (_b = (_a = this._stack[this._currentIndex]) === null || _a === void 0 ? void 0 : _a.state) !== null && _b !== void 0 ? _b : null;
175
+ }
176
+ /**
177
+ * Get the current path
178
+ */
179
+ get path() {
180
+ var _a, _b;
181
+ return (_b = (_a = this._stack[this._currentIndex]) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : '/';
182
+ }
183
+ /**
184
+ * Get the full navigation stack
185
+ */
186
+ get buildContextStack() {
187
+ // Return entries up to current index (simulating WebF's stack behavior)
188
+ return this._stack.slice(0, this._currentIndex + 1);
189
+ }
190
+ /**
191
+ * Push a new named route (WebF-style)
192
+ */
193
+ pushNamed(path, options) {
194
+ var _a;
195
+ const key = generateKey();
196
+ const state = (_a = options === null || options === void 0 ? void 0 : options.arguments) !== null && _a !== void 0 ? _a : null;
197
+ const newIndex = this._currentIndex + 1;
198
+ // Truncate forward stack if we're in the middle
199
+ this._stack = this._stack.slice(0, newIndex);
200
+ const entry = { path, state, key };
201
+ this._stack.push(entry);
202
+ this._currentIndex = newIndex;
203
+ if (hasWindow()) {
204
+ window.history.pushState({ path, state, key, index: newIndex }, '', path);
205
+ }
206
+ this._dispatchRouterChangeEvent(path, state, 'didPush');
207
+ }
208
+ /**
209
+ * Replace the current route (WebF-style)
210
+ */
211
+ pushReplacementNamed(path, options) {
212
+ var _a;
213
+ const key = generateKey();
214
+ const state = (_a = options === null || options === void 0 ? void 0 : options.arguments) !== null && _a !== void 0 ? _a : null;
215
+ const entry = { path, state, key };
216
+ this._stack[this._currentIndex] = entry;
217
+ if (hasWindow()) {
218
+ window.history.replaceState({ path, state, key, index: this._currentIndex }, '', path);
219
+ }
220
+ this._dispatchRouterChangeEvent(path, state, 'didPush');
221
+ }
222
+ /**
223
+ * Go back in history
224
+ */
225
+ back() {
226
+ var _a;
227
+ if (hasWindow()) {
228
+ window.history.back();
229
+ }
230
+ else if (this._currentIndex > 0) {
231
+ // Memory-based fallback for SSR
232
+ this._currentIndex--;
233
+ const entry = this._stack[this._currentIndex];
234
+ this._dispatchRouterChangeEvent((_a = entry === null || entry === void 0 ? void 0 : entry.path) !== null && _a !== void 0 ? _a : '/', entry === null || entry === void 0 ? void 0 : entry.state, 'didPopNext');
235
+ }
236
+ }
237
+ /**
238
+ * Pop the current route (Flutter-style)
239
+ */
240
+ pop(result) {
241
+ var _a;
242
+ if (this._currentIndex > 0) {
243
+ if (hasWindow()) {
244
+ window.history.back();
245
+ }
246
+ else {
247
+ // Memory-based fallback for SSR
248
+ this._currentIndex--;
249
+ const entry = this._stack[this._currentIndex];
250
+ this._dispatchRouterChangeEvent((_a = entry === null || entry === void 0 ? void 0 : entry.path) !== null && _a !== void 0 ? _a : '/', entry === null || entry === void 0 ? void 0 : entry.state, 'didPopNext');
251
+ }
252
+ }
253
+ }
254
+ /**
255
+ * Pop routes until reaching a specific route
256
+ */
257
+ popUntil(targetPath) {
258
+ var _a;
259
+ const targetIndex = this._stack.findIndex((e) => e.path === targetPath);
260
+ if (targetIndex !== -1 && targetIndex < this._currentIndex) {
261
+ if (hasWindow()) {
262
+ const delta = targetIndex - this._currentIndex;
263
+ window.history.go(delta);
264
+ }
265
+ else {
266
+ // Memory-based fallback for SSR
267
+ this._currentIndex = targetIndex;
268
+ const entry = this._stack[this._currentIndex];
269
+ this._dispatchRouterChangeEvent((_a = entry === null || entry === void 0 ? void 0 : entry.path) !== null && _a !== void 0 ? _a : '/', entry === null || entry === void 0 ? void 0 : entry.state, 'didPopNext');
270
+ }
271
+ }
272
+ }
273
+ /**
274
+ * Pop the current route and push a new route
275
+ */
276
+ popAndPushNamed(path, options) {
277
+ var _a;
278
+ const key = generateKey();
279
+ const state = (_a = options === null || options === void 0 ? void 0 : options.arguments) !== null && _a !== void 0 ? _a : null;
280
+ const entry = { path, state, key };
281
+ this._stack[this._currentIndex] = entry;
282
+ if (hasWindow()) {
283
+ window.history.replaceState({ path, state, key, index: this._currentIndex }, '', path);
284
+ }
285
+ this._dispatchRouterChangeEvent(path, state, 'didPush');
286
+ }
287
+ /**
288
+ * Push a new route and remove routes until a specific route
289
+ */
290
+ pushNamedAndRemoveUntil(state, path, untilPath) {
291
+ const targetIndex = this._stack.findIndex((e) => e.path === untilPath);
292
+ if (targetIndex !== -1) {
293
+ // Truncate stack to target and add new entry
294
+ this._stack = this._stack.slice(0, targetIndex + 1);
295
+ this._currentIndex = targetIndex;
296
+ }
297
+ this.pushNamed(path, { arguments: state });
298
+ }
299
+ /**
300
+ * Push a new route and remove all routes until a specific route (Flutter-style)
301
+ */
302
+ pushNamedAndRemoveUntilRoute(newPath, untilPath, options) {
303
+ this.pushNamedAndRemoveUntil(options === null || options === void 0 ? void 0 : options.arguments, newPath, untilPath);
304
+ }
305
+ /**
306
+ * Check if we can go back
307
+ */
308
+ canPop() {
309
+ return this._currentIndex > 0;
310
+ }
311
+ /**
312
+ * Pop if possible, return success status
313
+ */
314
+ maybePop(result) {
315
+ if (this.canPop()) {
316
+ this.pop(result);
317
+ return true;
318
+ }
319
+ return false;
320
+ }
321
+ /**
322
+ * Push state (web-style)
323
+ */
324
+ pushState(state, name) {
325
+ this.pushNamed(name, { arguments: state });
326
+ }
327
+ /**
328
+ * Replace state (web-style)
329
+ */
330
+ replaceState(state, name) {
331
+ this.pushReplacementNamed(name, { arguments: state });
332
+ }
333
+ /**
334
+ * Pop and push with restoration capability
335
+ */
336
+ restorablePopAndPushState(state, name) {
337
+ const key = generateKey();
338
+ this.popAndPushNamed(name, { arguments: state });
339
+ return key;
340
+ }
341
+ /**
342
+ * Pop and push named route with restoration capability
343
+ */
344
+ restorablePopAndPushNamed(path, options) {
345
+ const key = generateKey();
346
+ this.popAndPushNamed(path, options);
347
+ return key;
348
+ }
349
+ }
350
+ // Singleton instance
351
+ let browserHistoryInstance = null;
352
+ /**
353
+ * Get the browser history adapter instance
354
+ */
355
+ function getBrowserHistory() {
356
+ if (!browserHistoryInstance) {
357
+ browserHistoryInstance = new BrowserHistoryAdapter();
358
+ }
359
+ return browserHistoryInstance;
360
+ }
361
+ /**
362
+ * Reset the browser history adapter (for testing)
363
+ */
364
+ function resetBrowserHistory() {
365
+ browserHistoryInstance = null;
366
+ }
367
+
368
+ /**
369
+ * Get the appropriate history object based on the current platform.
370
+ * Returns WebF's hybridHistory in WebF environment, or BrowserHistoryAdapter in browser.
371
+ */
372
+ function getHybridHistory() {
373
+ const webfHistory = getWebFHybridHistory();
374
+ if (webfHistory) {
375
+ return webfHistory;
376
+ }
377
+ // Fallback to browser history adapter
378
+ return getBrowserHistory();
379
+ }
47
380
  let ensureRouteMountedCallback = null;
48
381
  function __unstable_setEnsureRouteMountedCallback(callback) {
49
382
  ensureRouteMountedCallback = callback;
@@ -57,7 +390,8 @@ function ensureRouteMounted(pathname) {
57
390
  }
58
391
  /**
59
392
  * WebF Router object - provides comprehensive navigation APIs
60
- * Combines web-like history management with Flutter-like navigation patterns
393
+ * Combines web-like history management with Flutter-like navigation patterns.
394
+ * Works in both WebF native environment and standard browser environment.
61
395
  */
62
396
  const WebFRouter = {
63
397
  /**
@@ -88,8 +422,6 @@ const WebFRouter = {
88
422
  */
89
423
  push: (path, state) => __awaiter(void 0, void 0, void 0, function* () {
90
424
  const hybridHistory = getHybridHistory();
91
- if (!hybridHistory)
92
- throw new Error('WebF hybridHistory is not available');
93
425
  yield ensureRouteMounted(path);
94
426
  hybridHistory.pushNamed(path, { arguments: state });
95
427
  }),
@@ -99,8 +431,6 @@ const WebFRouter = {
99
431
  */
100
432
  replace: (path, state) => __awaiter(void 0, void 0, void 0, function* () {
101
433
  const hybridHistory = getHybridHistory();
102
- if (!hybridHistory)
103
- throw new Error('WebF hybridHistory is not available');
104
434
  yield ensureRouteMounted(path);
105
435
  hybridHistory.pushReplacementNamed(path, { arguments: state });
106
436
  }),
@@ -109,8 +439,6 @@ const WebFRouter = {
109
439
  */
110
440
  back: () => {
111
441
  const hybridHistory = getHybridHistory();
112
- if (!hybridHistory)
113
- throw new Error('WebF hybridHistory is not available');
114
442
  hybridHistory.back();
115
443
  },
116
444
  /**
@@ -119,8 +447,6 @@ const WebFRouter = {
119
447
  */
120
448
  pop: (result) => {
121
449
  const hybridHistory = getHybridHistory();
122
- if (!hybridHistory)
123
- throw new Error('WebF hybridHistory is not available');
124
450
  hybridHistory.pop(result);
125
451
  },
126
452
  /**
@@ -128,8 +454,6 @@ const WebFRouter = {
128
454
  */
129
455
  popUntil: (path) => {
130
456
  const hybridHistory = getHybridHistory();
131
- if (!hybridHistory)
132
- throw new Error('WebF hybridHistory is not available');
133
457
  hybridHistory.popUntil(path);
134
458
  },
135
459
  /**
@@ -137,8 +461,6 @@ const WebFRouter = {
137
461
  */
138
462
  popAndPushNamed: (path, state) => __awaiter(void 0, void 0, void 0, function* () {
139
463
  const hybridHistory = getHybridHistory();
140
- if (!hybridHistory)
141
- throw new Error('WebF hybridHistory is not available');
142
464
  yield ensureRouteMounted(path);
143
465
  hybridHistory.popAndPushNamed(path, { arguments: state });
144
466
  }),
@@ -147,8 +469,6 @@ const WebFRouter = {
147
469
  */
148
470
  pushNamedAndRemoveUntil: (path, state, untilPath) => __awaiter(void 0, void 0, void 0, function* () {
149
471
  const hybridHistory = getHybridHistory();
150
- if (!hybridHistory)
151
- throw new Error('WebF hybridHistory is not available');
152
472
  yield ensureRouteMounted(path);
153
473
  hybridHistory.pushNamedAndRemoveUntil(state, path, untilPath);
154
474
  }),
@@ -157,8 +477,6 @@ const WebFRouter = {
157
477
  */
158
478
  pushNamedAndRemoveUntilRoute: (newPath, untilPath, state) => __awaiter(void 0, void 0, void 0, function* () {
159
479
  const hybridHistory = getHybridHistory();
160
- if (!hybridHistory)
161
- throw new Error('WebF hybridHistory is not available');
162
480
  yield ensureRouteMounted(newPath);
163
481
  hybridHistory.pushNamedAndRemoveUntilRoute(newPath, untilPath, { arguments: state });
164
482
  }),
@@ -167,8 +485,6 @@ const WebFRouter = {
167
485
  */
168
486
  canPop: () => {
169
487
  const hybridHistory = getHybridHistory();
170
- if (!hybridHistory)
171
- return false;
172
488
  return hybridHistory.canPop();
173
489
  },
174
490
  /**
@@ -177,8 +493,6 @@ const WebFRouter = {
177
493
  */
178
494
  maybePop: (result) => {
179
495
  const hybridHistory = getHybridHistory();
180
- if (!hybridHistory)
181
- return false;
182
496
  return hybridHistory.maybePop(result);
183
497
  },
184
498
  /**
@@ -186,8 +500,6 @@ const WebFRouter = {
186
500
  */
187
501
  pushState: (state, name) => {
188
502
  const hybridHistory = getHybridHistory();
189
- if (!hybridHistory)
190
- throw new Error('WebF hybridHistory is not available');
191
503
  hybridHistory.pushState(state, name);
192
504
  },
193
505
  /**
@@ -195,8 +507,6 @@ const WebFRouter = {
195
507
  */
196
508
  replaceState: (state, name) => {
197
509
  const hybridHistory = getHybridHistory();
198
- if (!hybridHistory)
199
- throw new Error('WebF hybridHistory is not available');
200
510
  hybridHistory.replaceState(state, name);
201
511
  },
202
512
  /**
@@ -205,8 +515,6 @@ const WebFRouter = {
205
515
  */
206
516
  restorablePopAndPushState: (state, name) => {
207
517
  const hybridHistory = getHybridHistory();
208
- if (!hybridHistory)
209
- throw new Error('WebF hybridHistory is not available');
210
518
  return hybridHistory.restorablePopAndPushState(state, name);
211
519
  },
212
520
  /**
@@ -215,8 +523,6 @@ const WebFRouter = {
215
523
  */
216
524
  restorablePopAndPushNamed: (path, state) => __awaiter(void 0, void 0, void 0, function* () {
217
525
  const hybridHistory = getHybridHistory();
218
- if (!hybridHistory)
219
- throw new Error('WebF hybridHistory is not available');
220
526
  yield ensureRouteMounted(path);
221
527
  return hybridHistory.restorablePopAndPushNamed(path, { arguments: state });
222
528
  })
@@ -319,41 +625,168 @@ var useMemoizedFn = function (fn) {
319
625
  return memoizedFn.current;
320
626
  };
321
627
 
322
- // Create the raw component using createWebFComponent
323
- const RawWebFRouterLink = reactCoreUi.createWebFComponent({
324
- tagName: 'webf-router-link',
325
- displayName: 'WebFRouterLink',
326
- // Map props to attributes
327
- attributeProps: ['path', 'title', 'theme'],
328
- // Event handlers
329
- events: [
330
- {
331
- propName: 'onScreen',
332
- eventName: 'onscreen',
333
- handler: (callback) => (event) => {
334
- // Cast through unknown first for proper type conversion
335
- callback(event);
628
+ // Lazily create the WebF component only when needed
629
+ let RawWebFRouterLink = null;
630
+ function getRawWebFRouterLink() {
631
+ if (RawWebFRouterLink)
632
+ return RawWebFRouterLink;
633
+ RawWebFRouterLink = reactCoreUi.createWebFComponent({
634
+ tagName: 'webf-router-link',
635
+ displayName: 'WebFRouterLink',
636
+ // Map props to attributes
637
+ attributeProps: ['path', 'title', 'theme'],
638
+ // Event handlers
639
+ events: [
640
+ {
641
+ propName: 'onScreen',
642
+ eventName: 'onscreen',
643
+ handler: (callback) => (event) => {
644
+ callback(event);
645
+ },
336
646
  },
337
- },
338
- {
339
- propName: 'offScreen',
340
- eventName: 'offscreen',
341
- handler: (callback) => (event) => {
342
- // Cast through unknown first for proper type conversion
343
- callback(event);
647
+ {
648
+ propName: 'offScreen',
649
+ eventName: 'offscreen',
650
+ handler: (callback) => (event) => {
651
+ callback(event);
652
+ },
344
653
  },
345
- },
346
- {
347
- propName: 'onPrerendering',
348
- eventName: 'prerendering',
349
- handler: (callback) => (event) => {
350
- callback(event);
654
+ {
655
+ propName: 'onPrerendering',
656
+ eventName: 'prerendering',
657
+ handler: (callback) => (event) => {
658
+ callback(event);
659
+ },
351
660
  },
352
- },
353
- ],
354
- });
355
- const WebFRouterLink = function (props) {
661
+ ],
662
+ });
663
+ return RawWebFRouterLink;
664
+ }
665
+ /**
666
+ * Browser-based RouterLink implementation
667
+ * Used when running in standard browser environment instead of WebF
668
+ */
669
+ const BrowserRouterLink = function (props) {
670
+ const [isActive, setIsActive] = React.useState(false);
671
+ const [isRender, setIsRender] = React.useState(false);
672
+ const hasTriggeredOnScreen = React.useRef(false);
673
+ React.useEffect(() => {
674
+ const browserHistory = getBrowserHistory();
675
+ const currentPath = browserHistory.path;
676
+ // Check if this route matches the current path
677
+ const isCurrentlyActive = currentPath === props.path;
678
+ setIsActive(isCurrentlyActive);
679
+ if (isCurrentlyActive && !hasTriggeredOnScreen.current) {
680
+ hasTriggeredOnScreen.current = true;
681
+ setIsRender(true);
682
+ // Create a synthetic event for onScreen callback
683
+ if (props.onScreen) {
684
+ const syntheticEvent = {
685
+ state: browserHistory.state,
686
+ kind: 'didPush',
687
+ path: props.path,
688
+ nativeEvent: new Event('onscreen'),
689
+ currentTarget: null,
690
+ target: null,
691
+ bubbles: true,
692
+ cancelable: false,
693
+ defaultPrevented: false,
694
+ eventPhase: 0,
695
+ isTrusted: true,
696
+ preventDefault: () => { },
697
+ isDefaultPrevented: () => false,
698
+ stopPropagation: () => { },
699
+ isPropagationStopped: () => false,
700
+ persist: () => { },
701
+ timeStamp: Date.now(),
702
+ type: 'onscreen',
703
+ };
704
+ props.onScreen(syntheticEvent);
705
+ }
706
+ }
707
+ // Listen for route changes
708
+ const handleRouteChange = (event) => {
709
+ var _a, _b, _c, _d, _e;
710
+ const customEvent = event;
711
+ const newPath = ((_a = customEvent.detail) === null || _a === void 0 ? void 0 : _a.path) || event.path;
712
+ const newIsActive = newPath === props.path;
713
+ if (newIsActive && !isActive) {
714
+ // Route became active
715
+ hasTriggeredOnScreen.current = true;
716
+ setIsRender(true);
717
+ setIsActive(true);
718
+ if (props.onScreen) {
719
+ const syntheticEvent = {
720
+ state: ((_b = customEvent.detail) === null || _b === void 0 ? void 0 : _b.state) || event.state,
721
+ kind: (((_c = customEvent.detail) === null || _c === void 0 ? void 0 : _c.kind) || event.kind),
722
+ path: newPath,
723
+ nativeEvent: event,
724
+ currentTarget: event.currentTarget,
725
+ target: event.target,
726
+ bubbles: true,
727
+ cancelable: false,
728
+ defaultPrevented: false,
729
+ eventPhase: 0,
730
+ isTrusted: true,
731
+ preventDefault: () => { },
732
+ isDefaultPrevented: () => false,
733
+ stopPropagation: () => { },
734
+ isPropagationStopped: () => false,
735
+ persist: () => { },
736
+ timeStamp: Date.now(),
737
+ type: 'onscreen',
738
+ };
739
+ props.onScreen(syntheticEvent);
740
+ }
741
+ }
742
+ else if (!newIsActive && isActive) {
743
+ // Route became inactive
744
+ setIsActive(false);
745
+ if (props.offScreen) {
746
+ const syntheticEvent = {
747
+ state: ((_d = customEvent.detail) === null || _d === void 0 ? void 0 : _d.state) || event.state,
748
+ kind: (((_e = customEvent.detail) === null || _e === void 0 ? void 0 : _e.kind) || event.kind),
749
+ path: newPath,
750
+ nativeEvent: event,
751
+ currentTarget: event.currentTarget,
752
+ target: event.target,
753
+ bubbles: true,
754
+ cancelable: false,
755
+ defaultPrevented: false,
756
+ eventPhase: 0,
757
+ isTrusted: true,
758
+ preventDefault: () => { },
759
+ isDefaultPrevented: () => false,
760
+ stopPropagation: () => { },
761
+ isPropagationStopped: () => false,
762
+ persist: () => { },
763
+ timeStamp: Date.now(),
764
+ type: 'offscreen',
765
+ };
766
+ props.offScreen(syntheticEvent);
767
+ }
768
+ }
769
+ };
770
+ document.addEventListener('hybridrouterchange', handleRouteChange);
771
+ return () => {
772
+ document.removeEventListener('hybridrouterchange', handleRouteChange);
773
+ };
774
+ }, [props.path, props.onScreen, props.offScreen, isActive]);
775
+ // In browser mode, we render a div that acts as a route container
776
+ // Only show content when route is rendered (similar to WebF behavior)
777
+ return (React.createElement("div", { "data-path": props.path, "data-title": props.title, style: {
778
+ display: isActive ? 'block' : 'none',
779
+ width: '100%',
780
+ height: '100%',
781
+ } }, isRender ? props.children : null));
782
+ };
783
+ /**
784
+ * WebF RouterLink implementation
785
+ * Used in WebF native environment
786
+ */
787
+ const WebFNativeRouterLink = function (props) {
356
788
  const [isRender, enableRender] = React.useState(false);
789
+ const RawComponent = getRawWebFRouterLink();
357
790
  const handleOnScreen = (event) => {
358
791
  enableRender(true);
359
792
  if (props.onScreen) {
@@ -365,7 +798,18 @@ const WebFRouterLink = function (props) {
365
798
  enableRender(true);
366
799
  (_a = props.onPrerendering) === null || _a === void 0 ? void 0 : _a.call(props, event);
367
800
  };
368
- return (React.createElement(RawWebFRouterLink, { title: props.title, path: props.path, theme: props.theme, onScreen: handleOnScreen, offScreen: props.offScreen, onPrerendering: handlePrerendering }, isRender ? props.children : null));
801
+ return (React.createElement(RawComponent, { title: props.title, path: props.path, theme: props.theme, onScreen: handleOnScreen, offScreen: props.offScreen, onPrerendering: handlePrerendering }, isRender ? props.children : null));
802
+ };
803
+ /**
804
+ * Unified RouterLink component that works in both WebF and browser environments
805
+ */
806
+ const WebFRouterLink = function (props) {
807
+ // Use WebF native implementation if in WebF environment
808
+ if (isWebF()) {
809
+ return React.createElement(WebFNativeRouterLink, Object.assign({}, props));
810
+ }
811
+ // Use browser-based implementation
812
+ return React.createElement(BrowserRouterLink, Object.assign({}, props));
369
813
  };
370
814
 
371
815
  /**
@@ -832,9 +1276,14 @@ exports.Routes = Routes;
832
1276
  exports.WebFRouter = WebFRouter;
833
1277
  exports.WebFRouterLink = WebFRouterLink;
834
1278
  exports.__unstable_setEnsureRouteMountedCallback = __unstable_setEnsureRouteMountedCallback;
1279
+ exports.detectPlatform = detectPlatform;
1280
+ exports.isBrowser = isBrowser;
1281
+ exports.isWebF = isWebF;
835
1282
  exports.matchPath = matchPath;
836
1283
  exports.matchRoutes = matchRoutes;
837
1284
  exports.pathToRegex = pathToRegex;
1285
+ exports.platform = platform;
1286
+ exports.resetBrowserHistory = resetBrowserHistory;
838
1287
  exports.useLocation = useLocation;
839
1288
  exports.useNavigate = useNavigate;
840
1289
  exports.useParams = useParams;