@sigmela/router 0.2.6 → 0.2.8

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/README.md CHANGED
@@ -152,6 +152,47 @@ Key methods:
152
152
  - `addSheet(pathPattern, componentOrStack, options?)` (shorthand for `stackPresentation: 'sheet'`)
153
153
  - `addStack(prefixOrStack, maybeStack?)` — compose nested stacks under a prefix
154
154
 
155
+ #### Provider Context
156
+
157
+ You can wrap an entire stack with a React context provider by passing a `provider` option:
158
+
159
+ ```tsx
160
+ import { ThemeProvider } from './theme';
161
+
162
+ const stack = new NavigationStack({
163
+ header: { largeTitle: true },
164
+ provider: ThemeProvider,
165
+ })
166
+ .addScreen('/feed', FeedScreen)
167
+ .addScreen('/feed/:id', FeedItemScreen);
168
+ ```
169
+
170
+ The `provider` component wraps the entire stack renderer, making the context available to all screens in the stack. This is useful for:
171
+ - **Theme providers**: Apply theme context to all screens
172
+ - **Auth providers**: Share authentication state across screens
173
+ - **Localization**: Provide i18n context to the entire stack
174
+
175
+ **Composing multiple providers:**
176
+
177
+ If you need multiple providers, create a composed component:
178
+
179
+ ```tsx
180
+ const ComposedProvider = ({ children }) => (
181
+ <ThemeProvider>
182
+ <AuthProvider>
183
+ <I18nProvider>
184
+ {children}
185
+ </I18nProvider>
186
+ </AuthProvider>
187
+ </ThemeProvider>
188
+ );
189
+
190
+ const stack = new NavigationStack({ provider: ComposedProvider })
191
+ .addScreen('/', HomeScreen);
192
+ ```
193
+
194
+ **Important:** The provider should be a stable reference (not an inline arrow function) to avoid unnecessary re-renders.
195
+
155
196
  ### Modal Stacks (Stack in Stack)
156
197
 
157
198
  You can pass an entire `NavigationStack` to `addModal()` or `addSheet()` to create a multi-screen flow inside a modal:
@@ -14,10 +14,12 @@ export class NavigationStack {
14
14
  this.stackId = idOrOptions ?? `stack-${nanoid()}`;
15
15
  this.defaultOptions = maybeOptions;
16
16
  this.debugEnabled = maybeDebug ?? false;
17
+ this.provider = maybeOptions?.provider;
17
18
  } else {
18
19
  this.stackId = `stack-${nanoid()}`;
19
20
  this.defaultOptions = idOrOptions;
20
21
  this.debugEnabled = false;
22
+ this.provider = idOrOptions?.provider;
21
23
  }
22
24
  }
23
25
  log(message, data) {
@@ -135,11 +137,16 @@ export class NavigationStack {
135
137
  // eslint-disable-next-line consistent-this
136
138
  const stackInstance = this;
137
139
  const stackId = stackInstance.getId();
140
+ const provider = this.provider;
138
141
  return function NavigationStackRenderer(props) {
139
- return /*#__PURE__*/React.createElement(StackRenderer, {
142
+ const stackElement = /*#__PURE__*/React.createElement(StackRenderer, {
140
143
  stackId: stackId,
141
144
  appearance: props.appearance
142
145
  });
146
+ if (provider) {
147
+ return /*#__PURE__*/React.createElement(provider, null, stackElement);
148
+ }
149
+ return stackElement;
143
150
  };
144
151
  }
145
152
  seed() {
@@ -466,6 +466,9 @@ export const ScreenStack = /*#__PURE__*/memo(props => {
466
466
  children: /*#__PURE__*/_jsx("div", {
467
467
  ref: containerRef,
468
468
  className: containerClassName + (animating ? ' animating' : ''),
469
+ style: {
470
+ '--stack-transition-time': `${transitionTime}ms`
471
+ },
469
472
  children: keysToRender.map(key => {
470
473
  const transitionState = stateMap.get(key);
471
474
  if (!transitionState || !transitionState.isMounted) {
@@ -17,8 +17,10 @@
17
17
 
18
18
  /* Base transition for opening screen */
19
19
  transition:
20
- transform 300ms cubic-bezier(0.22, 0.61, 0.36, 1),
21
- filter 300ms cubic-bezier(0.22, 0.61, 0.36, 1);
20
+ transform var(--stack-transition-time, 300ms)
21
+ cubic-bezier(0.22, 0.61, 0.36, 1),
22
+ filter var(--stack-transition-time, 300ms)
23
+ cubic-bezier(0.22, 0.61, 0.36, 1);
22
24
  }
23
25
 
24
26
  /* Inner container for regular screen */
@@ -43,6 +45,8 @@
43
45
  pointer-events: none;
44
46
  z-index: 1;
45
47
  will-change: opacity;
48
+ transition: opacity var(--stack-transition-time, 300ms)
49
+ cubic-bezier(0.22, 0.61, 0.36, 1);
46
50
  }
47
51
 
48
52
  /* Show overlay only for modal-like presentations.
@@ -53,26 +57,6 @@
53
57
  display: none;
54
58
  }
55
59
 
56
- /* Keyframes for overlay appearance */
57
- @keyframes modal-overlay-enter {
58
- from {
59
- opacity: 0;
60
- }
61
- to {
62
- opacity: 0.5;
63
- }
64
- }
65
-
66
- /* Keyframes for overlay disappearance */
67
- @keyframes modal-overlay-exit {
68
- from {
69
- opacity: 0.5;
70
- }
71
- to {
72
- opacity: 0;
73
- }
74
- }
75
-
76
60
  /* Overlay in initial state — transparent */
77
61
  .screen-stack-item.modal.transition-preEnter > .stack-modal-overlay,
78
62
  .screen-stack-item.modal-right.transition-preEnter > .stack-modal-overlay,
@@ -86,47 +70,33 @@
86
70
  pointer-events: none;
87
71
  }
88
72
 
89
- /* Overlay on enter — start animation */
73
+ /* Overlay on enter — fade in via transition */
90
74
  .screen-stack-item.modal.transition-entering > .stack-modal-overlay,
91
- .screen-stack-item.modal.phase-active.transition-preEnter > .stack-modal-overlay,
92
75
  .screen-stack-item.modal-right.transition-entering > .stack-modal-overlay,
93
- .screen-stack-item.modal-right.phase-active.transition-preEnter > .stack-modal-overlay,
94
76
  .screen-stack-item.contained-modal.transition-entering > .stack-modal-overlay,
95
- .screen-stack-item.contained-modal.phase-active.transition-preEnter > .stack-modal-overlay,
96
77
  .screen-stack-item.fullscreen-modal.transition-entering > .stack-modal-overlay,
97
- .screen-stack-item.fullscreen-modal.phase-active.transition-preEnter > .stack-modal-overlay,
98
78
  .screen-stack-item.formsheet.transition-entering > .stack-modal-overlay,
99
- .screen-stack-item.formsheet.phase-active.transition-preEnter > .stack-modal-overlay,
100
79
  .screen-stack-item.pagesheet.transition-entering > .stack-modal-overlay,
101
- .screen-stack-item.pagesheet.phase-active.transition-preEnter > .stack-modal-overlay,
102
- .screen-stack-item.sheet.transition-entering > .stack-modal-overlay,
103
- .screen-stack-item.sheet.phase-active.transition-preEnter > .stack-modal-overlay {
80
+ .screen-stack-item.sheet.transition-entering > .stack-modal-overlay {
104
81
  background: rgba(0, 0, 0, 0.5);
105
82
  pointer-events: none;
106
- animation: modal-overlay-enter 300ms cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
83
+ opacity: 0.5;
107
84
  }
108
85
 
109
86
  /* For modal-like in active / entered states — fully visible */
110
- .screen-stack-item.modal.phase-active > .stack-modal-overlay,
111
87
  .screen-stack-item.modal.transition-entered > .stack-modal-overlay,
112
- .screen-stack-item.modal-right.phase-active > .stack-modal-overlay,
113
88
  .screen-stack-item.modal-right.transition-entered > .stack-modal-overlay,
114
- .screen-stack-item.contained-modal.phase-active > .stack-modal-overlay,
115
89
  .screen-stack-item.contained-modal.transition-entered > .stack-modal-overlay,
116
- .screen-stack-item.fullscreen-modal.phase-active > .stack-modal-overlay,
117
90
  .screen-stack-item.fullscreen-modal.transition-entered > .stack-modal-overlay,
118
- .screen-stack-item.formsheet.phase-active > .stack-modal-overlay,
119
91
  .screen-stack-item.formsheet.transition-entered > .stack-modal-overlay,
120
- .screen-stack-item.pagesheet.phase-active > .stack-modal-overlay,
121
92
  .screen-stack-item.pagesheet.transition-entered > .stack-modal-overlay,
122
- .screen-stack-item.sheet.phase-active > .stack-modal-overlay,
123
93
  .screen-stack-item.sheet.transition-entered > .stack-modal-overlay {
124
94
  opacity: 0.5;
125
95
  background: rgba(0, 0, 0, 0.5);
126
96
  pointer-events: auto;
127
97
  }
128
98
 
129
- /* Overlay on modal-like close — disappearance animation */
99
+ /* Overlay on modal-like close — fade out via transition */
130
100
  .screen-stack-item.modal.phase-exiting > .stack-modal-overlay,
131
101
  .screen-stack-item.modal.transition-exiting > .stack-modal-overlay,
132
102
  .screen-stack-item.modal-right.phase-exiting > .stack-modal-overlay,
@@ -143,7 +113,7 @@
143
113
  .screen-stack-item.sheet.transition-exiting > .stack-modal-overlay {
144
114
  background: rgba(0, 0, 0, 0.5);
145
115
  pointer-events: none;
146
- animation: modal-overlay-exit 300ms cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
116
+ opacity: 0;
147
117
  }
148
118
 
149
119
  /* Overlay for transparent-modal — transparent */
@@ -1,4 +1,4 @@
1
- import type { ScreenOptions } from './types';
1
+ import type { ScreenOptions, NavigationStackOptions } from './types';
2
2
  import React from 'react';
3
3
  import { type MixedComponent } from './createController';
4
4
  import type { NavigationNode, NodeRoute, NodeChild } from './navigationNode';
@@ -10,11 +10,12 @@ export declare class NavigationStack implements NavigationNode {
10
10
  private readonly children;
11
11
  private readonly defaultOptions;
12
12
  private readonly debugEnabled;
13
+ private readonly provider;
13
14
  constructor();
14
15
  constructor(id: string);
15
- constructor(defaultOptions: ScreenOptions);
16
- constructor(id: string, defaultOptions: ScreenOptions);
17
- constructor(id: string, defaultOptions: ScreenOptions, debug: boolean);
16
+ constructor(defaultOptions: NavigationStackOptions);
17
+ constructor(id: string, defaultOptions: NavigationStackOptions);
18
+ constructor(id: string, defaultOptions: NavigationStackOptions, debug: boolean);
18
19
  private log;
19
20
  getId(): string;
20
21
  addScreen(pathPattern: string, mixedComponent: MixedComponent | NavigationNode, options?: ScreenOptions): NavigationStack;
@@ -43,6 +43,15 @@ export type ScreenOptions = Partial<Omit<RNSScreenProps, 'stackPresentation'>> &
43
43
  */
44
44
  maxWidth?: number;
45
45
  };
46
+ export type NavigationStackOptions = ScreenOptions & {
47
+ /**
48
+ * Optional React component to wrap the entire stack renderer.
49
+ * Useful for providing context (theme, auth, etc.) to all screens in the stack.
50
+ */
51
+ provider?: React.ComponentType<{
52
+ children: React.ReactNode;
53
+ }>;
54
+ };
46
55
  export type HistoryItem = {
47
56
  key: string;
48
57
  routeId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigmela/router",
3
- "version": "0.2.6",
3
+ "version": "0.2.8",
4
4
  "description": "React Native Router",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",