mobx-route 0.19.0 → 0.20.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/README.md CHANGED
@@ -22,9 +22,9 @@ _Uses [`path-to-regexp` power](https://www.npmjs.com/package/path-to-regexp)_
22
22
 
23
23
 
24
24
  ```ts
25
- import { Route } from "mobx-route";
25
+ import { createRoute } from "mobx-route";
26
26
 
27
- const userDetails = new Route("/users/:id");
27
+ const userDetails = createRoute("/users/:id");
28
28
 
29
29
  await userDetails.open({ id: 1 }); // path params are required
30
30
 
package/index.cjs CHANGED
@@ -34,7 +34,8 @@ const routeConfig = complex.createGlobalDynamicConfig(
34
34
  location,
35
35
  queryParams
36
36
  };
37
- }
37
+ },
38
+ Symbol.for("MOBX_ROUTE_CONFIG")
38
39
  );
39
40
  class Route {
40
41
  constructor(path, config = {}) {
@@ -46,35 +47,28 @@ class Route {
46
47
  this.isIndex = !!this.config.index;
47
48
  this.isHash = !!this.config.hash;
48
49
  this.meta = this.config.meta;
50
+ this.status = "unknown";
49
51
  this.parent = config.parent ?? null;
50
- mobx.computed.struct(this, "isOpened");
52
+ mobx.computed(this, "isPathMatched");
53
+ mobx.computed(this, "isOpened");
51
54
  mobx.computed.struct(this, "data");
52
55
  mobx.computed.struct(this, "params");
53
- mobx.computed.struct(this, "currentPath");
54
- mobx.computed.struct(this, "hasOpenedChildren");
55
- mobx.computed.struct(this, "isAbleToMergeQuery");
56
+ mobx.computed(this, "currentPath");
57
+ mobx.computed(this, "hasOpenedChildren");
58
+ mobx.computed(this, "isAbleToMergeQuery");
56
59
  mobx.computed(this, "baseUrl");
57
60
  mobx.observable(this, "children");
58
61
  mobx.observable.ref(this, "parent");
62
+ mobx.observable.ref(this, "status");
63
+ mobx.computed(this, "isOpening");
59
64
  mobx.action(this, "addChildren");
65
+ mobx.action(this, "confirmOpening");
66
+ mobx.action(this, "confirmClosing");
60
67
  mobx.action(this, "removeChildren");
61
68
  mobx.makeObservable(this);
62
- mobx.onBecomeObserved(this, "isOpened", () => {
63
- if (!config.afterOpen && !config.afterClose) {
64
- return;
65
- }
66
- this.reactionDisposer = mobx.reaction(
67
- () => this.isOpened,
68
- this.processOpenedState,
69
- {
70
- signal: this.abortController.signal,
71
- fireImmediately: true
72
- }
73
- );
74
- });
75
- mobx.onBecomeUnobserved(this, "isOpened", () => {
76
- this.reactionDisposer?.();
77
- this.reactionDisposer = void 0;
69
+ mobx.reaction(() => this.isPathMatched, this.checkPathMatch, {
70
+ signal: this.abortController.signal,
71
+ fireImmediately: true
78
72
  });
79
73
  }
80
74
  abortController;
@@ -84,7 +78,8 @@ class Route {
84
78
  _tokenData;
85
79
  _matcher;
86
80
  _compiler;
87
- reactionDisposer;
81
+ skipPathMatchCheck = false;
82
+ status;
88
83
  meta;
89
84
  /**
90
85
  * Indicates if this route is an index route. Index routes activate when parent route path matches exactly.
@@ -120,7 +115,7 @@ class Route {
120
115
  return { params: {}, path: pathnameToCheck };
121
116
  }
122
117
  this._matcher ??= pathToRegexp.match(this.tokenData, {
123
- end: false,
118
+ end: this.config.exact ?? false,
124
119
  ...this.config.matchOptions
125
120
  });
126
121
  const parsed = this._matcher(pathnameToCheck);
@@ -129,6 +124,9 @@ class Route {
129
124
  }
130
125
  return parsed;
131
126
  }
127
+ get isOpening() {
128
+ return this.status === "opening";
129
+ }
132
130
  /**
133
131
  * Matched path segment for current URL.
134
132
  *
@@ -160,13 +158,16 @@ class Route {
160
158
  }
161
159
  return params;
162
160
  }
161
+ get isPathMatched() {
162
+ return this.parsedPathData !== null;
163
+ }
163
164
  /**
164
165
  * Defines the "open" state for this route.
165
166
  *
166
167
  * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#isopened-boolean)
167
168
  */
168
169
  get isOpened() {
169
- if (this.params === null || this.parsedPathData === null) {
170
+ if (!this.isPathMatched || this.params === null || this.status !== "open-confirmed") {
170
171
  return false;
171
172
  }
172
173
  return !this.config.checkOpened || this.config.checkOpened(this.parsedPathData);
@@ -223,9 +224,7 @@ class Route {
223
224
  };
224
225
  const urlCreateParams = this.config.createUrl?.(defaultUrlCreateParams, this.query.data) ?? routeConfig.get().createUrl?.(defaultUrlCreateParams, this.query.data) ?? defaultUrlCreateParams;
225
226
  const path = this._compiler(this.processParams(urlCreateParams.params));
226
- const url = [urlCreateParams.baseUrl, this.isHash ? "#" : "", path].join(
227
- ""
228
- );
227
+ const url = `${urlCreateParams.baseUrl || ""}${this.isHash ? "#" : ""}${path}`;
229
228
  if (outputParams?.omitQuery) {
230
229
  return url;
231
230
  }
@@ -254,59 +253,96 @@ class Route {
254
253
  url = this.createUrl(args[0], query);
255
254
  }
256
255
  const state = rawState ?? null;
257
- const navigationData = {
256
+ const trx = {
258
257
  url,
259
258
  params,
260
259
  replace,
261
260
  state,
262
261
  query
263
262
  };
264
- const feedback = await this.beforeOpen(navigationData);
265
- if (feedback === false) {
263
+ const isConfirmed = await this.confirmOpening(trx);
264
+ if (!isConfirmed) {
266
265
  return;
267
266
  }
268
- if (typeof feedback === "object") {
269
- Object.assign(navigationData, feedback);
270
- }
271
- if (replace) {
272
- this.history.replace(url, state);
267
+ this.skipPathMatchCheck = true;
268
+ if (trx.replace) {
269
+ this.history.replace(trx.url, trx.state);
273
270
  } else {
274
- this.history.push(url, state);
271
+ this.history.push(trx.url, trx.state);
275
272
  }
276
- if (!this.reactionDisposer && this.isOpened) {
277
- this.config.afterOpen?.(this.parsedPathData, this);
273
+ }
274
+ get tokenData() {
275
+ if (!this._tokenData) {
276
+ this._tokenData = pathToRegexp.parse(this.path, this.config.parseOptions);
278
277
  }
278
+ return this._tokenData;
279
279
  }
280
- beforeOpen(openData) {
280
+ async confirmOpening(trx) {
281
+ this.status = "opening";
281
282
  if (this.config.beforeOpen) {
282
- return this.config.beforeOpen(openData);
283
+ const feedback = await this.config.beforeOpen(trx);
284
+ if (feedback === false) {
285
+ mobx.runInAction(() => {
286
+ this.status = "open-rejected";
287
+ });
288
+ return false;
289
+ }
290
+ if (typeof feedback === "object") {
291
+ mobx.runInAction(() => {
292
+ this.status = "open-confirmed";
293
+ });
294
+ return Object.assign(trx, feedback);
295
+ }
283
296
  }
297
+ mobx.runInAction(() => {
298
+ this.status = "open-confirmed";
299
+ });
284
300
  return true;
285
301
  }
286
- afterClose() {
287
- if (this.config.afterClose) {
288
- return this.config.afterClose();
289
- }
302
+ confirmClosing() {
303
+ this.status = "closed";
290
304
  return true;
291
305
  }
292
- get tokenData() {
293
- if (!this._tokenData) {
294
- this._tokenData = pathToRegexp.parse(this.path, this.config.parseOptions);
295
- }
296
- return this._tokenData;
297
- }
298
- firstOpenedStateCheck = true;
299
- processOpenedState = (isOpened) => {
300
- if (this.firstOpenedStateCheck) {
301
- this.firstOpenedStateCheck = false;
302
- if (!isOpened) {
306
+ firstPathMatchingRun = true;
307
+ checkPathMatch = async (isPathMathched) => {
308
+ if (this.firstPathMatchingRun) {
309
+ this.firstPathMatchingRun = false;
310
+ if (!isPathMathched) {
303
311
  return;
304
312
  }
305
313
  }
306
- if (isOpened) {
314
+ if (this.skipPathMatchCheck) {
315
+ this.skipPathMatchCheck = false;
316
+ return;
317
+ }
318
+ if (isPathMathched) {
319
+ const trx = {
320
+ url: this.parsedPathData.path,
321
+ params: this.parsedPathData.params,
322
+ state: this.history.location.state,
323
+ query: this.query.data
324
+ };
325
+ const nextTrxOrConfirmed = await this.confirmOpening(trx);
326
+ if (!nextTrxOrConfirmed) {
327
+ return;
328
+ }
307
329
  this.config.afterOpen?.(this.parsedPathData, this);
330
+ if (typeof nextTrxOrConfirmed === "object") {
331
+ if (nextTrxOrConfirmed.replace) {
332
+ this.history.replace(
333
+ nextTrxOrConfirmed.url,
334
+ nextTrxOrConfirmed.state
335
+ );
336
+ } else {
337
+ this.history.push(nextTrxOrConfirmed.url, nextTrxOrConfirmed.state);
338
+ }
339
+ }
340
+ return;
308
341
  } else {
309
- this.config.afterClose?.();
342
+ const isConfirmed = this.confirmClosing();
343
+ if (isConfirmed) {
344
+ this.config.afterClose?.();
345
+ }
310
346
  }
311
347
  };
312
348
  get isAbleToMergeQuery() {
package/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/core/config/config.ts","../src/core/route/route.ts","../src/core/route-group/route-group.ts","../src/core/router/router.ts","../src/core/utils/is-route-entity.ts","../src/core/virtual-route/virtual-route.ts"],"sourcesContent":["import {\n createBrowserHistory,\n type History,\n type IQueryParams,\n isObservableHistory,\n QueryParams,\n} from 'mobx-location-history';\nimport { createGlobalDynamicConfig } from 'yummies/complex';\n\nimport type { RouteGlobalConfig } from './config.types.js';\n\nlet localHistory: History | undefined;\n\nexport const routeConfig = createGlobalDynamicConfig<RouteGlobalConfig>(\n (update) => {\n if (localHistory && update?.history && isObservableHistory(localHistory)) {\n localHistory.destroy();\n }\n\n let history: History;\n\n if (update?.history) {\n history = update.history;\n } else {\n history = localHistory = createBrowserHistory();\n }\n\n let queryParams: IQueryParams;\n\n if (update?.history && !update.queryParams) {\n queryParams = new QueryParams({ history });\n } else {\n if (update?.queryParams) {\n queryParams = update.queryParams;\n } else {\n queryParams = new QueryParams({ history });\n }\n }\n\n return {\n ...update,\n history,\n location,\n queryParams,\n };\n },\n);\n","import { LinkedAbortController } from 'linked-abort-controller';\nimport {\n action,\n computed,\n makeObservable,\n observable,\n onBecomeObserved,\n onBecomeUnobserved,\n reaction,\n} from 'mobx';\nimport {\n buildSearchString,\n type History,\n type IQueryParams,\n} from 'mobx-location-history';\nimport {\n compile,\n match,\n type ParamData,\n parse,\n type TokenData,\n} from 'path-to-regexp';\nimport type { AnyObject, IsPartial, Maybe, MaybePromise } from 'yummies/types';\n\nimport { routeConfig } from '../config/index.js';\n\nimport type {\n AnyRoute,\n BeforeOpenFeedback,\n CreatedUrlOutputParams,\n InputPathParams,\n IRoute,\n ParsedPathData,\n ParsedPathParams,\n PreparedNavigationData,\n RouteConfiguration,\n RouteNavigateParams,\n UrlCreateParams,\n} from './route.types.js';\n\n/**\n * Class for creating path based route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html)\n */\nexport class Route<\n TPath extends string,\n TInputParams extends InputPathParams<TPath> = InputPathParams<TPath>,\n TOutputParams extends AnyObject = ParsedPathParams<TPath>,\n TParentRoute extends Route<any, any, any, any> | null = null,\n> implements IRoute<TPath, TInputParams, TOutputParams>\n{\n protected abortController: AbortController;\n protected history: History;\n parent: TParentRoute;\n\n query: IQueryParams;\n\n private _tokenData: TokenData | undefined;\n private _matcher?: ReturnType<typeof match>;\n private _compiler?: ReturnType<typeof compile>;\n private reactionDisposer: Maybe<VoidFunction>;\n\n meta?: AnyObject;\n\n /**\n * Indicates if this route is an index route. Index routes activate when parent route path matches exactly.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#isindex-boolean)\n */\n isIndex: boolean;\n\n /**\n * Indicates if this route is an hash route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#ishash-boolean)\n */\n isHash: boolean;\n\n children: AnyRoute[] = [];\n\n constructor(\n public path: TPath,\n protected config: RouteConfiguration<\n TPath,\n TInputParams,\n TOutputParams,\n TParentRoute\n > = {},\n ) {\n this.abortController = new LinkedAbortController(config.abortSignal);\n this.history = config.history ?? routeConfig.get().history;\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n this.isIndex = !!this.config.index;\n this.isHash = !!this.config.hash;\n this.meta = this.config.meta;\n this.parent = config.parent ?? (null as unknown as TParentRoute);\n\n computed.struct(this, 'isOpened');\n computed.struct(this, 'data');\n computed.struct(this, 'params');\n computed.struct(this, 'currentPath');\n computed.struct(this, 'hasOpenedChildren');\n computed.struct(this, 'isAbleToMergeQuery');\n computed(this, 'baseUrl');\n\n observable(this, 'children');\n observable.ref(this, 'parent');\n action(this, 'addChildren');\n action(this, 'removeChildren');\n\n makeObservable(this);\n\n onBecomeObserved(this, 'isOpened', () => {\n if (!config.afterOpen && !config.afterClose) {\n return;\n }\n\n this.reactionDisposer = reaction(\n () => this.isOpened,\n this.processOpenedState,\n {\n signal: this.abortController.signal,\n fireImmediately: true,\n },\n );\n });\n onBecomeUnobserved(this, 'isOpened', () => {\n this.reactionDisposer?.();\n this.reactionDisposer = undefined;\n });\n }\n\n protected get baseUrl() {\n const baseUrl = this.config.baseUrl ?? routeConfig.get().baseUrl;\n return baseUrl?.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n }\n\n protected get parsedPathData(): ParsedPathData<TPath> | null {\n let pathnameToCheck: string;\n\n if (this.isHash) {\n pathnameToCheck = this.history.location.hash.slice(1);\n } else {\n pathnameToCheck = this.history.location.pathname;\n }\n\n if (this.baseUrl) {\n if (!this.history.location.pathname.startsWith(this.baseUrl)) {\n return null;\n }\n\n pathnameToCheck = pathnameToCheck.replace(this.baseUrl, '');\n }\n\n if (\n (this.path === '' || this.path === '/') &&\n (pathnameToCheck === '/' || pathnameToCheck === '')\n ) {\n return { params: {} as any, path: pathnameToCheck };\n }\n\n this._matcher ??= match(this.tokenData, {\n end: false,\n ...this.config.matchOptions,\n });\n const parsed = this._matcher(pathnameToCheck);\n\n if (parsed === false) {\n return null;\n }\n\n return parsed as ParsedPathData<TPath>;\n }\n\n /**\n * Matched path segment for current URL.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#currentpath-parsedpathname-null)\n */\n get currentPath(): string | null {\n return this.parsedPathData?.path ?? null;\n }\n\n /**\n * Current parsed path parameters.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#params-parsedpathparams-null)\n */\n get params(): TOutputParams | null {\n if (!this.parsedPathData?.params) {\n return null;\n }\n\n let params: TOutputParams | null =\n (this.parsedPathData?.params as unknown as Maybe<TOutputParams>) ?? null;\n\n if (this.config.params) {\n const result = this.config.params(\n this.parsedPathData.params,\n this.config.meta,\n );\n if (result) {\n params = result;\n } else {\n return null;\n }\n }\n\n return params;\n }\n\n /**\n * Defines the \"open\" state for this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#isopened-boolean)\n */\n get isOpened() {\n if (this.params === null || this.parsedPathData === null) {\n return false;\n }\n\n return (\n !this.config.checkOpened || this.config.checkOpened(this.parsedPathData)\n );\n }\n\n /**\n * Allows to create child route based on this route with merging this route path and extending path.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#extend-path-config-route)\n */\n extend<\n TExtendedPath extends string,\n TExtendedInputParams extends\n InputPathParams<`${TPath}${TExtendedPath}`> = InputPathParams<`${TPath}${TExtendedPath}`>,\n TExtendedOutputParams extends AnyObject = TInputParams &\n ParsedPathParams<`${TPath}${TExtendedPath}`>,\n >(\n path: TExtendedPath,\n config?: Omit<\n RouteConfiguration<\n `${TPath}${TExtendedPath}`,\n TInputParams & TExtendedInputParams,\n TExtendedOutputParams,\n any\n >,\n 'parent'\n >,\n ) {\n type ExtendedRoutePath = `${TPath}${TExtendedPath}`;\n type ParentRoute = this;\n // biome-ignore lint/correctness/noUnusedVariables: this is need to extract unused fields\n const { index, params, ...configFromCurrentRoute } = this.config;\n\n const extendedChild = new Route<\n ExtendedRoutePath,\n TInputParams & TExtendedInputParams,\n TExtendedOutputParams,\n ParentRoute\n >(`${this.path}${path}`, {\n ...configFromCurrentRoute,\n ...config,\n parent: this,\n } as any);\n\n this.addChildren(extendedChild as any);\n\n return extendedChild;\n }\n\n addChildren(...routes: AnyRoute[]) {\n this.children.push(...routes);\n }\n\n removeChildren(...routes: AnyRoute[]) {\n this.children = this.children.filter((child) => !routes.includes(child));\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#hasopenedchildren-boolean)\n */\n get hasOpenedChildren(): boolean {\n return this.children.some(\n (child) => child.isOpened || child.hasOpenedChildren,\n );\n }\n\n protected processParams(\n params?: TInputParams | null | undefined,\n ): ParamData | undefined {\n if (params == null) return undefined;\n\n return Object.entries(params).reduce((acc, [key, value]) => {\n if (value != null) {\n acc[key] = Array.isArray(value) ? value.map(String) : String(value);\n }\n return acc;\n }, {} as ParamData);\n }\n\n createUrl(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: Maybe<TInputParams>,\n query?: Maybe<AnyObject>,\n mergeQueryOrParams?: boolean | CreatedUrlOutputParams,\n ]\n : [\n params: TInputParams,\n query?: Maybe<AnyObject>,\n mergeQueryOrParams?: boolean | CreatedUrlOutputParams,\n ]\n ) {\n const params = args[0];\n const rawQuery = args[1];\n const mergeQueryOrOutputParams = args[2] ?? this.isAbleToMergeQuery;\n const outputParams: Maybe<CreatedUrlOutputParams> =\n typeof mergeQueryOrOutputParams === 'boolean'\n ? { mergeQuery: mergeQueryOrOutputParams }\n : mergeQueryOrOutputParams;\n\n const query = outputParams?.mergeQuery\n ? { ...this.query.data, ...rawQuery }\n : (rawQuery ?? {});\n\n this._compiler ??= compile(this.tokenData);\n\n const defaultUrlCreateParams: UrlCreateParams<TInputParams> = {\n baseUrl: this.baseUrl,\n params: params as TInputParams,\n query,\n };\n const urlCreateParams: UrlCreateParams<TInputParams> =\n this.config.createUrl?.(defaultUrlCreateParams, this.query.data) ??\n routeConfig.get().createUrl?.(defaultUrlCreateParams, this.query.data) ??\n defaultUrlCreateParams;\n\n const path = this._compiler(this.processParams(urlCreateParams.params));\n\n const url = [urlCreateParams.baseUrl, this.isHash ? '#' : '', path].join(\n '',\n );\n\n if (outputParams?.omitQuery) {\n return url;\n }\n\n return `${url}${buildSearchString(urlCreateParams.query)}`;\n }\n\n /**\n * Navigates to this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#open-args)\n */\n open(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: TInputParams | null | undefined,\n navigateParams?: RouteNavigateParams,\n ]\n : [params: TInputParams, navigateParams?: RouteNavigateParams]\n ): Promise<void>;\n open(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: TInputParams | null | undefined,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ]\n : [\n params: TInputParams,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ]\n ): Promise<void>;\n open(url: string, navigateParams?: RouteNavigateParams): Promise<void>;\n open(\n url: string,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ): Promise<void>;\n\n /**\n * Navigates to this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#open-args)\n */\n async open(...args: any[]) {\n const {\n replace,\n state: rawState,\n query: rawQuery,\n mergeQuery: rawMergeQuery,\n } = typeof args[1] === 'boolean' || args.length > 2\n ? ({ replace: args[1], query: args[2] } as RouteNavigateParams)\n : ((args[1] ?? {}) as RouteNavigateParams);\n let url: string;\n let params: Maybe<InputPathParams<TPath>>;\n\n const mergeQuery = rawMergeQuery ?? this.isAbleToMergeQuery;\n const query = mergeQuery ? { ...this.query.data, ...rawQuery } : rawQuery;\n\n if (typeof args[0] === 'string') {\n url = args[0];\n } else {\n params = args[0] as InputPathParams<TPath>;\n url = this.createUrl(args[0], query);\n }\n\n const state = rawState ?? null;\n\n const navigationData: PreparedNavigationData<TInputParams> = {\n url,\n params: params as TInputParams,\n replace,\n state,\n query,\n };\n\n const feedback = await this.beforeOpen(navigationData);\n\n if (feedback === false) {\n return;\n }\n\n if (typeof feedback === 'object') {\n Object.assign(navigationData, feedback);\n }\n\n if (replace) {\n this.history.replace(url, state);\n } else {\n this.history.push(url, state);\n }\n\n if (!this.reactionDisposer && this.isOpened) {\n this.config.afterOpen?.(this.parsedPathData!, this);\n }\n }\n\n protected beforeOpen(\n openData: PreparedNavigationData<TInputParams>,\n ): MaybePromise<BeforeOpenFeedback> {\n if (this.config.beforeOpen) {\n return this.config.beforeOpen(openData);\n }\n\n return true;\n }\n\n protected afterClose() {\n if (this.config.afterClose) {\n return this.config.afterClose();\n }\n\n return true;\n }\n\n protected get tokenData() {\n if (!this._tokenData) {\n this._tokenData = parse(this.path, this.config.parseOptions);\n }\n return this._tokenData;\n }\n\n private firstOpenedStateCheck = true;\n private processOpenedState = (isOpened: boolean) => {\n if (this.firstOpenedStateCheck) {\n this.firstOpenedStateCheck = false;\n // ignore first 'afterClose' callback call\n if (!isOpened) {\n return;\n }\n }\n\n if (isOpened) {\n this.config.afterOpen?.(this.parsedPathData!, this);\n } else {\n this.config.afterClose?.();\n }\n };\n\n private get isAbleToMergeQuery() {\n return this.config.mergeQuery ?? routeConfig.get().mergeQuery;\n }\n\n destroy() {\n this.abortController.abort();\n }\n}\n\nexport const createRoute = <\n TPath extends string,\n TInputParams extends InputPathParams<TPath> = InputPathParams<TPath>,\n TOutputParams extends AnyObject = ParsedPathParams<TPath>,\n TParentRoute extends Route<any, any, any, any> | null = null,\n>(\n path: TPath,\n config?: RouteConfiguration<TPath, TInputParams, TOutputParams, TParentRoute>,\n) => new Route<TPath, TInputParams, TOutputParams, TParentRoute>(path, config);\n","import { computed, makeObservable, observable } from 'mobx';\n\nimport type {\n AbstractRouteGroup,\n AnyRouteEntity,\n RoutesCollection,\n} from './route-group.types.js';\n\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/**\n * Class for grouping related routes and managing their state.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html)\n */\nexport class RouteGroup<TRoutesCollection extends RoutesCollection>\n implements AbstractRouteGroup<TRoutesCollection>\n{\n routes: TRoutesCollection;\n\n constructor(\n routes: TRoutesCollection,\n private _indexRoute?: AnyRouteEntity,\n ) {\n this.routes = routes;\n\n computed.struct(this, 'isOpened');\n computed.struct(this, 'indexRoute');\n observable.shallow(this, 'routes');\n makeObservable(this);\n }\n\n /**\n * Returns true if at least one route in the group is open.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#isopened-boolean)\n */\n get isOpened(): boolean {\n const routes = Object.values(this.routes);\n return routes.some(\n (route) =>\n route.isOpened ||\n ('hasOpenedChildren' in route && route.hasOpenedChildren),\n );\n }\n\n /**\n * First found index route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#indexroute-route-undefined)\n */\n get indexRoute(): AnyRouteEntity | undefined {\n return (this._indexRoute ??\n Object.values(this.routes).find(\n (route) => 'isIndex' in route && route.isIndex,\n )) as unknown as AnyRouteEntity;\n }\n\n /**\n * Main navigation method for the group.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#open-args-any-void)\n */\n open(...args: any[]) {\n let lastGroupRoute: RouteGroup<any> | undefined;\n\n if (this.indexRoute && 'open' in this.indexRoute) {\n this.indexRoute.open(...args);\n return;\n }\n\n for (const routeName in this.routes) {\n const route = this.routes[routeName];\n if (route instanceof RouteGroup) {\n lastGroupRoute = route;\n }\n }\n\n if (lastGroupRoute) {\n lastGroupRoute.open(...args);\n } else if (process.env.NODE_ENV !== 'production') {\n console.warn(\n \"RouteGroup doesn't have index route. open() method doesn't work.\",\n );\n }\n }\n}\n\nexport const createRouteGroup = <TRoutesCollection extends RoutesCollection>(\n routes: TRoutesCollection,\n indexRoute?: AnyRouteEntity,\n) => new RouteGroup<TRoutesCollection>(routes, indexRoute);\n","import { computed, makeObservable } from 'mobx';\nimport {\n buildSearchString,\n type History,\n type IQueryParams,\n} from 'mobx-location-history';\n\nimport { routeConfig } from '../config/index.js';\nimport type { RoutesCollection } from '../route-group/index.js';\n\nimport type {\n RouterConfiguration,\n RouterNavigateOptions,\n} from './router.types.js';\n\n/**\n * Class for centralized routing management.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Router.html)\n */\nexport class Router<TRoutesCollection extends RoutesCollection> {\n routes: TRoutesCollection;\n history: History;\n query: IQueryParams;\n\n constructor(config: RouterConfiguration<TRoutesCollection>) {\n this.routes = config.routes;\n this.history = config.history ?? routeConfig.get().history;\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n\n computed.struct(this, 'location');\n\n makeObservable(this);\n }\n\n get location() {\n return this.history.location;\n }\n\n navigate(url: string, options?: RouterNavigateOptions) {\n const query =\n (options?.mergeQuery ?? routeConfig.get().mergeQuery)\n ? { ...this.query.data, ...options?.query }\n : { ...options?.query };\n\n const searchString = buildSearchString(query);\n const navigationUrl = `${url}${searchString}`;\n\n if (options?.replace) {\n this.history.replace(navigationUrl, options?.state);\n } else {\n this.history.push(navigationUrl, options?.state);\n }\n }\n}\n\nexport const createRouter = <TRoutesCollection extends RoutesCollection>(\n config: RouterConfiguration<TRoutesCollection>,\n) => new Router(config);\n","import type { AnyRouteEntity } from '../route-group/index.js';\n\nexport const isRouteEntity = (route: any): route is AnyRouteEntity =>\n route && 'isOpened' in route;\n","import { LinkedAbortController } from 'linked-abort-controller';\nimport {\n action,\n computed,\n makeObservable,\n observable,\n onBecomeObserved,\n onBecomeUnobserved,\n reaction,\n runInAction,\n} from 'mobx';\nimport type { IQueryParams } from 'mobx-location-history';\nimport { callFunction } from 'yummies/common';\nimport type { AnyObject, EmptyObject, IsPartial, Maybe } from 'yummies/types';\n\nimport { routeConfig } from '../config/index.js';\n\nimport type {\n AbstractVirtualRoute,\n VirtualOpenExtraParams,\n VirtualRouteConfiguration,\n} from './virtual-route.types.js';\n\n/**\n * Class for creating routes with custom activation logic\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html)\n */\nexport class VirtualRoute<TParams extends AnyObject | EmptyObject = EmptyObject>\n implements AbstractVirtualRoute<TParams>\n{\n protected abortController: AbortController;\n query: IQueryParams;\n params: TParams | null;\n\n private isLocalOpened: boolean;\n\n private openChecker: Maybe<VirtualRouteConfiguration<TParams>['checkOpened']>;\n private reactionDisposer: Maybe<VoidFunction>;\n\n constructor(protected config: VirtualRouteConfiguration<TParams> = {}) {\n this.abortController = new LinkedAbortController(config.abortSignal);\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n this.params = callFunction(config.initialParams, this) ?? null;\n this.openChecker = config.checkOpened;\n this.isLocalOpened = this.openChecker?.(this) ?? false;\n\n observable(this, 'params');\n observable.ref(this, 'isLocalOpened');\n observable.ref(this, '_isOpened');\n computed.struct(this, 'isOpened');\n action(this, 'setOpenChecker');\n action(this, 'open');\n action(this, 'close');\n makeObservable(this);\n\n onBecomeObserved(this, 'isOpened', () => {\n if (!config.afterOpen && !config.afterClose) {\n return;\n }\n\n this.reactionDisposer = reaction(\n () => this.isOpened,\n this.processOpenedState,\n {\n signal: this.abortController.signal,\n fireImmediately: true,\n },\n );\n });\n onBecomeUnobserved(this, 'isOpened', () => {\n this.reactionDisposer?.();\n this.reactionDisposer = undefined;\n });\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#isopened-boolean)\n */\n get isOpened() {\n const isOuterOpened = this.openChecker == null || this.openChecker(this);\n return this.isLocalOpened && isOuterOpened;\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#setopenchecker-openchecker-void)\n */\n setOpenChecker(\n openChecker: Maybe<VirtualRouteConfiguration<TParams>['checkOpened']>,\n ) {\n this.openChecker = openChecker;\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#open-params-extraparams-query-replace-promise-void)\n */\n open(\n ...args: IsPartial<TParams> extends true\n ? [params?: Maybe<TParams>, extraParams?: VirtualOpenExtraParams]\n : [params: TParams, extraParams?: VirtualOpenExtraParams]\n ): Promise<void>;\n async open(...args: any[]) {\n const params = (args[0] ?? {}) as unknown as TParams;\n const extraParams: Maybe<VirtualOpenExtraParams> = args[1];\n\n if (this.config.beforeOpen) {\n const beforeOpenResult = await this.config.beforeOpen(params, this);\n if (beforeOpenResult === false) {\n return;\n }\n }\n\n if (this.config.open == null) {\n runInAction(() => {\n this.isLocalOpened = true;\n });\n } else {\n const result = await this.config.open(params, this);\n // because result can return void so this is truthy for opening state\n runInAction(() => {\n this.isLocalOpened = result !== false;\n });\n }\n\n if (!this.isLocalOpened) {\n return;\n }\n\n if (extraParams?.query) {\n this.query.update(extraParams.query, extraParams.replace);\n }\n\n runInAction(() => {\n this.params = params;\n });\n\n if (!this.reactionDisposer && this.isOpened) {\n this.config.afterOpen?.(this.params!, this);\n }\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#close-void)\n */\n close() {\n if (this.config.close == null) {\n this.isLocalOpened = false;\n } else {\n const result = this.config.close(this);\n // because result can return void so this is truthy for opening state\n this.isLocalOpened = result !== false;\n }\n\n this.params = null;\n }\n\n private firstOpenedStateCheck = true;\n private processOpenedState = (isOpened: boolean) => {\n if (this.firstOpenedStateCheck) {\n this.firstOpenedStateCheck = false;\n // ignore first 'afterClose' callback call\n if (!isOpened) {\n return;\n }\n }\n\n if (isOpened) {\n this.config.afterOpen?.(this.params!, this);\n } else {\n this.config.afterClose?.();\n }\n };\n\n destroy() {\n this.abortController.abort();\n }\n}\n\nexport const createVirtualRoute = <\n TParams extends AnyObject | EmptyObject = EmptyObject,\n>(\n config?: VirtualRouteConfiguration<TParams>,\n) => new VirtualRoute<TParams>(config);\n"],"names":["createGlobalDynamicConfig","isObservableHistory","createBrowserHistory","QueryParams","LinkedAbortController","computed","observable","action","makeObservable","onBecomeObserved","reaction","onBecomeUnobserved","match","compile","buildSearchString","parse","callFunction","runInAction"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/core/config/config.ts","../src/core/route/route.ts","../src/core/route-group/route-group.ts","../src/core/router/router.ts","../src/core/utils/is-route-entity.ts","../src/core/virtual-route/virtual-route.ts"],"sourcesContent":["import {\n createBrowserHistory,\n type History,\n type IQueryParams,\n isObservableHistory,\n QueryParams,\n} from 'mobx-location-history';\nimport { createGlobalDynamicConfig } from 'yummies/complex';\n\nimport type { RouteGlobalConfig } from './config.types.js';\n\nlet localHistory: History | undefined;\n\nexport const routeConfig = createGlobalDynamicConfig<RouteGlobalConfig>(\n (update) => {\n if (localHistory && update?.history && isObservableHistory(localHistory)) {\n localHistory.destroy();\n }\n\n let history: History;\n\n if (update?.history) {\n history = update.history;\n } else {\n history = localHistory = createBrowserHistory();\n }\n\n let queryParams: IQueryParams;\n\n if (update?.history && !update.queryParams) {\n queryParams = new QueryParams({ history });\n } else {\n if (update?.queryParams) {\n queryParams = update.queryParams;\n } else {\n queryParams = new QueryParams({ history });\n }\n }\n\n return {\n ...update,\n history,\n location,\n queryParams,\n };\n },\n Symbol.for('MOBX_ROUTE_CONFIG'),\n);\n","import { LinkedAbortController } from 'linked-abort-controller';\nimport {\n action,\n computed,\n makeObservable,\n observable,\n reaction,\n runInAction,\n} from 'mobx';\nimport {\n buildSearchString,\n type History,\n type IQueryParams,\n} from 'mobx-location-history';\nimport {\n compile,\n match,\n type ParamData,\n parse,\n type TokenData,\n} from 'path-to-regexp';\nimport type { AnyObject, IsPartial, Maybe } from 'yummies/types';\nimport { routeConfig } from '../config/index.js';\nimport type {\n AnyRoute,\n CreatedUrlOutputParams,\n InputPathParams,\n IRoute,\n NavigationTrx,\n ParsedPathData,\n ParsedPathParams,\n RouteConfiguration,\n RouteNavigateParams,\n UrlCreateParams,\n} from './route.types.js';\n\n/**\n * Class for creating path based route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html)\n */\nexport class Route<\n TPath extends string,\n TInputParams extends InputPathParams<TPath> = InputPathParams<TPath>,\n TOutputParams extends AnyObject = ParsedPathParams<TPath>,\n TParentRoute extends Route<any, any, any, any> | null = null,\n> implements IRoute<TPath, TInputParams, TOutputParams>\n{\n protected abortController: AbortController;\n protected history: History;\n parent: TParentRoute;\n\n query: IQueryParams;\n\n private _tokenData: TokenData | undefined;\n private _matcher?: ReturnType<typeof match>;\n private _compiler?: ReturnType<typeof compile>;\n private skipPathMatchCheck = false;\n\n protected status:\n | 'opening'\n | 'closed'\n | 'open-rejected'\n | 'open-confirmed'\n | 'unknown';\n\n meta?: AnyObject;\n\n /**\n * Indicates if this route is an index route. Index routes activate when parent route path matches exactly.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#isindex-boolean)\n */\n isIndex: boolean;\n\n /**\n * Indicates if this route is an hash route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#ishash-boolean)\n */\n isHash: boolean;\n\n children: AnyRoute[] = [];\n\n constructor(\n public path: TPath,\n protected config: RouteConfiguration<\n TPath,\n TInputParams,\n TOutputParams,\n TParentRoute\n > = {},\n ) {\n this.abortController = new LinkedAbortController(config.abortSignal);\n this.history = config.history ?? routeConfig.get().history;\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n this.isIndex = !!this.config.index;\n this.isHash = !!this.config.hash;\n this.meta = this.config.meta;\n this.status = 'unknown';\n this.parent = config.parent ?? (null as unknown as TParentRoute);\n\n computed(this, 'isPathMatched');\n computed(this, 'isOpened');\n computed.struct(this, 'data');\n computed.struct(this, 'params');\n computed(this, 'currentPath');\n computed(this, 'hasOpenedChildren');\n computed(this, 'isAbleToMergeQuery');\n computed(this, 'baseUrl');\n\n observable(this, 'children');\n observable.ref(this, 'parent');\n observable.ref(this, 'status');\n computed(this, 'isOpening');\n action(this, 'addChildren');\n action(this, 'confirmOpening');\n action(this, 'confirmClosing');\n action(this, 'removeChildren');\n\n makeObservable(this);\n\n reaction(() => this.isPathMatched, this.checkPathMatch, {\n signal: this.abortController.signal,\n fireImmediately: true,\n });\n }\n\n protected get baseUrl() {\n const baseUrl = this.config.baseUrl ?? routeConfig.get().baseUrl;\n return baseUrl?.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n }\n\n protected get parsedPathData(): ParsedPathData<TPath> | null {\n let pathnameToCheck: string;\n\n if (this.isHash) {\n pathnameToCheck = this.history.location.hash.slice(1);\n } else {\n pathnameToCheck = this.history.location.pathname;\n }\n\n if (this.baseUrl) {\n if (!this.history.location.pathname.startsWith(this.baseUrl)) {\n return null;\n }\n\n pathnameToCheck = pathnameToCheck.replace(this.baseUrl, '');\n }\n\n if (\n (this.path === '' || this.path === '/') &&\n (pathnameToCheck === '/' || pathnameToCheck === '')\n ) {\n return { params: {} as any, path: pathnameToCheck };\n }\n\n this._matcher ??= match(this.tokenData, {\n end: this.config.exact ?? false,\n ...this.config.matchOptions,\n });\n const parsed = this._matcher(pathnameToCheck);\n\n if (parsed === false) {\n return null;\n }\n\n return parsed as ParsedPathData<TPath>;\n }\n\n get isOpening() {\n return this.status === 'opening';\n }\n\n /**\n * Matched path segment for current URL.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#currentpath-parsedpathname-null)\n */\n get currentPath(): string | null {\n return this.parsedPathData?.path ?? null;\n }\n\n /**\n * Current parsed path parameters.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#params-parsedpathparams-null)\n */\n get params(): TOutputParams | null {\n if (!this.parsedPathData?.params) {\n return null;\n }\n\n let params: TOutputParams | null =\n (this.parsedPathData?.params as unknown as Maybe<TOutputParams>) ?? null;\n\n if (this.config.params) {\n const result = this.config.params(\n this.parsedPathData.params,\n this.config.meta,\n );\n if (result) {\n params = result;\n } else {\n return null;\n }\n }\n\n return params;\n }\n\n protected get isPathMatched() {\n return this.parsedPathData !== null;\n }\n\n /**\n * Defines the \"open\" state for this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#isopened-boolean)\n */\n get isOpened() {\n if (\n !this.isPathMatched ||\n this.params === null ||\n this.status !== 'open-confirmed'\n ) {\n return false;\n }\n\n return (\n !this.config.checkOpened || this.config.checkOpened(this.parsedPathData!)\n );\n }\n\n /**\n * Allows to create child route based on this route with merging this route path and extending path.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#extend-path-config-route)\n */\n extend<\n TExtendedPath extends string,\n TExtendedInputParams extends\n InputPathParams<`${TPath}${TExtendedPath}`> = InputPathParams<`${TPath}${TExtendedPath}`>,\n TExtendedOutputParams extends AnyObject = TInputParams &\n ParsedPathParams<`${TPath}${TExtendedPath}`>,\n >(\n path: TExtendedPath,\n config?: Omit<\n RouteConfiguration<\n `${TPath}${TExtendedPath}`,\n TInputParams & TExtendedInputParams,\n TExtendedOutputParams,\n any\n >,\n 'parent'\n >,\n ) {\n type ExtendedRoutePath = `${TPath}${TExtendedPath}`;\n type ParentRoute = this;\n // biome-ignore lint/correctness/noUnusedVariables: this is need to extract unused fields\n const { index, params, ...configFromCurrentRoute } = this.config;\n\n const extendedChild = new Route<\n ExtendedRoutePath,\n TInputParams & TExtendedInputParams,\n TExtendedOutputParams,\n ParentRoute\n >(`${this.path}${path}`, {\n ...configFromCurrentRoute,\n ...config,\n parent: this,\n } as any);\n\n this.addChildren(extendedChild as any);\n\n return extendedChild;\n }\n\n addChildren(...routes: AnyRoute[]) {\n this.children.push(...routes);\n }\n\n removeChildren(...routes: AnyRoute[]) {\n this.children = this.children.filter((child) => !routes.includes(child));\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#hasopenedchildren-boolean)\n */\n get hasOpenedChildren(): boolean {\n return this.children.some(\n (child) => child.isOpened || child.hasOpenedChildren,\n );\n }\n\n protected processParams(\n params?: TInputParams | null | undefined,\n ): ParamData | undefined {\n if (params == null) return undefined;\n\n return Object.entries(params).reduce((acc, [key, value]) => {\n if (value != null) {\n acc[key] = Array.isArray(value) ? value.map(String) : String(value);\n }\n return acc;\n }, {} as ParamData);\n }\n\n createUrl(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: Maybe<TInputParams>,\n query?: Maybe<AnyObject>,\n mergeQueryOrParams?: boolean | CreatedUrlOutputParams,\n ]\n : [\n params: TInputParams,\n query?: Maybe<AnyObject>,\n mergeQueryOrParams?: boolean | CreatedUrlOutputParams,\n ]\n ) {\n const params = args[0];\n const rawQuery = args[1];\n const mergeQueryOrOutputParams = args[2] ?? this.isAbleToMergeQuery;\n const outputParams: Maybe<CreatedUrlOutputParams> =\n typeof mergeQueryOrOutputParams === 'boolean'\n ? { mergeQuery: mergeQueryOrOutputParams }\n : mergeQueryOrOutputParams;\n\n const query = outputParams?.mergeQuery\n ? { ...this.query.data, ...rawQuery }\n : (rawQuery ?? {});\n\n this._compiler ??= compile(this.tokenData);\n\n const defaultUrlCreateParams: UrlCreateParams<TInputParams> = {\n baseUrl: this.baseUrl,\n params: params as TInputParams,\n query,\n };\n const urlCreateParams: UrlCreateParams<TInputParams> =\n this.config.createUrl?.(defaultUrlCreateParams, this.query.data) ??\n routeConfig.get().createUrl?.(defaultUrlCreateParams, this.query.data) ??\n defaultUrlCreateParams;\n\n const path = this._compiler(this.processParams(urlCreateParams.params));\n\n const url = `${urlCreateParams.baseUrl || ''}${this.isHash ? '#' : ''}${path}`;\n\n if (outputParams?.omitQuery) {\n return url;\n }\n\n return `${url}${buildSearchString(urlCreateParams.query)}`;\n }\n\n /**\n * Navigates to this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#open-args)\n */\n open(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: TInputParams | null | undefined,\n navigateParams?: RouteNavigateParams,\n ]\n : [params: TInputParams, navigateParams?: RouteNavigateParams]\n ): Promise<void>;\n open(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: TInputParams | null | undefined,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ]\n : [\n params: TInputParams,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ]\n ): Promise<void>;\n open(url: string, navigateParams?: RouteNavigateParams): Promise<void>;\n open(\n url: string,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ): Promise<void>;\n\n /**\n * Navigates to this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#open-args)\n */\n async open(...args: any[]) {\n const {\n replace,\n state: rawState,\n query: rawQuery,\n mergeQuery: rawMergeQuery,\n } = typeof args[1] === 'boolean' || args.length > 2\n ? ({ replace: args[1], query: args[2] } as RouteNavigateParams)\n : ((args[1] ?? {}) as RouteNavigateParams);\n let url: string;\n let params: Maybe<InputPathParams<TPath>>;\n\n const mergeQuery = rawMergeQuery ?? this.isAbleToMergeQuery;\n const query = mergeQuery ? { ...this.query.data, ...rawQuery } : rawQuery;\n\n if (typeof args[0] === 'string') {\n url = args[0];\n } else {\n params = args[0] as InputPathParams<TPath>;\n url = this.createUrl(args[0], query);\n }\n\n const state = rawState ?? null;\n\n const trx: NavigationTrx<TInputParams> = {\n url,\n params: params as TInputParams,\n replace,\n state,\n query,\n };\n\n const isConfirmed = await this.confirmOpening(trx);\n\n if (!isConfirmed) {\n return;\n }\n\n this.skipPathMatchCheck = true;\n\n if (trx.replace) {\n this.history.replace(trx.url, trx.state);\n } else {\n this.history.push(trx.url, trx.state);\n }\n }\n\n protected get tokenData() {\n if (!this._tokenData) {\n this._tokenData = parse(this.path, this.config.parseOptions);\n }\n return this._tokenData;\n }\n\n protected async confirmOpening(trx: NavigationTrx<TInputParams>) {\n this.status = 'opening';\n\n if (this.config.beforeOpen) {\n const feedback = await this.config.beforeOpen(trx);\n\n if (feedback === false) {\n runInAction(() => {\n this.status = 'open-rejected';\n });\n return false;\n }\n\n if (typeof feedback === 'object') {\n runInAction(() => {\n this.status = 'open-confirmed';\n });\n\n return Object.assign(trx, feedback);\n }\n }\n\n runInAction(() => {\n this.status = 'open-confirmed';\n });\n\n return true;\n }\n\n protected confirmClosing() {\n this.status = 'closed';\n return true;\n }\n\n private firstPathMatchingRun = true;\n\n private checkPathMatch = async (isPathMathched: boolean) => {\n if (this.firstPathMatchingRun) {\n this.firstPathMatchingRun = false;\n // ignore first 'afterClose' callback call\n if (!isPathMathched) {\n return;\n }\n }\n\n if (this.skipPathMatchCheck) {\n // after open\n this.skipPathMatchCheck = false;\n return;\n }\n\n if (isPathMathched) {\n const trx: NavigationTrx<TInputParams> = {\n url: this.parsedPathData!.path,\n params: this.parsedPathData!.params as TInputParams,\n state: this.history.location.state,\n query: this.query.data,\n };\n\n const nextTrxOrConfirmed = await this.confirmOpening(trx);\n\n if (!nextTrxOrConfirmed) {\n return;\n }\n\n this.config.afterOpen?.(this.parsedPathData!, this);\n\n if (typeof nextTrxOrConfirmed === 'object') {\n if (nextTrxOrConfirmed.replace) {\n this.history.replace(\n nextTrxOrConfirmed.url,\n nextTrxOrConfirmed.state,\n );\n } else {\n this.history.push(nextTrxOrConfirmed.url, nextTrxOrConfirmed.state);\n }\n }\n\n return;\n } else {\n const isConfirmed = this.confirmClosing();\n\n if (isConfirmed) {\n this.config.afterClose?.();\n }\n }\n };\n\n private get isAbleToMergeQuery() {\n return this.config.mergeQuery ?? routeConfig.get().mergeQuery;\n }\n\n destroy() {\n this.abortController.abort();\n }\n}\n\nexport const createRoute = <\n TPath extends string,\n TInputParams extends InputPathParams<TPath> = InputPathParams<TPath>,\n TOutputParams extends AnyObject = ParsedPathParams<TPath>,\n TParentRoute extends Route<any, any, any, any> | null = null,\n>(\n path: TPath,\n config?: RouteConfiguration<TPath, TInputParams, TOutputParams, TParentRoute>,\n) => new Route<TPath, TInputParams, TOutputParams, TParentRoute>(path, config);\n","import { computed, makeObservable, observable } from 'mobx';\n\nimport type {\n AbstractRouteGroup,\n AnyRouteEntity,\n RoutesCollection,\n} from './route-group.types.js';\n\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/**\n * Class for grouping related routes and managing their state.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html)\n */\nexport class RouteGroup<TRoutesCollection extends RoutesCollection>\n implements AbstractRouteGroup<TRoutesCollection>\n{\n routes: TRoutesCollection;\n\n constructor(\n routes: TRoutesCollection,\n private _indexRoute?: AnyRouteEntity,\n ) {\n this.routes = routes;\n\n computed.struct(this, 'isOpened');\n computed.struct(this, 'indexRoute');\n observable.shallow(this, 'routes');\n makeObservable(this);\n }\n\n /**\n * Returns true if at least one route in the group is open.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#isopened-boolean)\n */\n get isOpened(): boolean {\n const routes = Object.values(this.routes);\n return routes.some(\n (route) =>\n route.isOpened ||\n ('hasOpenedChildren' in route && route.hasOpenedChildren),\n );\n }\n\n /**\n * First found index route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#indexroute-route-undefined)\n */\n get indexRoute(): AnyRouteEntity | undefined {\n return (this._indexRoute ??\n Object.values(this.routes).find(\n (route) => 'isIndex' in route && route.isIndex,\n )) as unknown as AnyRouteEntity;\n }\n\n /**\n * Main navigation method for the group.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#open-args-any-void)\n */\n open(...args: any[]) {\n let lastGroupRoute: RouteGroup<any> | undefined;\n\n if (this.indexRoute && 'open' in this.indexRoute) {\n this.indexRoute.open(...args);\n return;\n }\n\n for (const routeName in this.routes) {\n const route = this.routes[routeName];\n if (route instanceof RouteGroup) {\n lastGroupRoute = route;\n }\n }\n\n if (lastGroupRoute) {\n lastGroupRoute.open(...args);\n } else if (process.env.NODE_ENV !== 'production') {\n console.warn(\n \"RouteGroup doesn't have index route. open() method doesn't work.\",\n );\n }\n }\n}\n\nexport const createRouteGroup = <TRoutesCollection extends RoutesCollection>(\n routes: TRoutesCollection,\n indexRoute?: AnyRouteEntity,\n) => new RouteGroup<TRoutesCollection>(routes, indexRoute);\n","import { computed, makeObservable } from 'mobx';\nimport {\n buildSearchString,\n type History,\n type IQueryParams,\n} from 'mobx-location-history';\n\nimport { routeConfig } from '../config/index.js';\nimport type { RoutesCollection } from '../route-group/index.js';\n\nimport type {\n RouterConfiguration,\n RouterNavigateOptions,\n} from './router.types.js';\n\n/**\n * Class for centralized routing management.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Router.html)\n */\nexport class Router<TRoutesCollection extends RoutesCollection> {\n routes: TRoutesCollection;\n history: History;\n query: IQueryParams;\n\n constructor(config: RouterConfiguration<TRoutesCollection>) {\n this.routes = config.routes;\n this.history = config.history ?? routeConfig.get().history;\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n\n computed.struct(this, 'location');\n\n makeObservable(this);\n }\n\n get location() {\n return this.history.location;\n }\n\n navigate(url: string, options?: RouterNavigateOptions) {\n const query =\n (options?.mergeQuery ?? routeConfig.get().mergeQuery)\n ? { ...this.query.data, ...options?.query }\n : { ...options?.query };\n\n const searchString = buildSearchString(query);\n const navigationUrl = `${url}${searchString}`;\n\n if (options?.replace) {\n this.history.replace(navigationUrl, options?.state);\n } else {\n this.history.push(navigationUrl, options?.state);\n }\n }\n}\n\nexport const createRouter = <TRoutesCollection extends RoutesCollection>(\n config: RouterConfiguration<TRoutesCollection>,\n) => new Router(config);\n","import type { AnyRouteEntity } from '../route-group/index.js';\n\nexport const isRouteEntity = (route: any): route is AnyRouteEntity =>\n route && 'isOpened' in route;\n","import { LinkedAbortController } from 'linked-abort-controller';\nimport {\n action,\n computed,\n makeObservable,\n observable,\n onBecomeObserved,\n onBecomeUnobserved,\n reaction,\n runInAction,\n} from 'mobx';\nimport type { IQueryParams } from 'mobx-location-history';\nimport { callFunction } from 'yummies/common';\nimport type { AnyObject, EmptyObject, IsPartial, Maybe } from 'yummies/types';\n\nimport { routeConfig } from '../config/index.js';\n\nimport type {\n AbstractVirtualRoute,\n VirtualOpenExtraParams,\n VirtualRouteConfiguration,\n} from './virtual-route.types.js';\n\n/**\n * Class for creating routes with custom activation logic\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html)\n */\nexport class VirtualRoute<TParams extends AnyObject | EmptyObject = EmptyObject>\n implements AbstractVirtualRoute<TParams>\n{\n protected abortController: AbortController;\n query: IQueryParams;\n params: TParams | null;\n\n private isLocalOpened: boolean;\n\n private openChecker: Maybe<VirtualRouteConfiguration<TParams>['checkOpened']>;\n private reactionDisposer: Maybe<VoidFunction>;\n\n constructor(protected config: VirtualRouteConfiguration<TParams> = {}) {\n this.abortController = new LinkedAbortController(config.abortSignal);\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n this.params = callFunction(config.initialParams, this) ?? null;\n this.openChecker = config.checkOpened;\n this.isLocalOpened = this.openChecker?.(this) ?? false;\n\n observable(this, 'params');\n observable.ref(this, 'isLocalOpened');\n observable.ref(this, '_isOpened');\n computed.struct(this, 'isOpened');\n action(this, 'setOpenChecker');\n action(this, 'open');\n action(this, 'close');\n makeObservable(this);\n\n onBecomeObserved(this, 'isOpened', () => {\n if (!config.afterOpen && !config.afterClose) {\n return;\n }\n\n this.reactionDisposer = reaction(\n () => this.isOpened,\n this.processOpenedState,\n {\n signal: this.abortController.signal,\n fireImmediately: true,\n },\n );\n });\n onBecomeUnobserved(this, 'isOpened', () => {\n this.reactionDisposer?.();\n this.reactionDisposer = undefined;\n });\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#isopened-boolean)\n */\n get isOpened() {\n const isOuterOpened = this.openChecker == null || this.openChecker(this);\n return this.isLocalOpened && isOuterOpened;\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#setopenchecker-openchecker-void)\n */\n setOpenChecker(\n openChecker: Maybe<VirtualRouteConfiguration<TParams>['checkOpened']>,\n ) {\n this.openChecker = openChecker;\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#open-params-extraparams-query-replace-promise-void)\n */\n open(\n ...args: IsPartial<TParams> extends true\n ? [params?: Maybe<TParams>, extraParams?: VirtualOpenExtraParams]\n : [params: TParams, extraParams?: VirtualOpenExtraParams]\n ): Promise<void>;\n async open(...args: any[]) {\n const params = (args[0] ?? {}) as unknown as TParams;\n const extraParams: Maybe<VirtualOpenExtraParams> = args[1];\n\n if (this.config.beforeOpen) {\n const beforeOpenResult = await this.config.beforeOpen(params, this);\n if (beforeOpenResult === false) {\n return;\n }\n }\n\n if (this.config.open == null) {\n runInAction(() => {\n this.isLocalOpened = true;\n });\n } else {\n const result = await this.config.open(params, this);\n // because result can return void so this is truthy for opening state\n runInAction(() => {\n this.isLocalOpened = result !== false;\n });\n }\n\n if (!this.isLocalOpened) {\n return;\n }\n\n if (extraParams?.query) {\n this.query.update(extraParams.query, extraParams.replace);\n }\n\n runInAction(() => {\n this.params = params;\n });\n\n if (!this.reactionDisposer && this.isOpened) {\n this.config.afterOpen?.(this.params!, this);\n }\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#close-void)\n */\n close() {\n if (this.config.close == null) {\n this.isLocalOpened = false;\n } else {\n const result = this.config.close(this);\n // because result can return void so this is truthy for opening state\n this.isLocalOpened = result !== false;\n }\n\n this.params = null;\n }\n\n private firstOpenedStateCheck = true;\n private processOpenedState = (isOpened: boolean) => {\n if (this.firstOpenedStateCheck) {\n this.firstOpenedStateCheck = false;\n // ignore first 'afterClose' callback call\n if (!isOpened) {\n return;\n }\n }\n\n if (isOpened) {\n this.config.afterOpen?.(this.params!, this);\n } else {\n this.config.afterClose?.();\n }\n };\n\n destroy() {\n this.abortController.abort();\n }\n}\n\nexport const createVirtualRoute = <\n TParams extends AnyObject | EmptyObject = EmptyObject,\n>(\n config?: VirtualRouteConfiguration<TParams>,\n) => new VirtualRoute<TParams>(config);\n"],"names":["createGlobalDynamicConfig","isObservableHistory","createBrowserHistory","QueryParams","LinkedAbortController","computed","observable","action","makeObservable","reaction","match","compile","buildSearchString","parse","runInAction","callFunction","onBecomeObserved","onBecomeUnobserved"],"mappings":"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"}
package/index.d.ts CHANGED
@@ -138,7 +138,7 @@ declare class RouteGroup<TRoutesCollection extends RoutesCollection> implements
138
138
  }
139
139
  declare const createRouteGroup: <TRoutesCollection extends RoutesCollection>(routes: TRoutesCollection, indexRoute?: AnyRouteEntity) => RouteGroup<TRoutesCollection>;
140
140
 
141
- type PreparedNavigationData<TParams extends AnyObject = AnyObject> = {
141
+ type NavigationTrx<TParams extends AnyObject = AnyObject> = {
142
142
  state?: any;
143
143
  /**
144
144
  * path parameters
@@ -153,11 +153,7 @@ type PreparedNavigationData<TParams extends AnyObject = AnyObject> = {
153
153
  /**
154
154
  * Returning `false` means ignore navigation
155
155
  */
156
- type BeforeOpenFeedback = void | boolean | {
157
- url: string;
158
- state?: any;
159
- replace?: boolean;
160
- };
156
+ type BeforeOpenFeedback = void | boolean | Pick<NavigationTrx, 'url' | 'state' | 'replace'>;
161
157
  interface UrlCreateParams<TInputParams> {
162
158
  baseUrl?: string | undefined;
163
159
  params: TInputParams;
@@ -175,6 +171,11 @@ interface RouteConfiguration<TPath extends string, TInputParams extends InputPat
175
171
  abortSignal?: AbortSignal;
176
172
  index?: boolean;
177
173
  hash?: boolean;
174
+ /**
175
+ * Exact match for the path
176
+ * @default false
177
+ */
178
+ exact?: boolean;
178
179
  /**
179
180
  * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#meta)
180
181
  */
@@ -194,7 +195,7 @@ interface RouteConfiguration<TPath extends string, TInputParams extends InputPat
194
195
  /**
195
196
  * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#beforeopen)
196
197
  */
197
- beforeOpen?: (preparedNavigationData: PreparedNavigationData<NoInfer<TInputParams>>) => MaybePromise<BeforeOpenFeedback>;
198
+ beforeOpen?: (navigationTransaction: NavigationTrx<NoInfer<TInputParams>>) => MaybePromise<BeforeOpenFeedback>;
198
199
  /**
199
200
  * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#afterclose)
200
201
  */
@@ -211,6 +212,7 @@ interface RouteConfiguration<TPath extends string, TInputParams extends InputPat
211
212
  type AnyRoute = IRoute;
212
213
  interface IRoute<TPath extends string = string, TInputParams extends InputPathParams<TPath> = InputPathParams<TPath>, TOutputParams extends AnyObject = ParsedPathParams<TPath>> {
213
214
  isOpened: boolean;
215
+ isOpening: boolean;
214
216
  path: TPath;
215
217
  /**
216
218
  * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#hasopenedchildren-boolean)
@@ -288,7 +290,8 @@ declare class Route<TPath extends string, TInputParams extends InputPathParams<T
288
290
  private _tokenData;
289
291
  private _matcher?;
290
292
  private _compiler?;
291
- private reactionDisposer;
293
+ private skipPathMatchCheck;
294
+ protected status: 'opening' | 'closed' | 'open-rejected' | 'open-confirmed' | 'unknown';
292
295
  meta?: AnyObject;
293
296
  /**
294
297
  * Indicates if this route is an index route. Index routes activate when parent route path matches exactly.
@@ -306,6 +309,7 @@ declare class Route<TPath extends string, TInputParams extends InputPathParams<T
306
309
  constructor(path: TPath, config?: RouteConfiguration<TPath, TInputParams, TOutputParams, TParentRoute>);
307
310
  protected get baseUrl(): string | undefined;
308
311
  protected get parsedPathData(): ParsedPathData<TPath> | null;
312
+ get isOpening(): boolean;
309
313
  /**
310
314
  * Matched path segment for current URL.
311
315
  *
@@ -318,6 +322,7 @@ declare class Route<TPath extends string, TInputParams extends InputPathParams<T
318
322
  * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#params-parsedpathparams-null)
319
323
  */
320
324
  get params(): TOutputParams | null;
325
+ protected get isPathMatched(): boolean;
321
326
  /**
322
327
  * Defines the "open" state for this route.
323
328
  *
@@ -366,11 +371,11 @@ declare class Route<TPath extends string, TInputParams extends InputPathParams<T
366
371
  ]): Promise<void>;
367
372
  open(url: string, navigateParams?: RouteNavigateParams): Promise<void>;
368
373
  open(url: string, replace?: RouteNavigateParams['replace'], query?: RouteNavigateParams['query']): Promise<void>;
369
- protected beforeOpen(openData: PreparedNavigationData<TInputParams>): MaybePromise<BeforeOpenFeedback>;
370
- protected afterClose(): true | void;
371
374
  protected get tokenData(): TokenData;
372
- private firstOpenedStateCheck;
373
- private processOpenedState;
375
+ protected confirmOpening(trx: NavigationTrx<TInputParams>): Promise<boolean | (NavigationTrx<TInputParams> & Pick<NavigationTrx<AnyObject>, "url" | "state" | "replace">)>;
376
+ protected confirmClosing(): boolean;
377
+ private firstPathMatchingRun;
378
+ private checkPathMatch;
374
379
  private get isAbleToMergeQuery();
375
380
  destroy(): void;
376
381
  }
@@ -418,4 +423,4 @@ declare const createRouter: <TRoutesCollection extends RoutesCollection>(config:
418
423
  declare const isRouteEntity: (route: any) => route is AnyRouteEntity;
419
424
 
420
425
  export { Route, RouteGroup, Router, VirtualRoute, createRoute, createRouteGroup, createRouter, createVirtualRoute, isRouteEntity, routeConfig };
421
- export type { AbstractRouteGroup, AbstractVirtualRoute, AnyAbstractRoute, AnyAbstractRouteEntity, AnyRoute, AnyRouteEntity, AnyRouteGroup, AnyRouter, AnyVirtualRoute, BeforeOpenFeedback, CreatedUrlOutputParams, IRoute, InferInputParams, InferParams, InferPath, InputPathParam, InputPathParams, ParsedPathData, ParsedPathParam, ParsedPathParams, PathToObject, PreparedNavigationData, RouteConfiguration, RouteGlobalConfig, RouteNavigateParams, RouteParams, RouterConfiguration, RouterNavigateOptions, RoutesArrayCollection, RoutesCollection, RoutesObjectCollection, UrlCreateParams, UrlCreateParamsFn, VirtualOpenExtraParams, VirtualRouteConfiguration };
426
+ export type { AbstractRouteGroup, AbstractVirtualRoute, AnyAbstractRoute, AnyAbstractRouteEntity, AnyRoute, AnyRouteEntity, AnyRouteGroup, AnyRouter, AnyVirtualRoute, BeforeOpenFeedback, CreatedUrlOutputParams, IRoute, InferInputParams, InferParams, InferPath, InputPathParam, InputPathParams, NavigationTrx, ParsedPathData, ParsedPathParam, ParsedPathParams, PathToObject, RouteConfiguration, RouteGlobalConfig, RouteNavigateParams, RouteParams, RouterConfiguration, RouterNavigateOptions, RoutesArrayCollection, RoutesCollection, RoutesObjectCollection, UrlCreateParams, UrlCreateParamsFn, VirtualOpenExtraParams, VirtualRouteConfiguration };
package/index.js CHANGED
@@ -2,7 +2,7 @@ import { createBrowserHistory, QueryParams, isObservableHistory, buildSearchStri
2
2
  export * from "mobx-location-history";
3
3
  import { createGlobalDynamicConfig } from "yummies/complex";
4
4
  import { LinkedAbortController } from "linked-abort-controller";
5
- import { computed, observable, action, makeObservable, onBecomeObserved, reaction, onBecomeUnobserved, runInAction } from "mobx";
5
+ import { computed, observable, action, makeObservable, reaction, runInAction, onBecomeObserved, onBecomeUnobserved } from "mobx";
6
6
  import { match, compile, parse } from "path-to-regexp";
7
7
  import { callFunction } from "yummies/common";
8
8
  let localHistory;
@@ -33,7 +33,8 @@ const routeConfig = createGlobalDynamicConfig(
33
33
  location,
34
34
  queryParams
35
35
  };
36
- }
36
+ },
37
+ Symbol.for("MOBX_ROUTE_CONFIG")
37
38
  );
38
39
  class Route {
39
40
  constructor(path, config = {}) {
@@ -45,35 +46,28 @@ class Route {
45
46
  this.isIndex = !!this.config.index;
46
47
  this.isHash = !!this.config.hash;
47
48
  this.meta = this.config.meta;
49
+ this.status = "unknown";
48
50
  this.parent = config.parent ?? null;
49
- computed.struct(this, "isOpened");
51
+ computed(this, "isPathMatched");
52
+ computed(this, "isOpened");
50
53
  computed.struct(this, "data");
51
54
  computed.struct(this, "params");
52
- computed.struct(this, "currentPath");
53
- computed.struct(this, "hasOpenedChildren");
54
- computed.struct(this, "isAbleToMergeQuery");
55
+ computed(this, "currentPath");
56
+ computed(this, "hasOpenedChildren");
57
+ computed(this, "isAbleToMergeQuery");
55
58
  computed(this, "baseUrl");
56
59
  observable(this, "children");
57
60
  observable.ref(this, "parent");
61
+ observable.ref(this, "status");
62
+ computed(this, "isOpening");
58
63
  action(this, "addChildren");
64
+ action(this, "confirmOpening");
65
+ action(this, "confirmClosing");
59
66
  action(this, "removeChildren");
60
67
  makeObservable(this);
61
- onBecomeObserved(this, "isOpened", () => {
62
- if (!config.afterOpen && !config.afterClose) {
63
- return;
64
- }
65
- this.reactionDisposer = reaction(
66
- () => this.isOpened,
67
- this.processOpenedState,
68
- {
69
- signal: this.abortController.signal,
70
- fireImmediately: true
71
- }
72
- );
73
- });
74
- onBecomeUnobserved(this, "isOpened", () => {
75
- this.reactionDisposer?.();
76
- this.reactionDisposer = void 0;
68
+ reaction(() => this.isPathMatched, this.checkPathMatch, {
69
+ signal: this.abortController.signal,
70
+ fireImmediately: true
77
71
  });
78
72
  }
79
73
  abortController;
@@ -83,7 +77,8 @@ class Route {
83
77
  _tokenData;
84
78
  _matcher;
85
79
  _compiler;
86
- reactionDisposer;
80
+ skipPathMatchCheck = false;
81
+ status;
87
82
  meta;
88
83
  /**
89
84
  * Indicates if this route is an index route. Index routes activate when parent route path matches exactly.
@@ -119,7 +114,7 @@ class Route {
119
114
  return { params: {}, path: pathnameToCheck };
120
115
  }
121
116
  this._matcher ??= match(this.tokenData, {
122
- end: false,
117
+ end: this.config.exact ?? false,
123
118
  ...this.config.matchOptions
124
119
  });
125
120
  const parsed = this._matcher(pathnameToCheck);
@@ -128,6 +123,9 @@ class Route {
128
123
  }
129
124
  return parsed;
130
125
  }
126
+ get isOpening() {
127
+ return this.status === "opening";
128
+ }
131
129
  /**
132
130
  * Matched path segment for current URL.
133
131
  *
@@ -159,13 +157,16 @@ class Route {
159
157
  }
160
158
  return params;
161
159
  }
160
+ get isPathMatched() {
161
+ return this.parsedPathData !== null;
162
+ }
162
163
  /**
163
164
  * Defines the "open" state for this route.
164
165
  *
165
166
  * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#isopened-boolean)
166
167
  */
167
168
  get isOpened() {
168
- if (this.params === null || this.parsedPathData === null) {
169
+ if (!this.isPathMatched || this.params === null || this.status !== "open-confirmed") {
169
170
  return false;
170
171
  }
171
172
  return !this.config.checkOpened || this.config.checkOpened(this.parsedPathData);
@@ -222,9 +223,7 @@ class Route {
222
223
  };
223
224
  const urlCreateParams = this.config.createUrl?.(defaultUrlCreateParams, this.query.data) ?? routeConfig.get().createUrl?.(defaultUrlCreateParams, this.query.data) ?? defaultUrlCreateParams;
224
225
  const path = this._compiler(this.processParams(urlCreateParams.params));
225
- const url = [urlCreateParams.baseUrl, this.isHash ? "#" : "", path].join(
226
- ""
227
- );
226
+ const url = `${urlCreateParams.baseUrl || ""}${this.isHash ? "#" : ""}${path}`;
228
227
  if (outputParams?.omitQuery) {
229
228
  return url;
230
229
  }
@@ -253,59 +252,96 @@ class Route {
253
252
  url = this.createUrl(args[0], query);
254
253
  }
255
254
  const state = rawState ?? null;
256
- const navigationData = {
255
+ const trx = {
257
256
  url,
258
257
  params,
259
258
  replace,
260
259
  state,
261
260
  query
262
261
  };
263
- const feedback = await this.beforeOpen(navigationData);
264
- if (feedback === false) {
262
+ const isConfirmed = await this.confirmOpening(trx);
263
+ if (!isConfirmed) {
265
264
  return;
266
265
  }
267
- if (typeof feedback === "object") {
268
- Object.assign(navigationData, feedback);
269
- }
270
- if (replace) {
271
- this.history.replace(url, state);
266
+ this.skipPathMatchCheck = true;
267
+ if (trx.replace) {
268
+ this.history.replace(trx.url, trx.state);
272
269
  } else {
273
- this.history.push(url, state);
270
+ this.history.push(trx.url, trx.state);
274
271
  }
275
- if (!this.reactionDisposer && this.isOpened) {
276
- this.config.afterOpen?.(this.parsedPathData, this);
272
+ }
273
+ get tokenData() {
274
+ if (!this._tokenData) {
275
+ this._tokenData = parse(this.path, this.config.parseOptions);
277
276
  }
277
+ return this._tokenData;
278
278
  }
279
- beforeOpen(openData) {
279
+ async confirmOpening(trx) {
280
+ this.status = "opening";
280
281
  if (this.config.beforeOpen) {
281
- return this.config.beforeOpen(openData);
282
+ const feedback = await this.config.beforeOpen(trx);
283
+ if (feedback === false) {
284
+ runInAction(() => {
285
+ this.status = "open-rejected";
286
+ });
287
+ return false;
288
+ }
289
+ if (typeof feedback === "object") {
290
+ runInAction(() => {
291
+ this.status = "open-confirmed";
292
+ });
293
+ return Object.assign(trx, feedback);
294
+ }
282
295
  }
296
+ runInAction(() => {
297
+ this.status = "open-confirmed";
298
+ });
283
299
  return true;
284
300
  }
285
- afterClose() {
286
- if (this.config.afterClose) {
287
- return this.config.afterClose();
288
- }
301
+ confirmClosing() {
302
+ this.status = "closed";
289
303
  return true;
290
304
  }
291
- get tokenData() {
292
- if (!this._tokenData) {
293
- this._tokenData = parse(this.path, this.config.parseOptions);
294
- }
295
- return this._tokenData;
296
- }
297
- firstOpenedStateCheck = true;
298
- processOpenedState = (isOpened) => {
299
- if (this.firstOpenedStateCheck) {
300
- this.firstOpenedStateCheck = false;
301
- if (!isOpened) {
305
+ firstPathMatchingRun = true;
306
+ checkPathMatch = async (isPathMathched) => {
307
+ if (this.firstPathMatchingRun) {
308
+ this.firstPathMatchingRun = false;
309
+ if (!isPathMathched) {
302
310
  return;
303
311
  }
304
312
  }
305
- if (isOpened) {
313
+ if (this.skipPathMatchCheck) {
314
+ this.skipPathMatchCheck = false;
315
+ return;
316
+ }
317
+ if (isPathMathched) {
318
+ const trx = {
319
+ url: this.parsedPathData.path,
320
+ params: this.parsedPathData.params,
321
+ state: this.history.location.state,
322
+ query: this.query.data
323
+ };
324
+ const nextTrxOrConfirmed = await this.confirmOpening(trx);
325
+ if (!nextTrxOrConfirmed) {
326
+ return;
327
+ }
306
328
  this.config.afterOpen?.(this.parsedPathData, this);
329
+ if (typeof nextTrxOrConfirmed === "object") {
330
+ if (nextTrxOrConfirmed.replace) {
331
+ this.history.replace(
332
+ nextTrxOrConfirmed.url,
333
+ nextTrxOrConfirmed.state
334
+ );
335
+ } else {
336
+ this.history.push(nextTrxOrConfirmed.url, nextTrxOrConfirmed.state);
337
+ }
338
+ }
339
+ return;
307
340
  } else {
308
- this.config.afterClose?.();
341
+ const isConfirmed = this.confirmClosing();
342
+ if (isConfirmed) {
343
+ this.config.afterClose?.();
344
+ }
309
345
  }
310
346
  };
311
347
  get isAbleToMergeQuery() {
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/core/config/config.ts","../src/core/route/route.ts","../src/core/route-group/route-group.ts","../src/core/router/router.ts","../src/core/utils/is-route-entity.ts","../src/core/virtual-route/virtual-route.ts"],"sourcesContent":["import {\n createBrowserHistory,\n type History,\n type IQueryParams,\n isObservableHistory,\n QueryParams,\n} from 'mobx-location-history';\nimport { createGlobalDynamicConfig } from 'yummies/complex';\n\nimport type { RouteGlobalConfig } from './config.types.js';\n\nlet localHistory: History | undefined;\n\nexport const routeConfig = createGlobalDynamicConfig<RouteGlobalConfig>(\n (update) => {\n if (localHistory && update?.history && isObservableHistory(localHistory)) {\n localHistory.destroy();\n }\n\n let history: History;\n\n if (update?.history) {\n history = update.history;\n } else {\n history = localHistory = createBrowserHistory();\n }\n\n let queryParams: IQueryParams;\n\n if (update?.history && !update.queryParams) {\n queryParams = new QueryParams({ history });\n } else {\n if (update?.queryParams) {\n queryParams = update.queryParams;\n } else {\n queryParams = new QueryParams({ history });\n }\n }\n\n return {\n ...update,\n history,\n location,\n queryParams,\n };\n },\n);\n","import { LinkedAbortController } from 'linked-abort-controller';\nimport {\n action,\n computed,\n makeObservable,\n observable,\n onBecomeObserved,\n onBecomeUnobserved,\n reaction,\n} from 'mobx';\nimport {\n buildSearchString,\n type History,\n type IQueryParams,\n} from 'mobx-location-history';\nimport {\n compile,\n match,\n type ParamData,\n parse,\n type TokenData,\n} from 'path-to-regexp';\nimport type { AnyObject, IsPartial, Maybe, MaybePromise } from 'yummies/types';\n\nimport { routeConfig } from '../config/index.js';\n\nimport type {\n AnyRoute,\n BeforeOpenFeedback,\n CreatedUrlOutputParams,\n InputPathParams,\n IRoute,\n ParsedPathData,\n ParsedPathParams,\n PreparedNavigationData,\n RouteConfiguration,\n RouteNavigateParams,\n UrlCreateParams,\n} from './route.types.js';\n\n/**\n * Class for creating path based route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html)\n */\nexport class Route<\n TPath extends string,\n TInputParams extends InputPathParams<TPath> = InputPathParams<TPath>,\n TOutputParams extends AnyObject = ParsedPathParams<TPath>,\n TParentRoute extends Route<any, any, any, any> | null = null,\n> implements IRoute<TPath, TInputParams, TOutputParams>\n{\n protected abortController: AbortController;\n protected history: History;\n parent: TParentRoute;\n\n query: IQueryParams;\n\n private _tokenData: TokenData | undefined;\n private _matcher?: ReturnType<typeof match>;\n private _compiler?: ReturnType<typeof compile>;\n private reactionDisposer: Maybe<VoidFunction>;\n\n meta?: AnyObject;\n\n /**\n * Indicates if this route is an index route. Index routes activate when parent route path matches exactly.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#isindex-boolean)\n */\n isIndex: boolean;\n\n /**\n * Indicates if this route is an hash route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#ishash-boolean)\n */\n isHash: boolean;\n\n children: AnyRoute[] = [];\n\n constructor(\n public path: TPath,\n protected config: RouteConfiguration<\n TPath,\n TInputParams,\n TOutputParams,\n TParentRoute\n > = {},\n ) {\n this.abortController = new LinkedAbortController(config.abortSignal);\n this.history = config.history ?? routeConfig.get().history;\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n this.isIndex = !!this.config.index;\n this.isHash = !!this.config.hash;\n this.meta = this.config.meta;\n this.parent = config.parent ?? (null as unknown as TParentRoute);\n\n computed.struct(this, 'isOpened');\n computed.struct(this, 'data');\n computed.struct(this, 'params');\n computed.struct(this, 'currentPath');\n computed.struct(this, 'hasOpenedChildren');\n computed.struct(this, 'isAbleToMergeQuery');\n computed(this, 'baseUrl');\n\n observable(this, 'children');\n observable.ref(this, 'parent');\n action(this, 'addChildren');\n action(this, 'removeChildren');\n\n makeObservable(this);\n\n onBecomeObserved(this, 'isOpened', () => {\n if (!config.afterOpen && !config.afterClose) {\n return;\n }\n\n this.reactionDisposer = reaction(\n () => this.isOpened,\n this.processOpenedState,\n {\n signal: this.abortController.signal,\n fireImmediately: true,\n },\n );\n });\n onBecomeUnobserved(this, 'isOpened', () => {\n this.reactionDisposer?.();\n this.reactionDisposer = undefined;\n });\n }\n\n protected get baseUrl() {\n const baseUrl = this.config.baseUrl ?? routeConfig.get().baseUrl;\n return baseUrl?.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n }\n\n protected get parsedPathData(): ParsedPathData<TPath> | null {\n let pathnameToCheck: string;\n\n if (this.isHash) {\n pathnameToCheck = this.history.location.hash.slice(1);\n } else {\n pathnameToCheck = this.history.location.pathname;\n }\n\n if (this.baseUrl) {\n if (!this.history.location.pathname.startsWith(this.baseUrl)) {\n return null;\n }\n\n pathnameToCheck = pathnameToCheck.replace(this.baseUrl, '');\n }\n\n if (\n (this.path === '' || this.path === '/') &&\n (pathnameToCheck === '/' || pathnameToCheck === '')\n ) {\n return { params: {} as any, path: pathnameToCheck };\n }\n\n this._matcher ??= match(this.tokenData, {\n end: false,\n ...this.config.matchOptions,\n });\n const parsed = this._matcher(pathnameToCheck);\n\n if (parsed === false) {\n return null;\n }\n\n return parsed as ParsedPathData<TPath>;\n }\n\n /**\n * Matched path segment for current URL.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#currentpath-parsedpathname-null)\n */\n get currentPath(): string | null {\n return this.parsedPathData?.path ?? null;\n }\n\n /**\n * Current parsed path parameters.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#params-parsedpathparams-null)\n */\n get params(): TOutputParams | null {\n if (!this.parsedPathData?.params) {\n return null;\n }\n\n let params: TOutputParams | null =\n (this.parsedPathData?.params as unknown as Maybe<TOutputParams>) ?? null;\n\n if (this.config.params) {\n const result = this.config.params(\n this.parsedPathData.params,\n this.config.meta,\n );\n if (result) {\n params = result;\n } else {\n return null;\n }\n }\n\n return params;\n }\n\n /**\n * Defines the \"open\" state for this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#isopened-boolean)\n */\n get isOpened() {\n if (this.params === null || this.parsedPathData === null) {\n return false;\n }\n\n return (\n !this.config.checkOpened || this.config.checkOpened(this.parsedPathData)\n );\n }\n\n /**\n * Allows to create child route based on this route with merging this route path and extending path.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#extend-path-config-route)\n */\n extend<\n TExtendedPath extends string,\n TExtendedInputParams extends\n InputPathParams<`${TPath}${TExtendedPath}`> = InputPathParams<`${TPath}${TExtendedPath}`>,\n TExtendedOutputParams extends AnyObject = TInputParams &\n ParsedPathParams<`${TPath}${TExtendedPath}`>,\n >(\n path: TExtendedPath,\n config?: Omit<\n RouteConfiguration<\n `${TPath}${TExtendedPath}`,\n TInputParams & TExtendedInputParams,\n TExtendedOutputParams,\n any\n >,\n 'parent'\n >,\n ) {\n type ExtendedRoutePath = `${TPath}${TExtendedPath}`;\n type ParentRoute = this;\n // biome-ignore lint/correctness/noUnusedVariables: this is need to extract unused fields\n const { index, params, ...configFromCurrentRoute } = this.config;\n\n const extendedChild = new Route<\n ExtendedRoutePath,\n TInputParams & TExtendedInputParams,\n TExtendedOutputParams,\n ParentRoute\n >(`${this.path}${path}`, {\n ...configFromCurrentRoute,\n ...config,\n parent: this,\n } as any);\n\n this.addChildren(extendedChild as any);\n\n return extendedChild;\n }\n\n addChildren(...routes: AnyRoute[]) {\n this.children.push(...routes);\n }\n\n removeChildren(...routes: AnyRoute[]) {\n this.children = this.children.filter((child) => !routes.includes(child));\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#hasopenedchildren-boolean)\n */\n get hasOpenedChildren(): boolean {\n return this.children.some(\n (child) => child.isOpened || child.hasOpenedChildren,\n );\n }\n\n protected processParams(\n params?: TInputParams | null | undefined,\n ): ParamData | undefined {\n if (params == null) return undefined;\n\n return Object.entries(params).reduce((acc, [key, value]) => {\n if (value != null) {\n acc[key] = Array.isArray(value) ? value.map(String) : String(value);\n }\n return acc;\n }, {} as ParamData);\n }\n\n createUrl(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: Maybe<TInputParams>,\n query?: Maybe<AnyObject>,\n mergeQueryOrParams?: boolean | CreatedUrlOutputParams,\n ]\n : [\n params: TInputParams,\n query?: Maybe<AnyObject>,\n mergeQueryOrParams?: boolean | CreatedUrlOutputParams,\n ]\n ) {\n const params = args[0];\n const rawQuery = args[1];\n const mergeQueryOrOutputParams = args[2] ?? this.isAbleToMergeQuery;\n const outputParams: Maybe<CreatedUrlOutputParams> =\n typeof mergeQueryOrOutputParams === 'boolean'\n ? { mergeQuery: mergeQueryOrOutputParams }\n : mergeQueryOrOutputParams;\n\n const query = outputParams?.mergeQuery\n ? { ...this.query.data, ...rawQuery }\n : (rawQuery ?? {});\n\n this._compiler ??= compile(this.tokenData);\n\n const defaultUrlCreateParams: UrlCreateParams<TInputParams> = {\n baseUrl: this.baseUrl,\n params: params as TInputParams,\n query,\n };\n const urlCreateParams: UrlCreateParams<TInputParams> =\n this.config.createUrl?.(defaultUrlCreateParams, this.query.data) ??\n routeConfig.get().createUrl?.(defaultUrlCreateParams, this.query.data) ??\n defaultUrlCreateParams;\n\n const path = this._compiler(this.processParams(urlCreateParams.params));\n\n const url = [urlCreateParams.baseUrl, this.isHash ? '#' : '', path].join(\n '',\n );\n\n if (outputParams?.omitQuery) {\n return url;\n }\n\n return `${url}${buildSearchString(urlCreateParams.query)}`;\n }\n\n /**\n * Navigates to this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#open-args)\n */\n open(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: TInputParams | null | undefined,\n navigateParams?: RouteNavigateParams,\n ]\n : [params: TInputParams, navigateParams?: RouteNavigateParams]\n ): Promise<void>;\n open(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: TInputParams | null | undefined,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ]\n : [\n params: TInputParams,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ]\n ): Promise<void>;\n open(url: string, navigateParams?: RouteNavigateParams): Promise<void>;\n open(\n url: string,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ): Promise<void>;\n\n /**\n * Navigates to this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#open-args)\n */\n async open(...args: any[]) {\n const {\n replace,\n state: rawState,\n query: rawQuery,\n mergeQuery: rawMergeQuery,\n } = typeof args[1] === 'boolean' || args.length > 2\n ? ({ replace: args[1], query: args[2] } as RouteNavigateParams)\n : ((args[1] ?? {}) as RouteNavigateParams);\n let url: string;\n let params: Maybe<InputPathParams<TPath>>;\n\n const mergeQuery = rawMergeQuery ?? this.isAbleToMergeQuery;\n const query = mergeQuery ? { ...this.query.data, ...rawQuery } : rawQuery;\n\n if (typeof args[0] === 'string') {\n url = args[0];\n } else {\n params = args[0] as InputPathParams<TPath>;\n url = this.createUrl(args[0], query);\n }\n\n const state = rawState ?? null;\n\n const navigationData: PreparedNavigationData<TInputParams> = {\n url,\n params: params as TInputParams,\n replace,\n state,\n query,\n };\n\n const feedback = await this.beforeOpen(navigationData);\n\n if (feedback === false) {\n return;\n }\n\n if (typeof feedback === 'object') {\n Object.assign(navigationData, feedback);\n }\n\n if (replace) {\n this.history.replace(url, state);\n } else {\n this.history.push(url, state);\n }\n\n if (!this.reactionDisposer && this.isOpened) {\n this.config.afterOpen?.(this.parsedPathData!, this);\n }\n }\n\n protected beforeOpen(\n openData: PreparedNavigationData<TInputParams>,\n ): MaybePromise<BeforeOpenFeedback> {\n if (this.config.beforeOpen) {\n return this.config.beforeOpen(openData);\n }\n\n return true;\n }\n\n protected afterClose() {\n if (this.config.afterClose) {\n return this.config.afterClose();\n }\n\n return true;\n }\n\n protected get tokenData() {\n if (!this._tokenData) {\n this._tokenData = parse(this.path, this.config.parseOptions);\n }\n return this._tokenData;\n }\n\n private firstOpenedStateCheck = true;\n private processOpenedState = (isOpened: boolean) => {\n if (this.firstOpenedStateCheck) {\n this.firstOpenedStateCheck = false;\n // ignore first 'afterClose' callback call\n if (!isOpened) {\n return;\n }\n }\n\n if (isOpened) {\n this.config.afterOpen?.(this.parsedPathData!, this);\n } else {\n this.config.afterClose?.();\n }\n };\n\n private get isAbleToMergeQuery() {\n return this.config.mergeQuery ?? routeConfig.get().mergeQuery;\n }\n\n destroy() {\n this.abortController.abort();\n }\n}\n\nexport const createRoute = <\n TPath extends string,\n TInputParams extends InputPathParams<TPath> = InputPathParams<TPath>,\n TOutputParams extends AnyObject = ParsedPathParams<TPath>,\n TParentRoute extends Route<any, any, any, any> | null = null,\n>(\n path: TPath,\n config?: RouteConfiguration<TPath, TInputParams, TOutputParams, TParentRoute>,\n) => new Route<TPath, TInputParams, TOutputParams, TParentRoute>(path, config);\n","import { computed, makeObservable, observable } from 'mobx';\n\nimport type {\n AbstractRouteGroup,\n AnyRouteEntity,\n RoutesCollection,\n} from './route-group.types.js';\n\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/**\n * Class for grouping related routes and managing their state.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html)\n */\nexport class RouteGroup<TRoutesCollection extends RoutesCollection>\n implements AbstractRouteGroup<TRoutesCollection>\n{\n routes: TRoutesCollection;\n\n constructor(\n routes: TRoutesCollection,\n private _indexRoute?: AnyRouteEntity,\n ) {\n this.routes = routes;\n\n computed.struct(this, 'isOpened');\n computed.struct(this, 'indexRoute');\n observable.shallow(this, 'routes');\n makeObservable(this);\n }\n\n /**\n * Returns true if at least one route in the group is open.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#isopened-boolean)\n */\n get isOpened(): boolean {\n const routes = Object.values(this.routes);\n return routes.some(\n (route) =>\n route.isOpened ||\n ('hasOpenedChildren' in route && route.hasOpenedChildren),\n );\n }\n\n /**\n * First found index route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#indexroute-route-undefined)\n */\n get indexRoute(): AnyRouteEntity | undefined {\n return (this._indexRoute ??\n Object.values(this.routes).find(\n (route) => 'isIndex' in route && route.isIndex,\n )) as unknown as AnyRouteEntity;\n }\n\n /**\n * Main navigation method for the group.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#open-args-any-void)\n */\n open(...args: any[]) {\n let lastGroupRoute: RouteGroup<any> | undefined;\n\n if (this.indexRoute && 'open' in this.indexRoute) {\n this.indexRoute.open(...args);\n return;\n }\n\n for (const routeName in this.routes) {\n const route = this.routes[routeName];\n if (route instanceof RouteGroup) {\n lastGroupRoute = route;\n }\n }\n\n if (lastGroupRoute) {\n lastGroupRoute.open(...args);\n } else if (process.env.NODE_ENV !== 'production') {\n console.warn(\n \"RouteGroup doesn't have index route. open() method doesn't work.\",\n );\n }\n }\n}\n\nexport const createRouteGroup = <TRoutesCollection extends RoutesCollection>(\n routes: TRoutesCollection,\n indexRoute?: AnyRouteEntity,\n) => new RouteGroup<TRoutesCollection>(routes, indexRoute);\n","import { computed, makeObservable } from 'mobx';\nimport {\n buildSearchString,\n type History,\n type IQueryParams,\n} from 'mobx-location-history';\n\nimport { routeConfig } from '../config/index.js';\nimport type { RoutesCollection } from '../route-group/index.js';\n\nimport type {\n RouterConfiguration,\n RouterNavigateOptions,\n} from './router.types.js';\n\n/**\n * Class for centralized routing management.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Router.html)\n */\nexport class Router<TRoutesCollection extends RoutesCollection> {\n routes: TRoutesCollection;\n history: History;\n query: IQueryParams;\n\n constructor(config: RouterConfiguration<TRoutesCollection>) {\n this.routes = config.routes;\n this.history = config.history ?? routeConfig.get().history;\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n\n computed.struct(this, 'location');\n\n makeObservable(this);\n }\n\n get location() {\n return this.history.location;\n }\n\n navigate(url: string, options?: RouterNavigateOptions) {\n const query =\n (options?.mergeQuery ?? routeConfig.get().mergeQuery)\n ? { ...this.query.data, ...options?.query }\n : { ...options?.query };\n\n const searchString = buildSearchString(query);\n const navigationUrl = `${url}${searchString}`;\n\n if (options?.replace) {\n this.history.replace(navigationUrl, options?.state);\n } else {\n this.history.push(navigationUrl, options?.state);\n }\n }\n}\n\nexport const createRouter = <TRoutesCollection extends RoutesCollection>(\n config: RouterConfiguration<TRoutesCollection>,\n) => new Router(config);\n","import type { AnyRouteEntity } from '../route-group/index.js';\n\nexport const isRouteEntity = (route: any): route is AnyRouteEntity =>\n route && 'isOpened' in route;\n","import { LinkedAbortController } from 'linked-abort-controller';\nimport {\n action,\n computed,\n makeObservable,\n observable,\n onBecomeObserved,\n onBecomeUnobserved,\n reaction,\n runInAction,\n} from 'mobx';\nimport type { IQueryParams } from 'mobx-location-history';\nimport { callFunction } from 'yummies/common';\nimport type { AnyObject, EmptyObject, IsPartial, Maybe } from 'yummies/types';\n\nimport { routeConfig } from '../config/index.js';\n\nimport type {\n AbstractVirtualRoute,\n VirtualOpenExtraParams,\n VirtualRouteConfiguration,\n} from './virtual-route.types.js';\n\n/**\n * Class for creating routes with custom activation logic\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html)\n */\nexport class VirtualRoute<TParams extends AnyObject | EmptyObject = EmptyObject>\n implements AbstractVirtualRoute<TParams>\n{\n protected abortController: AbortController;\n query: IQueryParams;\n params: TParams | null;\n\n private isLocalOpened: boolean;\n\n private openChecker: Maybe<VirtualRouteConfiguration<TParams>['checkOpened']>;\n private reactionDisposer: Maybe<VoidFunction>;\n\n constructor(protected config: VirtualRouteConfiguration<TParams> = {}) {\n this.abortController = new LinkedAbortController(config.abortSignal);\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n this.params = callFunction(config.initialParams, this) ?? null;\n this.openChecker = config.checkOpened;\n this.isLocalOpened = this.openChecker?.(this) ?? false;\n\n observable(this, 'params');\n observable.ref(this, 'isLocalOpened');\n observable.ref(this, '_isOpened');\n computed.struct(this, 'isOpened');\n action(this, 'setOpenChecker');\n action(this, 'open');\n action(this, 'close');\n makeObservable(this);\n\n onBecomeObserved(this, 'isOpened', () => {\n if (!config.afterOpen && !config.afterClose) {\n return;\n }\n\n this.reactionDisposer = reaction(\n () => this.isOpened,\n this.processOpenedState,\n {\n signal: this.abortController.signal,\n fireImmediately: true,\n },\n );\n });\n onBecomeUnobserved(this, 'isOpened', () => {\n this.reactionDisposer?.();\n this.reactionDisposer = undefined;\n });\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#isopened-boolean)\n */\n get isOpened() {\n const isOuterOpened = this.openChecker == null || this.openChecker(this);\n return this.isLocalOpened && isOuterOpened;\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#setopenchecker-openchecker-void)\n */\n setOpenChecker(\n openChecker: Maybe<VirtualRouteConfiguration<TParams>['checkOpened']>,\n ) {\n this.openChecker = openChecker;\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#open-params-extraparams-query-replace-promise-void)\n */\n open(\n ...args: IsPartial<TParams> extends true\n ? [params?: Maybe<TParams>, extraParams?: VirtualOpenExtraParams]\n : [params: TParams, extraParams?: VirtualOpenExtraParams]\n ): Promise<void>;\n async open(...args: any[]) {\n const params = (args[0] ?? {}) as unknown as TParams;\n const extraParams: Maybe<VirtualOpenExtraParams> = args[1];\n\n if (this.config.beforeOpen) {\n const beforeOpenResult = await this.config.beforeOpen(params, this);\n if (beforeOpenResult === false) {\n return;\n }\n }\n\n if (this.config.open == null) {\n runInAction(() => {\n this.isLocalOpened = true;\n });\n } else {\n const result = await this.config.open(params, this);\n // because result can return void so this is truthy for opening state\n runInAction(() => {\n this.isLocalOpened = result !== false;\n });\n }\n\n if (!this.isLocalOpened) {\n return;\n }\n\n if (extraParams?.query) {\n this.query.update(extraParams.query, extraParams.replace);\n }\n\n runInAction(() => {\n this.params = params;\n });\n\n if (!this.reactionDisposer && this.isOpened) {\n this.config.afterOpen?.(this.params!, this);\n }\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#close-void)\n */\n close() {\n if (this.config.close == null) {\n this.isLocalOpened = false;\n } else {\n const result = this.config.close(this);\n // because result can return void so this is truthy for opening state\n this.isLocalOpened = result !== false;\n }\n\n this.params = null;\n }\n\n private firstOpenedStateCheck = true;\n private processOpenedState = (isOpened: boolean) => {\n if (this.firstOpenedStateCheck) {\n this.firstOpenedStateCheck = false;\n // ignore first 'afterClose' callback call\n if (!isOpened) {\n return;\n }\n }\n\n if (isOpened) {\n this.config.afterOpen?.(this.params!, this);\n } else {\n this.config.afterClose?.();\n }\n };\n\n destroy() {\n this.abortController.abort();\n }\n}\n\nexport const createVirtualRoute = <\n TParams extends AnyObject | EmptyObject = EmptyObject,\n>(\n config?: VirtualRouteConfiguration<TParams>,\n) => new VirtualRoute<TParams>(config);\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/core/config/config.ts","../src/core/route/route.ts","../src/core/route-group/route-group.ts","../src/core/router/router.ts","../src/core/utils/is-route-entity.ts","../src/core/virtual-route/virtual-route.ts"],"sourcesContent":["import {\n createBrowserHistory,\n type History,\n type IQueryParams,\n isObservableHistory,\n QueryParams,\n} from 'mobx-location-history';\nimport { createGlobalDynamicConfig } from 'yummies/complex';\n\nimport type { RouteGlobalConfig } from './config.types.js';\n\nlet localHistory: History | undefined;\n\nexport const routeConfig = createGlobalDynamicConfig<RouteGlobalConfig>(\n (update) => {\n if (localHistory && update?.history && isObservableHistory(localHistory)) {\n localHistory.destroy();\n }\n\n let history: History;\n\n if (update?.history) {\n history = update.history;\n } else {\n history = localHistory = createBrowserHistory();\n }\n\n let queryParams: IQueryParams;\n\n if (update?.history && !update.queryParams) {\n queryParams = new QueryParams({ history });\n } else {\n if (update?.queryParams) {\n queryParams = update.queryParams;\n } else {\n queryParams = new QueryParams({ history });\n }\n }\n\n return {\n ...update,\n history,\n location,\n queryParams,\n };\n },\n Symbol.for('MOBX_ROUTE_CONFIG'),\n);\n","import { LinkedAbortController } from 'linked-abort-controller';\nimport {\n action,\n computed,\n makeObservable,\n observable,\n reaction,\n runInAction,\n} from 'mobx';\nimport {\n buildSearchString,\n type History,\n type IQueryParams,\n} from 'mobx-location-history';\nimport {\n compile,\n match,\n type ParamData,\n parse,\n type TokenData,\n} from 'path-to-regexp';\nimport type { AnyObject, IsPartial, Maybe } from 'yummies/types';\nimport { routeConfig } from '../config/index.js';\nimport type {\n AnyRoute,\n CreatedUrlOutputParams,\n InputPathParams,\n IRoute,\n NavigationTrx,\n ParsedPathData,\n ParsedPathParams,\n RouteConfiguration,\n RouteNavigateParams,\n UrlCreateParams,\n} from './route.types.js';\n\n/**\n * Class for creating path based route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html)\n */\nexport class Route<\n TPath extends string,\n TInputParams extends InputPathParams<TPath> = InputPathParams<TPath>,\n TOutputParams extends AnyObject = ParsedPathParams<TPath>,\n TParentRoute extends Route<any, any, any, any> | null = null,\n> implements IRoute<TPath, TInputParams, TOutputParams>\n{\n protected abortController: AbortController;\n protected history: History;\n parent: TParentRoute;\n\n query: IQueryParams;\n\n private _tokenData: TokenData | undefined;\n private _matcher?: ReturnType<typeof match>;\n private _compiler?: ReturnType<typeof compile>;\n private skipPathMatchCheck = false;\n\n protected status:\n | 'opening'\n | 'closed'\n | 'open-rejected'\n | 'open-confirmed'\n | 'unknown';\n\n meta?: AnyObject;\n\n /**\n * Indicates if this route is an index route. Index routes activate when parent route path matches exactly.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#isindex-boolean)\n */\n isIndex: boolean;\n\n /**\n * Indicates if this route is an hash route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#ishash-boolean)\n */\n isHash: boolean;\n\n children: AnyRoute[] = [];\n\n constructor(\n public path: TPath,\n protected config: RouteConfiguration<\n TPath,\n TInputParams,\n TOutputParams,\n TParentRoute\n > = {},\n ) {\n this.abortController = new LinkedAbortController(config.abortSignal);\n this.history = config.history ?? routeConfig.get().history;\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n this.isIndex = !!this.config.index;\n this.isHash = !!this.config.hash;\n this.meta = this.config.meta;\n this.status = 'unknown';\n this.parent = config.parent ?? (null as unknown as TParentRoute);\n\n computed(this, 'isPathMatched');\n computed(this, 'isOpened');\n computed.struct(this, 'data');\n computed.struct(this, 'params');\n computed(this, 'currentPath');\n computed(this, 'hasOpenedChildren');\n computed(this, 'isAbleToMergeQuery');\n computed(this, 'baseUrl');\n\n observable(this, 'children');\n observable.ref(this, 'parent');\n observable.ref(this, 'status');\n computed(this, 'isOpening');\n action(this, 'addChildren');\n action(this, 'confirmOpening');\n action(this, 'confirmClosing');\n action(this, 'removeChildren');\n\n makeObservable(this);\n\n reaction(() => this.isPathMatched, this.checkPathMatch, {\n signal: this.abortController.signal,\n fireImmediately: true,\n });\n }\n\n protected get baseUrl() {\n const baseUrl = this.config.baseUrl ?? routeConfig.get().baseUrl;\n return baseUrl?.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl;\n }\n\n protected get parsedPathData(): ParsedPathData<TPath> | null {\n let pathnameToCheck: string;\n\n if (this.isHash) {\n pathnameToCheck = this.history.location.hash.slice(1);\n } else {\n pathnameToCheck = this.history.location.pathname;\n }\n\n if (this.baseUrl) {\n if (!this.history.location.pathname.startsWith(this.baseUrl)) {\n return null;\n }\n\n pathnameToCheck = pathnameToCheck.replace(this.baseUrl, '');\n }\n\n if (\n (this.path === '' || this.path === '/') &&\n (pathnameToCheck === '/' || pathnameToCheck === '')\n ) {\n return { params: {} as any, path: pathnameToCheck };\n }\n\n this._matcher ??= match(this.tokenData, {\n end: this.config.exact ?? false,\n ...this.config.matchOptions,\n });\n const parsed = this._matcher(pathnameToCheck);\n\n if (parsed === false) {\n return null;\n }\n\n return parsed as ParsedPathData<TPath>;\n }\n\n get isOpening() {\n return this.status === 'opening';\n }\n\n /**\n * Matched path segment for current URL.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#currentpath-parsedpathname-null)\n */\n get currentPath(): string | null {\n return this.parsedPathData?.path ?? null;\n }\n\n /**\n * Current parsed path parameters.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#params-parsedpathparams-null)\n */\n get params(): TOutputParams | null {\n if (!this.parsedPathData?.params) {\n return null;\n }\n\n let params: TOutputParams | null =\n (this.parsedPathData?.params as unknown as Maybe<TOutputParams>) ?? null;\n\n if (this.config.params) {\n const result = this.config.params(\n this.parsedPathData.params,\n this.config.meta,\n );\n if (result) {\n params = result;\n } else {\n return null;\n }\n }\n\n return params;\n }\n\n protected get isPathMatched() {\n return this.parsedPathData !== null;\n }\n\n /**\n * Defines the \"open\" state for this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#isopened-boolean)\n */\n get isOpened() {\n if (\n !this.isPathMatched ||\n this.params === null ||\n this.status !== 'open-confirmed'\n ) {\n return false;\n }\n\n return (\n !this.config.checkOpened || this.config.checkOpened(this.parsedPathData!)\n );\n }\n\n /**\n * Allows to create child route based on this route with merging this route path and extending path.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#extend-path-config-route)\n */\n extend<\n TExtendedPath extends string,\n TExtendedInputParams extends\n InputPathParams<`${TPath}${TExtendedPath}`> = InputPathParams<`${TPath}${TExtendedPath}`>,\n TExtendedOutputParams extends AnyObject = TInputParams &\n ParsedPathParams<`${TPath}${TExtendedPath}`>,\n >(\n path: TExtendedPath,\n config?: Omit<\n RouteConfiguration<\n `${TPath}${TExtendedPath}`,\n TInputParams & TExtendedInputParams,\n TExtendedOutputParams,\n any\n >,\n 'parent'\n >,\n ) {\n type ExtendedRoutePath = `${TPath}${TExtendedPath}`;\n type ParentRoute = this;\n // biome-ignore lint/correctness/noUnusedVariables: this is need to extract unused fields\n const { index, params, ...configFromCurrentRoute } = this.config;\n\n const extendedChild = new Route<\n ExtendedRoutePath,\n TInputParams & TExtendedInputParams,\n TExtendedOutputParams,\n ParentRoute\n >(`${this.path}${path}`, {\n ...configFromCurrentRoute,\n ...config,\n parent: this,\n } as any);\n\n this.addChildren(extendedChild as any);\n\n return extendedChild;\n }\n\n addChildren(...routes: AnyRoute[]) {\n this.children.push(...routes);\n }\n\n removeChildren(...routes: AnyRoute[]) {\n this.children = this.children.filter((child) => !routes.includes(child));\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#hasopenedchildren-boolean)\n */\n get hasOpenedChildren(): boolean {\n return this.children.some(\n (child) => child.isOpened || child.hasOpenedChildren,\n );\n }\n\n protected processParams(\n params?: TInputParams | null | undefined,\n ): ParamData | undefined {\n if (params == null) return undefined;\n\n return Object.entries(params).reduce((acc, [key, value]) => {\n if (value != null) {\n acc[key] = Array.isArray(value) ? value.map(String) : String(value);\n }\n return acc;\n }, {} as ParamData);\n }\n\n createUrl(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: Maybe<TInputParams>,\n query?: Maybe<AnyObject>,\n mergeQueryOrParams?: boolean | CreatedUrlOutputParams,\n ]\n : [\n params: TInputParams,\n query?: Maybe<AnyObject>,\n mergeQueryOrParams?: boolean | CreatedUrlOutputParams,\n ]\n ) {\n const params = args[0];\n const rawQuery = args[1];\n const mergeQueryOrOutputParams = args[2] ?? this.isAbleToMergeQuery;\n const outputParams: Maybe<CreatedUrlOutputParams> =\n typeof mergeQueryOrOutputParams === 'boolean'\n ? { mergeQuery: mergeQueryOrOutputParams }\n : mergeQueryOrOutputParams;\n\n const query = outputParams?.mergeQuery\n ? { ...this.query.data, ...rawQuery }\n : (rawQuery ?? {});\n\n this._compiler ??= compile(this.tokenData);\n\n const defaultUrlCreateParams: UrlCreateParams<TInputParams> = {\n baseUrl: this.baseUrl,\n params: params as TInputParams,\n query,\n };\n const urlCreateParams: UrlCreateParams<TInputParams> =\n this.config.createUrl?.(defaultUrlCreateParams, this.query.data) ??\n routeConfig.get().createUrl?.(defaultUrlCreateParams, this.query.data) ??\n defaultUrlCreateParams;\n\n const path = this._compiler(this.processParams(urlCreateParams.params));\n\n const url = `${urlCreateParams.baseUrl || ''}${this.isHash ? '#' : ''}${path}`;\n\n if (outputParams?.omitQuery) {\n return url;\n }\n\n return `${url}${buildSearchString(urlCreateParams.query)}`;\n }\n\n /**\n * Navigates to this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#open-args)\n */\n open(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: TInputParams | null | undefined,\n navigateParams?: RouteNavigateParams,\n ]\n : [params: TInputParams, navigateParams?: RouteNavigateParams]\n ): Promise<void>;\n open(\n ...args: IsPartial<TInputParams> extends true\n ? [\n params?: TInputParams | null | undefined,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ]\n : [\n params: TInputParams,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ]\n ): Promise<void>;\n open(url: string, navigateParams?: RouteNavigateParams): Promise<void>;\n open(\n url: string,\n replace?: RouteNavigateParams['replace'],\n query?: RouteNavigateParams['query'],\n ): Promise<void>;\n\n /**\n * Navigates to this route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Route.html#open-args)\n */\n async open(...args: any[]) {\n const {\n replace,\n state: rawState,\n query: rawQuery,\n mergeQuery: rawMergeQuery,\n } = typeof args[1] === 'boolean' || args.length > 2\n ? ({ replace: args[1], query: args[2] } as RouteNavigateParams)\n : ((args[1] ?? {}) as RouteNavigateParams);\n let url: string;\n let params: Maybe<InputPathParams<TPath>>;\n\n const mergeQuery = rawMergeQuery ?? this.isAbleToMergeQuery;\n const query = mergeQuery ? { ...this.query.data, ...rawQuery } : rawQuery;\n\n if (typeof args[0] === 'string') {\n url = args[0];\n } else {\n params = args[0] as InputPathParams<TPath>;\n url = this.createUrl(args[0], query);\n }\n\n const state = rawState ?? null;\n\n const trx: NavigationTrx<TInputParams> = {\n url,\n params: params as TInputParams,\n replace,\n state,\n query,\n };\n\n const isConfirmed = await this.confirmOpening(trx);\n\n if (!isConfirmed) {\n return;\n }\n\n this.skipPathMatchCheck = true;\n\n if (trx.replace) {\n this.history.replace(trx.url, trx.state);\n } else {\n this.history.push(trx.url, trx.state);\n }\n }\n\n protected get tokenData() {\n if (!this._tokenData) {\n this._tokenData = parse(this.path, this.config.parseOptions);\n }\n return this._tokenData;\n }\n\n protected async confirmOpening(trx: NavigationTrx<TInputParams>) {\n this.status = 'opening';\n\n if (this.config.beforeOpen) {\n const feedback = await this.config.beforeOpen(trx);\n\n if (feedback === false) {\n runInAction(() => {\n this.status = 'open-rejected';\n });\n return false;\n }\n\n if (typeof feedback === 'object') {\n runInAction(() => {\n this.status = 'open-confirmed';\n });\n\n return Object.assign(trx, feedback);\n }\n }\n\n runInAction(() => {\n this.status = 'open-confirmed';\n });\n\n return true;\n }\n\n protected confirmClosing() {\n this.status = 'closed';\n return true;\n }\n\n private firstPathMatchingRun = true;\n\n private checkPathMatch = async (isPathMathched: boolean) => {\n if (this.firstPathMatchingRun) {\n this.firstPathMatchingRun = false;\n // ignore first 'afterClose' callback call\n if (!isPathMathched) {\n return;\n }\n }\n\n if (this.skipPathMatchCheck) {\n // after open\n this.skipPathMatchCheck = false;\n return;\n }\n\n if (isPathMathched) {\n const trx: NavigationTrx<TInputParams> = {\n url: this.parsedPathData!.path,\n params: this.parsedPathData!.params as TInputParams,\n state: this.history.location.state,\n query: this.query.data,\n };\n\n const nextTrxOrConfirmed = await this.confirmOpening(trx);\n\n if (!nextTrxOrConfirmed) {\n return;\n }\n\n this.config.afterOpen?.(this.parsedPathData!, this);\n\n if (typeof nextTrxOrConfirmed === 'object') {\n if (nextTrxOrConfirmed.replace) {\n this.history.replace(\n nextTrxOrConfirmed.url,\n nextTrxOrConfirmed.state,\n );\n } else {\n this.history.push(nextTrxOrConfirmed.url, nextTrxOrConfirmed.state);\n }\n }\n\n return;\n } else {\n const isConfirmed = this.confirmClosing();\n\n if (isConfirmed) {\n this.config.afterClose?.();\n }\n }\n };\n\n private get isAbleToMergeQuery() {\n return this.config.mergeQuery ?? routeConfig.get().mergeQuery;\n }\n\n destroy() {\n this.abortController.abort();\n }\n}\n\nexport const createRoute = <\n TPath extends string,\n TInputParams extends InputPathParams<TPath> = InputPathParams<TPath>,\n TOutputParams extends AnyObject = ParsedPathParams<TPath>,\n TParentRoute extends Route<any, any, any, any> | null = null,\n>(\n path: TPath,\n config?: RouteConfiguration<TPath, TInputParams, TOutputParams, TParentRoute>,\n) => new Route<TPath, TInputParams, TOutputParams, TParentRoute>(path, config);\n","import { computed, makeObservable, observable } from 'mobx';\n\nimport type {\n AbstractRouteGroup,\n AnyRouteEntity,\n RoutesCollection,\n} from './route-group.types.js';\n\ndeclare const process: { env: { NODE_ENV?: string } };\n\n/**\n * Class for grouping related routes and managing their state.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html)\n */\nexport class RouteGroup<TRoutesCollection extends RoutesCollection>\n implements AbstractRouteGroup<TRoutesCollection>\n{\n routes: TRoutesCollection;\n\n constructor(\n routes: TRoutesCollection,\n private _indexRoute?: AnyRouteEntity,\n ) {\n this.routes = routes;\n\n computed.struct(this, 'isOpened');\n computed.struct(this, 'indexRoute');\n observable.shallow(this, 'routes');\n makeObservable(this);\n }\n\n /**\n * Returns true if at least one route in the group is open.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#isopened-boolean)\n */\n get isOpened(): boolean {\n const routes = Object.values(this.routes);\n return routes.some(\n (route) =>\n route.isOpened ||\n ('hasOpenedChildren' in route && route.hasOpenedChildren),\n );\n }\n\n /**\n * First found index route.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#indexroute-route-undefined)\n */\n get indexRoute(): AnyRouteEntity | undefined {\n return (this._indexRoute ??\n Object.values(this.routes).find(\n (route) => 'isIndex' in route && route.isIndex,\n )) as unknown as AnyRouteEntity;\n }\n\n /**\n * Main navigation method for the group.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/RouteGroup.html#open-args-any-void)\n */\n open(...args: any[]) {\n let lastGroupRoute: RouteGroup<any> | undefined;\n\n if (this.indexRoute && 'open' in this.indexRoute) {\n this.indexRoute.open(...args);\n return;\n }\n\n for (const routeName in this.routes) {\n const route = this.routes[routeName];\n if (route instanceof RouteGroup) {\n lastGroupRoute = route;\n }\n }\n\n if (lastGroupRoute) {\n lastGroupRoute.open(...args);\n } else if (process.env.NODE_ENV !== 'production') {\n console.warn(\n \"RouteGroup doesn't have index route. open() method doesn't work.\",\n );\n }\n }\n}\n\nexport const createRouteGroup = <TRoutesCollection extends RoutesCollection>(\n routes: TRoutesCollection,\n indexRoute?: AnyRouteEntity,\n) => new RouteGroup<TRoutesCollection>(routes, indexRoute);\n","import { computed, makeObservable } from 'mobx';\nimport {\n buildSearchString,\n type History,\n type IQueryParams,\n} from 'mobx-location-history';\n\nimport { routeConfig } from '../config/index.js';\nimport type { RoutesCollection } from '../route-group/index.js';\n\nimport type {\n RouterConfiguration,\n RouterNavigateOptions,\n} from './router.types.js';\n\n/**\n * Class for centralized routing management.\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/Router.html)\n */\nexport class Router<TRoutesCollection extends RoutesCollection> {\n routes: TRoutesCollection;\n history: History;\n query: IQueryParams;\n\n constructor(config: RouterConfiguration<TRoutesCollection>) {\n this.routes = config.routes;\n this.history = config.history ?? routeConfig.get().history;\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n\n computed.struct(this, 'location');\n\n makeObservable(this);\n }\n\n get location() {\n return this.history.location;\n }\n\n navigate(url: string, options?: RouterNavigateOptions) {\n const query =\n (options?.mergeQuery ?? routeConfig.get().mergeQuery)\n ? { ...this.query.data, ...options?.query }\n : { ...options?.query };\n\n const searchString = buildSearchString(query);\n const navigationUrl = `${url}${searchString}`;\n\n if (options?.replace) {\n this.history.replace(navigationUrl, options?.state);\n } else {\n this.history.push(navigationUrl, options?.state);\n }\n }\n}\n\nexport const createRouter = <TRoutesCollection extends RoutesCollection>(\n config: RouterConfiguration<TRoutesCollection>,\n) => new Router(config);\n","import type { AnyRouteEntity } from '../route-group/index.js';\n\nexport const isRouteEntity = (route: any): route is AnyRouteEntity =>\n route && 'isOpened' in route;\n","import { LinkedAbortController } from 'linked-abort-controller';\nimport {\n action,\n computed,\n makeObservable,\n observable,\n onBecomeObserved,\n onBecomeUnobserved,\n reaction,\n runInAction,\n} from 'mobx';\nimport type { IQueryParams } from 'mobx-location-history';\nimport { callFunction } from 'yummies/common';\nimport type { AnyObject, EmptyObject, IsPartial, Maybe } from 'yummies/types';\n\nimport { routeConfig } from '../config/index.js';\n\nimport type {\n AbstractVirtualRoute,\n VirtualOpenExtraParams,\n VirtualRouteConfiguration,\n} from './virtual-route.types.js';\n\n/**\n * Class for creating routes with custom activation logic\n *\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html)\n */\nexport class VirtualRoute<TParams extends AnyObject | EmptyObject = EmptyObject>\n implements AbstractVirtualRoute<TParams>\n{\n protected abortController: AbortController;\n query: IQueryParams;\n params: TParams | null;\n\n private isLocalOpened: boolean;\n\n private openChecker: Maybe<VirtualRouteConfiguration<TParams>['checkOpened']>;\n private reactionDisposer: Maybe<VoidFunction>;\n\n constructor(protected config: VirtualRouteConfiguration<TParams> = {}) {\n this.abortController = new LinkedAbortController(config.abortSignal);\n this.query = config.queryParams ?? routeConfig.get().queryParams;\n this.params = callFunction(config.initialParams, this) ?? null;\n this.openChecker = config.checkOpened;\n this.isLocalOpened = this.openChecker?.(this) ?? false;\n\n observable(this, 'params');\n observable.ref(this, 'isLocalOpened');\n observable.ref(this, '_isOpened');\n computed.struct(this, 'isOpened');\n action(this, 'setOpenChecker');\n action(this, 'open');\n action(this, 'close');\n makeObservable(this);\n\n onBecomeObserved(this, 'isOpened', () => {\n if (!config.afterOpen && !config.afterClose) {\n return;\n }\n\n this.reactionDisposer = reaction(\n () => this.isOpened,\n this.processOpenedState,\n {\n signal: this.abortController.signal,\n fireImmediately: true,\n },\n );\n });\n onBecomeUnobserved(this, 'isOpened', () => {\n this.reactionDisposer?.();\n this.reactionDisposer = undefined;\n });\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#isopened-boolean)\n */\n get isOpened() {\n const isOuterOpened = this.openChecker == null || this.openChecker(this);\n return this.isLocalOpened && isOuterOpened;\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#setopenchecker-openchecker-void)\n */\n setOpenChecker(\n openChecker: Maybe<VirtualRouteConfiguration<TParams>['checkOpened']>,\n ) {\n this.openChecker = openChecker;\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#open-params-extraparams-query-replace-promise-void)\n */\n open(\n ...args: IsPartial<TParams> extends true\n ? [params?: Maybe<TParams>, extraParams?: VirtualOpenExtraParams]\n : [params: TParams, extraParams?: VirtualOpenExtraParams]\n ): Promise<void>;\n async open(...args: any[]) {\n const params = (args[0] ?? {}) as unknown as TParams;\n const extraParams: Maybe<VirtualOpenExtraParams> = args[1];\n\n if (this.config.beforeOpen) {\n const beforeOpenResult = await this.config.beforeOpen(params, this);\n if (beforeOpenResult === false) {\n return;\n }\n }\n\n if (this.config.open == null) {\n runInAction(() => {\n this.isLocalOpened = true;\n });\n } else {\n const result = await this.config.open(params, this);\n // because result can return void so this is truthy for opening state\n runInAction(() => {\n this.isLocalOpened = result !== false;\n });\n }\n\n if (!this.isLocalOpened) {\n return;\n }\n\n if (extraParams?.query) {\n this.query.update(extraParams.query, extraParams.replace);\n }\n\n runInAction(() => {\n this.params = params;\n });\n\n if (!this.reactionDisposer && this.isOpened) {\n this.config.afterOpen?.(this.params!, this);\n }\n }\n\n /**\n * [**Documentation**](https://js2me.github.io/mobx-route/core/VirtualRoute.html#close-void)\n */\n close() {\n if (this.config.close == null) {\n this.isLocalOpened = false;\n } else {\n const result = this.config.close(this);\n // because result can return void so this is truthy for opening state\n this.isLocalOpened = result !== false;\n }\n\n this.params = null;\n }\n\n private firstOpenedStateCheck = true;\n private processOpenedState = (isOpened: boolean) => {\n if (this.firstOpenedStateCheck) {\n this.firstOpenedStateCheck = false;\n // ignore first 'afterClose' callback call\n if (!isOpened) {\n return;\n }\n }\n\n if (isOpened) {\n this.config.afterOpen?.(this.params!, this);\n } else {\n this.config.afterClose?.();\n }\n };\n\n destroy() {\n this.abortController.abort();\n }\n}\n\nexport const createVirtualRoute = <\n TParams extends AnyObject | EmptyObject = EmptyObject,\n>(\n config?: VirtualRouteConfiguration<TParams>,\n) => new VirtualRoute<TParams>(config);\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobx-route",
3
- "version": "0.19.0",
3
+ "version": "0.20.1",
4
4
  "keywords": [
5
5
  "mobx",
6
6
  "react",
package/react.cjs CHANGED
@@ -125,22 +125,31 @@ const RouteViewGroup = mobxReactLite.observer(
125
125
  params,
126
126
  ...navigateParams
127
127
  }) => {
128
- let activeChildNode = null;
128
+ let activeChildRouteNode = null;
129
129
  let lastInactiveChildNode = null;
130
+ let hasRoutesInOpening = false;
130
131
  const childNodes = Array.isArray(children) ? children : [children];
131
132
  for (const childNode of childNodes) {
132
- if (react.isValidElement(childNode) && // @ts-expect-error redundand checks better to wrap in this directive
133
- mobxRoute.isRouteEntity(childNode.props?.route) && // @ts-expect-error redundand checks better to wrap in this directive
134
- childNode.props.route.isOpened) {
135
- activeChildNode = childNode;
136
- break;
133
+ const isRouteChild = react.isValidElement(childNode) && // @ts-expect-error redundand checks better to wrap in this directive
134
+ mobxRoute.isRouteEntity(childNode.props?.route);
135
+ if (isRouteChild) {
136
+ const route = childNode.props.route;
137
+ if (route.isOpened) {
138
+ activeChildRouteNode = childNode;
139
+ break;
140
+ } else {
141
+ if (route.isOpening) {
142
+ hasRoutesInOpening = true;
143
+ }
144
+ lastInactiveChildNode = childNode;
145
+ }
137
146
  } else {
138
147
  lastInactiveChildNode = childNode;
139
148
  }
140
149
  }
141
- const hasActiveChildNode = !!activeChildNode;
150
+ const hasActiveChildNode = !!activeChildRouteNode;
142
151
  react.useEffect(() => {
143
- if (!hasActiveChildNode && otherwiseNavigation) {
152
+ if (!hasActiveChildNode && !hasRoutesInOpening && otherwiseNavigation) {
144
153
  if (typeof otherwiseNavigation === "string") {
145
154
  const history = mobxRoute.routeConfig.get().history;
146
155
  const url = `${otherwiseNavigation}${mobxRoute.buildSearchString(navigateParams.query || {})}`;
@@ -153,11 +162,11 @@ const RouteViewGroup = mobxReactLite.observer(
153
162
  otherwiseNavigation.open(params, navigateParams);
154
163
  }
155
164
  }
156
- }, [hasActiveChildNode, otherwiseNavigation]);
157
- if (otherwiseNavigation && !activeChildNode) {
165
+ }, [hasActiveChildNode, hasRoutesInOpening, otherwiseNavigation]);
166
+ if (otherwiseNavigation && !activeChildRouteNode) {
158
167
  return null;
159
168
  }
160
- const resultNodeToRender = activeChildNode ?? lastInactiveChildNode ?? null;
169
+ const resultNodeToRender = activeChildRouteNode ?? lastInactiveChildNode ?? null;
161
170
  if (Layout) {
162
171
  return /* @__PURE__ */ jsxRuntime.jsx(Layout, { children: resultNodeToRender });
163
172
  }
package/react.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.cjs","sources":["../src/react/components/link.tsx","../src/react/components/route-view.tsx","../src/react/components/route-view-group.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite';\nimport {\n type AnyRoute,\n buildSearchString,\n type InputPathParams,\n parseSearchString,\n type RouteNavigateParams,\n routeConfig,\n} from 'mobx-route';\nimport {\n type AnchorHTMLAttributes,\n cloneElement,\n forwardRef,\n isValidElement,\n type MouseEvent,\n useMemo,\n useRef,\n} from 'react';\nimport { isShallowEqual } from 'yummies/data';\nimport type { IsPartial } from 'yummies/types';\n\ninterface LinkAnchorProps\n extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\n asChild?: boolean;\n}\n\ntype LinkPathRouteProps<TRoute extends AnyRoute> = {\n to: TRoute;\n} & (IsPartial<InputPathParams<TRoute['path']>> extends true\n ? {\n params?: InputPathParams<TRoute['path']> | null | undefined;\n }\n : { params: InputPathParams<TRoute['path']> });\n\ntype LinkSimpleRouteProps =\n | {\n to: string;\n }\n | {\n href: string;\n };\n\nexport type LinkProps<TRoute extends AnyRoute> = LinkAnchorProps &\n RouteNavigateParams &\n (LinkPathRouteProps<TRoute> | LinkSimpleRouteProps);\n\ntype LinkComponentType = <TRoute extends AnyRoute>(\n props: LinkProps<TRoute>,\n) => React.ReactNode;\n\nexport const Link = observer(\n forwardRef<\n HTMLAnchorElement,\n LinkAnchorProps &\n RouteNavigateParams & {\n params?: any;\n to: string | AnyRoute;\n href: string;\n }\n >(\n (\n {\n to,\n href: outerHref,\n mergeQuery,\n asChild,\n children,\n params,\n // route navigate params\n query,\n replace,\n state,\n ...outerAnchorProps\n },\n ref,\n ) => {\n const isExternalNavigation =\n outerAnchorProps.target === '_blank' ||\n outerAnchorProps.target === 'blank';\n const queryDataRef = useRef<RouteNavigateParams['query']>(query);\n\n if (!isShallowEqual(queryDataRef.current, query)) {\n queryDataRef.current = query;\n }\n\n const { href, navigateParams } = useMemo(() => {\n const navigateParams: RouteNavigateParams = {\n mergeQuery,\n query,\n replace,\n state,\n };\n\n const cfg = routeConfig.get();\n\n let href: string;\n\n if (outerHref) {\n href = outerHref;\n } else {\n if (typeof to === 'string') {\n const isNeedToMergeQuery =\n navigateParams.mergeQuery ?? cfg.mergeQuery;\n\n const [path, ...querySegments] = to.split('?');\n\n const existedQuery = parseSearchString(querySegments.join('?'));\n\n const query = {\n ...(isNeedToMergeQuery ? cfg.queryParams.data : {}),\n ...existedQuery,\n ...navigateParams.query,\n };\n\n href = `${path}${buildSearchString(query)}`;\n } else {\n href = to.createUrl(\n params,\n navigateParams.query,\n navigateParams.mergeQuery,\n );\n }\n }\n\n return {\n href: cfg.formatLinkHref?.(href) ?? href,\n navigateParams,\n };\n }, [mergeQuery, replace, state, to, queryDataRef.current]);\n\n const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {\n if (\n isExternalNavigation ||\n event.ctrlKey ||\n event.metaKey ||\n event.altKey ||\n event.shiftKey ||\n event.button !== 0\n )\n return;\n\n outerAnchorProps.onClick?.(event);\n\n if (!event.defaultPrevented) {\n event.preventDefault();\n\n if (navigateParams.replace) {\n routeConfig.get().history.replace(href, navigateParams.state);\n } else {\n routeConfig.get().history.push(href, navigateParams.state);\n }\n }\n };\n\n const anchorProps = {\n ...outerAnchorProps,\n href,\n onClick: handleClick,\n rel:\n outerAnchorProps.rel ??\n (isExternalNavigation ? 'noopener noreferrer' : undefined),\n };\n\n return asChild && isValidElement(children) ? (\n cloneElement(children, anchorProps)\n ) : (\n <a {...anchorProps} ref={ref}>\n {children}\n </a>\n );\n },\n ),\n) as unknown as LinkComponentType;\n","import { observer } from 'mobx-react-lite';\nimport type {\n AnyAbstractRouteEntity,\n AnyRoute,\n AnyVirtualRoute,\n} from 'mobx-route';\nimport { useRef } from 'react';\nimport { type LoadableConfig, loadable } from 'react-simple-loadable';\n\nexport type RouteViewComponent<TRoute extends AnyAbstractRouteEntity> =\n React.ComponentType<RouteViewProps<TRoute>>;\n\ninterface RouteViewConfigWithoutRoute {\n children?: React.ReactNode | (() => React.ReactNode);\n}\n\nexport interface RouteViewConfigWithRoute<TRoute extends AnyAbstractRouteEntity>\n extends Pick<LoadableConfig, 'loading' | 'preload' | 'throwOnError'> {\n route: TRoute;\n view?: RouteViewComponent<TRoute>;\n loadView?: (route: TRoute) => Promise<RouteViewComponent<TRoute>>;\n /**\n * Case when route is not opened\n */\n fallback?: React.ReactNode;\n children?:\n | React.ReactNode\n | ((\n params: RouteViewProps<TRoute>['params'],\n route: TRoute,\n ) => React.ReactNode);\n}\n\nexport type RouteViewConfig<TRoute extends AnyAbstractRouteEntity> =\n | RouteViewConfigWithRoute<TRoute>\n | RouteViewConfigWithoutRoute;\n\nexport type RouteViewProps<TRoute extends AnyAbstractRouteEntity> = {\n children?: React.ReactNode;\n params: TRoute extends AnyRoute\n ? Exclude<TRoute['params'], null | undefined>\n : TRoute extends AnyVirtualRoute\n ? TRoute['params']\n : never;\n};\n\ntype RouteViewBaseComponent = <TRoute extends AnyAbstractRouteEntity>(\n props: RouteViewConfig<TRoute>,\n) => React.ReactNode;\n\nfunction RouteViewBase<TRoute extends AnyAbstractRouteEntity>(\n props: Readonly<RouteViewConfig<TRoute>>,\n): React.ReactNode {\n // @ts-expect-error redundand pass first argument\n const lazyViewComponentRef = useRef<React.ComponentType<any>>();\n\n let Component: React.ComponentType<any> | undefined;\n\n if (!('route' in props)) {\n return typeof props.children === 'function'\n ? props.children()\n : props.children;\n }\n\n if (!props.route.isOpened) {\n return props.fallback ?? null;\n }\n\n if (props.loadView) {\n if (!lazyViewComponentRef.current) {\n lazyViewComponentRef.current = loadable({\n load: () => props.loadView!(props.route),\n loading: props.loading,\n preload: props.preload,\n throwOnError: props.throwOnError,\n });\n }\n Component = lazyViewComponentRef.current;\n } else {\n Component = props.view;\n }\n\n const params: any = 'params' in props.route ? props.route.params : {};\n\n if (Component) {\n return <Component params={params}>{props.children}</Component>;\n }\n\n if (typeof props.children === 'function') {\n return props.children(params, props.route);\n }\n\n return props.children;\n}\n\nexport const RouteView = observer(RouteViewBase) as RouteViewBaseComponent;\n","import { observer } from 'mobx-react-lite';\nimport {\n type AnyRouteEntity,\n buildSearchString,\n isRouteEntity,\n type RouteNavigateParams,\n type RouteParams,\n routeConfig,\n} from 'mobx-route';\nimport { isValidElement, useEffect } from 'react';\nimport type { IsPartial, Maybe } from 'yummies/types';\n\ntype LayoutComponent =\n | React.ComponentType<{ children?: React.ReactNode }>\n | React.ComponentType<{ children: React.ReactNode }>;\n\ninterface BaseProps extends RouteNavigateParams {\n children: React.ReactNode;\n layout?: LayoutComponent;\n}\n\ntype PropsWithDefaultRoute<TRoute extends AnyRouteEntity> = BaseProps & {\n otherwise?: TRoute;\n} & (IsPartial<RouteParams<TRoute>> extends true\n ? {\n params?: Maybe<RouteParams<TRoute>>;\n }\n : {\n params: RouteParams<TRoute>;\n });\n\ntype PropsWithDefaultUrl = BaseProps & {\n otherwise?: string;\n};\n\nexport type RouteViewGroupProps<TRoute extends AnyRouteEntity> =\n | PropsWithDefaultRoute<TRoute>\n | PropsWithDefaultUrl;\n\ntype RouteViewGroupComponent = <TRoute extends AnyRouteEntity>(\n props: RouteViewGroupProps<TRoute>,\n) => React.ReactNode;\n\nexport const RouteViewGroup = observer(\n <TRoute extends AnyRouteEntity>({\n children,\n layout: Layout,\n otherwise: otherwiseNavigation,\n // @ts-expect-error\n params,\n ...navigateParams\n }: RouteViewGroupProps<TRoute>) => {\n let activeChildNode: React.ReactNode = null;\n let lastInactiveChildNode: React.ReactNode = null;\n\n const childNodes: React.ReactNode[] = Array.isArray(children)\n ? children\n : [children];\n\n for (const childNode of childNodes) {\n if (\n isValidElement(childNode) &&\n // @ts-expect-error redundand checks better to wrap in this directive\n isRouteEntity(childNode.props?.route) &&\n // @ts-expect-error redundand checks better to wrap in this directive\n childNode.props.route.isOpened\n ) {\n activeChildNode = childNode;\n break;\n } else {\n lastInactiveChildNode = childNode;\n }\n }\n\n const hasActiveChildNode = !!activeChildNode;\n\n useEffect(() => {\n if (!hasActiveChildNode && otherwiseNavigation) {\n if (typeof otherwiseNavigation === 'string') {\n const history = routeConfig.get().history;\n const url = `${otherwiseNavigation}${buildSearchString(navigateParams.query || {})}`;\n\n if (navigateParams.replace) {\n history.replace(url, navigateParams.state);\n } else {\n history.push(url, navigateParams.state);\n }\n } else if (!otherwiseNavigation.isOpened) {\n otherwiseNavigation.open(params, navigateParams);\n }\n }\n }, [hasActiveChildNode, otherwiseNavigation]);\n\n if (otherwiseNavigation && !activeChildNode) {\n return null;\n }\n\n const resultNodeToRender = activeChildNode ?? lastInactiveChildNode ?? null;\n\n if (Layout) {\n return <Layout>{resultNodeToRender}</Layout>;\n }\n\n return resultNodeToRender;\n },\n) as unknown as RouteViewGroupComponent;\n"],"names":["observer","forwardRef","useRef","isShallowEqual","useMemo","navigateParams","routeConfig","href","parseSearchString","query","buildSearchString","isValidElement","cloneElement","jsx","loadable","isRouteEntity","useEffect"],"mappings":"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"}
1
+ {"version":3,"file":"react.cjs","sources":["../src/react/components/link.tsx","../src/react/components/route-view.tsx","../src/react/components/route-view-group.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite';\nimport {\n type AnyRoute,\n buildSearchString,\n type InputPathParams,\n parseSearchString,\n type RouteNavigateParams,\n routeConfig,\n} from 'mobx-route';\nimport {\n type AnchorHTMLAttributes,\n cloneElement,\n forwardRef,\n isValidElement,\n type MouseEvent,\n useMemo,\n useRef,\n} from 'react';\nimport { isShallowEqual } from 'yummies/data';\nimport type { IsPartial } from 'yummies/types';\n\ninterface LinkAnchorProps\n extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\n asChild?: boolean;\n}\n\ntype LinkPathRouteProps<TRoute extends AnyRoute> = {\n to: TRoute;\n} & (IsPartial<InputPathParams<TRoute['path']>> extends true\n ? {\n params?: InputPathParams<TRoute['path']> | null | undefined;\n }\n : { params: InputPathParams<TRoute['path']> });\n\ntype LinkSimpleRouteProps =\n | {\n to: string;\n }\n | {\n href: string;\n };\n\nexport type LinkProps<TRoute extends AnyRoute> = LinkAnchorProps &\n RouteNavigateParams &\n (LinkPathRouteProps<TRoute> | LinkSimpleRouteProps);\n\ntype LinkComponentType = <TRoute extends AnyRoute>(\n props: LinkProps<TRoute>,\n) => React.ReactNode;\n\nexport const Link = observer(\n forwardRef<\n HTMLAnchorElement,\n LinkAnchorProps &\n RouteNavigateParams & {\n params?: any;\n to: string | AnyRoute;\n href: string;\n }\n >(\n (\n {\n to,\n href: outerHref,\n mergeQuery,\n asChild,\n children,\n params,\n // route navigate params\n query,\n replace,\n state,\n ...outerAnchorProps\n },\n ref,\n ) => {\n const isExternalNavigation =\n outerAnchorProps.target === '_blank' ||\n outerAnchorProps.target === 'blank';\n const queryDataRef = useRef<RouteNavigateParams['query']>(query);\n\n if (!isShallowEqual(queryDataRef.current, query)) {\n queryDataRef.current = query;\n }\n\n const { href, navigateParams } = useMemo(() => {\n const navigateParams: RouteNavigateParams = {\n mergeQuery,\n query,\n replace,\n state,\n };\n\n const cfg = routeConfig.get();\n\n let href: string;\n\n if (outerHref) {\n href = outerHref;\n } else {\n if (typeof to === 'string') {\n const isNeedToMergeQuery =\n navigateParams.mergeQuery ?? cfg.mergeQuery;\n\n const [path, ...querySegments] = to.split('?');\n\n const existedQuery = parseSearchString(querySegments.join('?'));\n\n const query = {\n ...(isNeedToMergeQuery ? cfg.queryParams.data : {}),\n ...existedQuery,\n ...navigateParams.query,\n };\n\n href = `${path}${buildSearchString(query)}`;\n } else {\n href = to.createUrl(\n params,\n navigateParams.query,\n navigateParams.mergeQuery,\n );\n }\n }\n\n return {\n href: cfg.formatLinkHref?.(href) ?? href,\n navigateParams,\n };\n }, [mergeQuery, replace, state, to, queryDataRef.current]);\n\n const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {\n if (\n isExternalNavigation ||\n event.ctrlKey ||\n event.metaKey ||\n event.altKey ||\n event.shiftKey ||\n event.button !== 0\n )\n return;\n\n outerAnchorProps.onClick?.(event);\n\n if (!event.defaultPrevented) {\n event.preventDefault();\n\n if (navigateParams.replace) {\n routeConfig.get().history.replace(href, navigateParams.state);\n } else {\n routeConfig.get().history.push(href, navigateParams.state);\n }\n }\n };\n\n const anchorProps = {\n ...outerAnchorProps,\n href,\n onClick: handleClick,\n rel:\n outerAnchorProps.rel ??\n (isExternalNavigation ? 'noopener noreferrer' : undefined),\n };\n\n return asChild && isValidElement(children) ? (\n cloneElement(children, anchorProps)\n ) : (\n <a {...anchorProps} ref={ref}>\n {children}\n </a>\n );\n },\n ),\n) as unknown as LinkComponentType;\n","import { observer } from 'mobx-react-lite';\nimport type {\n AnyAbstractRouteEntity,\n AnyRoute,\n AnyVirtualRoute,\n} from 'mobx-route';\nimport { useRef } from 'react';\nimport { type LoadableConfig, loadable } from 'react-simple-loadable';\n\nexport type RouteViewComponent<TRoute extends AnyAbstractRouteEntity> =\n React.ComponentType<RouteViewProps<TRoute>>;\n\ninterface RouteViewConfigWithoutRoute {\n children?: React.ReactNode | (() => React.ReactNode);\n}\n\nexport interface RouteViewConfigWithRoute<TRoute extends AnyAbstractRouteEntity>\n extends Pick<LoadableConfig, 'loading' | 'preload' | 'throwOnError'> {\n route: TRoute;\n view?: RouteViewComponent<TRoute>;\n loadView?: (route: TRoute) => Promise<RouteViewComponent<TRoute>>;\n /**\n * Case when route is not opened\n */\n fallback?: React.ReactNode;\n children?:\n | React.ReactNode\n | ((\n params: RouteViewProps<TRoute>['params'],\n route: TRoute,\n ) => React.ReactNode);\n}\n\nexport type RouteViewConfig<TRoute extends AnyAbstractRouteEntity> =\n | RouteViewConfigWithRoute<TRoute>\n | RouteViewConfigWithoutRoute;\n\nexport type RouteViewProps<TRoute extends AnyAbstractRouteEntity> = {\n children?: React.ReactNode;\n params: TRoute extends AnyRoute\n ? Exclude<TRoute['params'], null | undefined>\n : TRoute extends AnyVirtualRoute\n ? TRoute['params']\n : never;\n};\n\ntype RouteViewBaseComponent = <TRoute extends AnyAbstractRouteEntity>(\n props: RouteViewConfig<TRoute>,\n) => React.ReactNode;\n\nfunction RouteViewBase<TRoute extends AnyAbstractRouteEntity>(\n props: Readonly<RouteViewConfig<TRoute>>,\n): React.ReactNode {\n // @ts-expect-error redundand pass first argument\n const lazyViewComponentRef = useRef<React.ComponentType<any>>();\n\n let Component: React.ComponentType<any> | undefined;\n\n if (!('route' in props)) {\n return typeof props.children === 'function'\n ? props.children()\n : props.children;\n }\n\n if (!props.route.isOpened) {\n return props.fallback ?? null;\n }\n\n if (props.loadView) {\n if (!lazyViewComponentRef.current) {\n lazyViewComponentRef.current = loadable({\n load: () => props.loadView!(props.route),\n loading: props.loading,\n preload: props.preload,\n throwOnError: props.throwOnError,\n });\n }\n Component = lazyViewComponentRef.current;\n } else {\n Component = props.view;\n }\n\n const params: any = 'params' in props.route ? props.route.params : {};\n\n if (Component) {\n return <Component params={params}>{props.children}</Component>;\n }\n\n if (typeof props.children === 'function') {\n return props.children(params, props.route);\n }\n\n return props.children;\n}\n\nexport const RouteView = observer(RouteViewBase) as RouteViewBaseComponent;\n","import { observer } from 'mobx-react-lite';\nimport {\n type AnyRoute,\n type AnyRouteEntity,\n buildSearchString,\n isRouteEntity,\n type RouteNavigateParams,\n type RouteParams,\n routeConfig,\n} from 'mobx-route';\nimport { isValidElement, useEffect } from 'react';\nimport type { IsPartial, Maybe } from 'yummies/types';\n\ntype LayoutComponent =\n | React.ComponentType<{ children?: React.ReactNode }>\n | React.ComponentType<{ children: React.ReactNode }>;\n\ninterface BaseProps extends RouteNavigateParams {\n children: React.ReactNode;\n layout?: LayoutComponent;\n}\n\ntype PropsWithDefaultRoute<TRoute extends AnyRouteEntity> = BaseProps & {\n otherwise?: TRoute;\n} & (IsPartial<RouteParams<TRoute>> extends true\n ? {\n params?: Maybe<RouteParams<TRoute>>;\n }\n : {\n params: RouteParams<TRoute>;\n });\n\ntype PropsWithDefaultUrl = BaseProps & {\n otherwise?: string;\n};\n\nexport type RouteViewGroupProps<TRoute extends AnyRouteEntity> =\n | PropsWithDefaultRoute<TRoute>\n | PropsWithDefaultUrl;\n\ntype RouteViewGroupComponent = <TRoute extends AnyRouteEntity>(\n props: RouteViewGroupProps<TRoute>,\n) => React.ReactNode;\n\nexport const RouteViewGroup = observer(\n <TRoute extends AnyRouteEntity>({\n children,\n layout: Layout,\n otherwise: otherwiseNavigation,\n // @ts-expect-error\n params,\n ...navigateParams\n }: RouteViewGroupProps<TRoute>) => {\n let activeChildRouteNode: React.ReactNode = null;\n let lastInactiveChildNode: React.ReactNode = null;\n let hasRoutesInOpening = false;\n\n const childNodes: React.ReactNode[] = Array.isArray(children)\n ? children\n : [children];\n\n for (const childNode of childNodes) {\n const isRouteChild =\n isValidElement(childNode) &&\n // @ts-expect-error redundand checks better to wrap in this directive\n isRouteEntity(childNode.props?.route);\n\n if (isRouteChild) {\n const route = (childNode.props as any).route as AnyRoute;\n\n if (route.isOpened) {\n activeChildRouteNode = childNode;\n break;\n } else {\n if (route.isOpening) {\n hasRoutesInOpening = true;\n }\n lastInactiveChildNode = childNode;\n }\n } else {\n lastInactiveChildNode = childNode;\n }\n }\n\n const hasActiveChildNode = !!activeChildRouteNode;\n\n useEffect(() => {\n if (!hasActiveChildNode && !hasRoutesInOpening && otherwiseNavigation) {\n if (typeof otherwiseNavigation === 'string') {\n const history = routeConfig.get().history;\n const url = `${otherwiseNavigation}${buildSearchString(navigateParams.query || {})}`;\n\n if (navigateParams.replace) {\n history.replace(url, navigateParams.state);\n } else {\n history.push(url, navigateParams.state);\n }\n } else if (!otherwiseNavigation.isOpened) {\n otherwiseNavigation.open(params, navigateParams);\n }\n }\n }, [hasActiveChildNode, hasRoutesInOpening, otherwiseNavigation]);\n\n if (otherwiseNavigation && !activeChildRouteNode) {\n return null;\n }\n\n const resultNodeToRender =\n activeChildRouteNode ?? lastInactiveChildNode ?? null;\n\n if (Layout) {\n return <Layout>{resultNodeToRender}</Layout>;\n }\n\n return resultNodeToRender;\n },\n) as unknown as RouteViewGroupComponent;\n"],"names":["observer","forwardRef","useRef","isShallowEqual","useMemo","navigateParams","routeConfig","href","parseSearchString","query","buildSearchString","isValidElement","cloneElement","jsx","loadable","isRouteEntity","useEffect"],"mappings":"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"}
package/react.js CHANGED
@@ -123,22 +123,31 @@ const RouteViewGroup = observer(
123
123
  params,
124
124
  ...navigateParams
125
125
  }) => {
126
- let activeChildNode = null;
126
+ let activeChildRouteNode = null;
127
127
  let lastInactiveChildNode = null;
128
+ let hasRoutesInOpening = false;
128
129
  const childNodes = Array.isArray(children) ? children : [children];
129
130
  for (const childNode of childNodes) {
130
- if (isValidElement(childNode) && // @ts-expect-error redundand checks better to wrap in this directive
131
- isRouteEntity(childNode.props?.route) && // @ts-expect-error redundand checks better to wrap in this directive
132
- childNode.props.route.isOpened) {
133
- activeChildNode = childNode;
134
- break;
131
+ const isRouteChild = isValidElement(childNode) && // @ts-expect-error redundand checks better to wrap in this directive
132
+ isRouteEntity(childNode.props?.route);
133
+ if (isRouteChild) {
134
+ const route = childNode.props.route;
135
+ if (route.isOpened) {
136
+ activeChildRouteNode = childNode;
137
+ break;
138
+ } else {
139
+ if (route.isOpening) {
140
+ hasRoutesInOpening = true;
141
+ }
142
+ lastInactiveChildNode = childNode;
143
+ }
135
144
  } else {
136
145
  lastInactiveChildNode = childNode;
137
146
  }
138
147
  }
139
- const hasActiveChildNode = !!activeChildNode;
148
+ const hasActiveChildNode = !!activeChildRouteNode;
140
149
  useEffect(() => {
141
- if (!hasActiveChildNode && otherwiseNavigation) {
150
+ if (!hasActiveChildNode && !hasRoutesInOpening && otherwiseNavigation) {
142
151
  if (typeof otherwiseNavigation === "string") {
143
152
  const history = routeConfig.get().history;
144
153
  const url = `${otherwiseNavigation}${buildSearchString(navigateParams.query || {})}`;
@@ -151,11 +160,11 @@ const RouteViewGroup = observer(
151
160
  otherwiseNavigation.open(params, navigateParams);
152
161
  }
153
162
  }
154
- }, [hasActiveChildNode, otherwiseNavigation]);
155
- if (otherwiseNavigation && !activeChildNode) {
163
+ }, [hasActiveChildNode, hasRoutesInOpening, otherwiseNavigation]);
164
+ if (otherwiseNavigation && !activeChildRouteNode) {
156
165
  return null;
157
166
  }
158
- const resultNodeToRender = activeChildNode ?? lastInactiveChildNode ?? null;
167
+ const resultNodeToRender = activeChildRouteNode ?? lastInactiveChildNode ?? null;
159
168
  if (Layout) {
160
169
  return /* @__PURE__ */ jsx(Layout, { children: resultNodeToRender });
161
170
  }
package/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","sources":["../src/react/components/link.tsx","../src/react/components/route-view.tsx","../src/react/components/route-view-group.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite';\nimport {\n type AnyRoute,\n buildSearchString,\n type InputPathParams,\n parseSearchString,\n type RouteNavigateParams,\n routeConfig,\n} from 'mobx-route';\nimport {\n type AnchorHTMLAttributes,\n cloneElement,\n forwardRef,\n isValidElement,\n type MouseEvent,\n useMemo,\n useRef,\n} from 'react';\nimport { isShallowEqual } from 'yummies/data';\nimport type { IsPartial } from 'yummies/types';\n\ninterface LinkAnchorProps\n extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\n asChild?: boolean;\n}\n\ntype LinkPathRouteProps<TRoute extends AnyRoute> = {\n to: TRoute;\n} & (IsPartial<InputPathParams<TRoute['path']>> extends true\n ? {\n params?: InputPathParams<TRoute['path']> | null | undefined;\n }\n : { params: InputPathParams<TRoute['path']> });\n\ntype LinkSimpleRouteProps =\n | {\n to: string;\n }\n | {\n href: string;\n };\n\nexport type LinkProps<TRoute extends AnyRoute> = LinkAnchorProps &\n RouteNavigateParams &\n (LinkPathRouteProps<TRoute> | LinkSimpleRouteProps);\n\ntype LinkComponentType = <TRoute extends AnyRoute>(\n props: LinkProps<TRoute>,\n) => React.ReactNode;\n\nexport const Link = observer(\n forwardRef<\n HTMLAnchorElement,\n LinkAnchorProps &\n RouteNavigateParams & {\n params?: any;\n to: string | AnyRoute;\n href: string;\n }\n >(\n (\n {\n to,\n href: outerHref,\n mergeQuery,\n asChild,\n children,\n params,\n // route navigate params\n query,\n replace,\n state,\n ...outerAnchorProps\n },\n ref,\n ) => {\n const isExternalNavigation =\n outerAnchorProps.target === '_blank' ||\n outerAnchorProps.target === 'blank';\n const queryDataRef = useRef<RouteNavigateParams['query']>(query);\n\n if (!isShallowEqual(queryDataRef.current, query)) {\n queryDataRef.current = query;\n }\n\n const { href, navigateParams } = useMemo(() => {\n const navigateParams: RouteNavigateParams = {\n mergeQuery,\n query,\n replace,\n state,\n };\n\n const cfg = routeConfig.get();\n\n let href: string;\n\n if (outerHref) {\n href = outerHref;\n } else {\n if (typeof to === 'string') {\n const isNeedToMergeQuery =\n navigateParams.mergeQuery ?? cfg.mergeQuery;\n\n const [path, ...querySegments] = to.split('?');\n\n const existedQuery = parseSearchString(querySegments.join('?'));\n\n const query = {\n ...(isNeedToMergeQuery ? cfg.queryParams.data : {}),\n ...existedQuery,\n ...navigateParams.query,\n };\n\n href = `${path}${buildSearchString(query)}`;\n } else {\n href = to.createUrl(\n params,\n navigateParams.query,\n navigateParams.mergeQuery,\n );\n }\n }\n\n return {\n href: cfg.formatLinkHref?.(href) ?? href,\n navigateParams,\n };\n }, [mergeQuery, replace, state, to, queryDataRef.current]);\n\n const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {\n if (\n isExternalNavigation ||\n event.ctrlKey ||\n event.metaKey ||\n event.altKey ||\n event.shiftKey ||\n event.button !== 0\n )\n return;\n\n outerAnchorProps.onClick?.(event);\n\n if (!event.defaultPrevented) {\n event.preventDefault();\n\n if (navigateParams.replace) {\n routeConfig.get().history.replace(href, navigateParams.state);\n } else {\n routeConfig.get().history.push(href, navigateParams.state);\n }\n }\n };\n\n const anchorProps = {\n ...outerAnchorProps,\n href,\n onClick: handleClick,\n rel:\n outerAnchorProps.rel ??\n (isExternalNavigation ? 'noopener noreferrer' : undefined),\n };\n\n return asChild && isValidElement(children) ? (\n cloneElement(children, anchorProps)\n ) : (\n <a {...anchorProps} ref={ref}>\n {children}\n </a>\n );\n },\n ),\n) as unknown as LinkComponentType;\n","import { observer } from 'mobx-react-lite';\nimport type {\n AnyAbstractRouteEntity,\n AnyRoute,\n AnyVirtualRoute,\n} from 'mobx-route';\nimport { useRef } from 'react';\nimport { type LoadableConfig, loadable } from 'react-simple-loadable';\n\nexport type RouteViewComponent<TRoute extends AnyAbstractRouteEntity> =\n React.ComponentType<RouteViewProps<TRoute>>;\n\ninterface RouteViewConfigWithoutRoute {\n children?: React.ReactNode | (() => React.ReactNode);\n}\n\nexport interface RouteViewConfigWithRoute<TRoute extends AnyAbstractRouteEntity>\n extends Pick<LoadableConfig, 'loading' | 'preload' | 'throwOnError'> {\n route: TRoute;\n view?: RouteViewComponent<TRoute>;\n loadView?: (route: TRoute) => Promise<RouteViewComponent<TRoute>>;\n /**\n * Case when route is not opened\n */\n fallback?: React.ReactNode;\n children?:\n | React.ReactNode\n | ((\n params: RouteViewProps<TRoute>['params'],\n route: TRoute,\n ) => React.ReactNode);\n}\n\nexport type RouteViewConfig<TRoute extends AnyAbstractRouteEntity> =\n | RouteViewConfigWithRoute<TRoute>\n | RouteViewConfigWithoutRoute;\n\nexport type RouteViewProps<TRoute extends AnyAbstractRouteEntity> = {\n children?: React.ReactNode;\n params: TRoute extends AnyRoute\n ? Exclude<TRoute['params'], null | undefined>\n : TRoute extends AnyVirtualRoute\n ? TRoute['params']\n : never;\n};\n\ntype RouteViewBaseComponent = <TRoute extends AnyAbstractRouteEntity>(\n props: RouteViewConfig<TRoute>,\n) => React.ReactNode;\n\nfunction RouteViewBase<TRoute extends AnyAbstractRouteEntity>(\n props: Readonly<RouteViewConfig<TRoute>>,\n): React.ReactNode {\n // @ts-expect-error redundand pass first argument\n const lazyViewComponentRef = useRef<React.ComponentType<any>>();\n\n let Component: React.ComponentType<any> | undefined;\n\n if (!('route' in props)) {\n return typeof props.children === 'function'\n ? props.children()\n : props.children;\n }\n\n if (!props.route.isOpened) {\n return props.fallback ?? null;\n }\n\n if (props.loadView) {\n if (!lazyViewComponentRef.current) {\n lazyViewComponentRef.current = loadable({\n load: () => props.loadView!(props.route),\n loading: props.loading,\n preload: props.preload,\n throwOnError: props.throwOnError,\n });\n }\n Component = lazyViewComponentRef.current;\n } else {\n Component = props.view;\n }\n\n const params: any = 'params' in props.route ? props.route.params : {};\n\n if (Component) {\n return <Component params={params}>{props.children}</Component>;\n }\n\n if (typeof props.children === 'function') {\n return props.children(params, props.route);\n }\n\n return props.children;\n}\n\nexport const RouteView = observer(RouteViewBase) as RouteViewBaseComponent;\n","import { observer } from 'mobx-react-lite';\nimport {\n type AnyRouteEntity,\n buildSearchString,\n isRouteEntity,\n type RouteNavigateParams,\n type RouteParams,\n routeConfig,\n} from 'mobx-route';\nimport { isValidElement, useEffect } from 'react';\nimport type { IsPartial, Maybe } from 'yummies/types';\n\ntype LayoutComponent =\n | React.ComponentType<{ children?: React.ReactNode }>\n | React.ComponentType<{ children: React.ReactNode }>;\n\ninterface BaseProps extends RouteNavigateParams {\n children: React.ReactNode;\n layout?: LayoutComponent;\n}\n\ntype PropsWithDefaultRoute<TRoute extends AnyRouteEntity> = BaseProps & {\n otherwise?: TRoute;\n} & (IsPartial<RouteParams<TRoute>> extends true\n ? {\n params?: Maybe<RouteParams<TRoute>>;\n }\n : {\n params: RouteParams<TRoute>;\n });\n\ntype PropsWithDefaultUrl = BaseProps & {\n otherwise?: string;\n};\n\nexport type RouteViewGroupProps<TRoute extends AnyRouteEntity> =\n | PropsWithDefaultRoute<TRoute>\n | PropsWithDefaultUrl;\n\ntype RouteViewGroupComponent = <TRoute extends AnyRouteEntity>(\n props: RouteViewGroupProps<TRoute>,\n) => React.ReactNode;\n\nexport const RouteViewGroup = observer(\n <TRoute extends AnyRouteEntity>({\n children,\n layout: Layout,\n otherwise: otherwiseNavigation,\n // @ts-expect-error\n params,\n ...navigateParams\n }: RouteViewGroupProps<TRoute>) => {\n let activeChildNode: React.ReactNode = null;\n let lastInactiveChildNode: React.ReactNode = null;\n\n const childNodes: React.ReactNode[] = Array.isArray(children)\n ? children\n : [children];\n\n for (const childNode of childNodes) {\n if (\n isValidElement(childNode) &&\n // @ts-expect-error redundand checks better to wrap in this directive\n isRouteEntity(childNode.props?.route) &&\n // @ts-expect-error redundand checks better to wrap in this directive\n childNode.props.route.isOpened\n ) {\n activeChildNode = childNode;\n break;\n } else {\n lastInactiveChildNode = childNode;\n }\n }\n\n const hasActiveChildNode = !!activeChildNode;\n\n useEffect(() => {\n if (!hasActiveChildNode && otherwiseNavigation) {\n if (typeof otherwiseNavigation === 'string') {\n const history = routeConfig.get().history;\n const url = `${otherwiseNavigation}${buildSearchString(navigateParams.query || {})}`;\n\n if (navigateParams.replace) {\n history.replace(url, navigateParams.state);\n } else {\n history.push(url, navigateParams.state);\n }\n } else if (!otherwiseNavigation.isOpened) {\n otherwiseNavigation.open(params, navigateParams);\n }\n }\n }, [hasActiveChildNode, otherwiseNavigation]);\n\n if (otherwiseNavigation && !activeChildNode) {\n return null;\n }\n\n const resultNodeToRender = activeChildNode ?? lastInactiveChildNode ?? null;\n\n if (Layout) {\n return <Layout>{resultNodeToRender}</Layout>;\n }\n\n return resultNodeToRender;\n },\n) as unknown as RouteViewGroupComponent;\n"],"names":["navigateParams","href","query"],"mappings":"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"}
1
+ {"version":3,"file":"react.js","sources":["../src/react/components/link.tsx","../src/react/components/route-view.tsx","../src/react/components/route-view-group.tsx"],"sourcesContent":["import { observer } from 'mobx-react-lite';\nimport {\n type AnyRoute,\n buildSearchString,\n type InputPathParams,\n parseSearchString,\n type RouteNavigateParams,\n routeConfig,\n} from 'mobx-route';\nimport {\n type AnchorHTMLAttributes,\n cloneElement,\n forwardRef,\n isValidElement,\n type MouseEvent,\n useMemo,\n useRef,\n} from 'react';\nimport { isShallowEqual } from 'yummies/data';\nimport type { IsPartial } from 'yummies/types';\n\ninterface LinkAnchorProps\n extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {\n asChild?: boolean;\n}\n\ntype LinkPathRouteProps<TRoute extends AnyRoute> = {\n to: TRoute;\n} & (IsPartial<InputPathParams<TRoute['path']>> extends true\n ? {\n params?: InputPathParams<TRoute['path']> | null | undefined;\n }\n : { params: InputPathParams<TRoute['path']> });\n\ntype LinkSimpleRouteProps =\n | {\n to: string;\n }\n | {\n href: string;\n };\n\nexport type LinkProps<TRoute extends AnyRoute> = LinkAnchorProps &\n RouteNavigateParams &\n (LinkPathRouteProps<TRoute> | LinkSimpleRouteProps);\n\ntype LinkComponentType = <TRoute extends AnyRoute>(\n props: LinkProps<TRoute>,\n) => React.ReactNode;\n\nexport const Link = observer(\n forwardRef<\n HTMLAnchorElement,\n LinkAnchorProps &\n RouteNavigateParams & {\n params?: any;\n to: string | AnyRoute;\n href: string;\n }\n >(\n (\n {\n to,\n href: outerHref,\n mergeQuery,\n asChild,\n children,\n params,\n // route navigate params\n query,\n replace,\n state,\n ...outerAnchorProps\n },\n ref,\n ) => {\n const isExternalNavigation =\n outerAnchorProps.target === '_blank' ||\n outerAnchorProps.target === 'blank';\n const queryDataRef = useRef<RouteNavigateParams['query']>(query);\n\n if (!isShallowEqual(queryDataRef.current, query)) {\n queryDataRef.current = query;\n }\n\n const { href, navigateParams } = useMemo(() => {\n const navigateParams: RouteNavigateParams = {\n mergeQuery,\n query,\n replace,\n state,\n };\n\n const cfg = routeConfig.get();\n\n let href: string;\n\n if (outerHref) {\n href = outerHref;\n } else {\n if (typeof to === 'string') {\n const isNeedToMergeQuery =\n navigateParams.mergeQuery ?? cfg.mergeQuery;\n\n const [path, ...querySegments] = to.split('?');\n\n const existedQuery = parseSearchString(querySegments.join('?'));\n\n const query = {\n ...(isNeedToMergeQuery ? cfg.queryParams.data : {}),\n ...existedQuery,\n ...navigateParams.query,\n };\n\n href = `${path}${buildSearchString(query)}`;\n } else {\n href = to.createUrl(\n params,\n navigateParams.query,\n navigateParams.mergeQuery,\n );\n }\n }\n\n return {\n href: cfg.formatLinkHref?.(href) ?? href,\n navigateParams,\n };\n }, [mergeQuery, replace, state, to, queryDataRef.current]);\n\n const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {\n if (\n isExternalNavigation ||\n event.ctrlKey ||\n event.metaKey ||\n event.altKey ||\n event.shiftKey ||\n event.button !== 0\n )\n return;\n\n outerAnchorProps.onClick?.(event);\n\n if (!event.defaultPrevented) {\n event.preventDefault();\n\n if (navigateParams.replace) {\n routeConfig.get().history.replace(href, navigateParams.state);\n } else {\n routeConfig.get().history.push(href, navigateParams.state);\n }\n }\n };\n\n const anchorProps = {\n ...outerAnchorProps,\n href,\n onClick: handleClick,\n rel:\n outerAnchorProps.rel ??\n (isExternalNavigation ? 'noopener noreferrer' : undefined),\n };\n\n return asChild && isValidElement(children) ? (\n cloneElement(children, anchorProps)\n ) : (\n <a {...anchorProps} ref={ref}>\n {children}\n </a>\n );\n },\n ),\n) as unknown as LinkComponentType;\n","import { observer } from 'mobx-react-lite';\nimport type {\n AnyAbstractRouteEntity,\n AnyRoute,\n AnyVirtualRoute,\n} from 'mobx-route';\nimport { useRef } from 'react';\nimport { type LoadableConfig, loadable } from 'react-simple-loadable';\n\nexport type RouteViewComponent<TRoute extends AnyAbstractRouteEntity> =\n React.ComponentType<RouteViewProps<TRoute>>;\n\ninterface RouteViewConfigWithoutRoute {\n children?: React.ReactNode | (() => React.ReactNode);\n}\n\nexport interface RouteViewConfigWithRoute<TRoute extends AnyAbstractRouteEntity>\n extends Pick<LoadableConfig, 'loading' | 'preload' | 'throwOnError'> {\n route: TRoute;\n view?: RouteViewComponent<TRoute>;\n loadView?: (route: TRoute) => Promise<RouteViewComponent<TRoute>>;\n /**\n * Case when route is not opened\n */\n fallback?: React.ReactNode;\n children?:\n | React.ReactNode\n | ((\n params: RouteViewProps<TRoute>['params'],\n route: TRoute,\n ) => React.ReactNode);\n}\n\nexport type RouteViewConfig<TRoute extends AnyAbstractRouteEntity> =\n | RouteViewConfigWithRoute<TRoute>\n | RouteViewConfigWithoutRoute;\n\nexport type RouteViewProps<TRoute extends AnyAbstractRouteEntity> = {\n children?: React.ReactNode;\n params: TRoute extends AnyRoute\n ? Exclude<TRoute['params'], null | undefined>\n : TRoute extends AnyVirtualRoute\n ? TRoute['params']\n : never;\n};\n\ntype RouteViewBaseComponent = <TRoute extends AnyAbstractRouteEntity>(\n props: RouteViewConfig<TRoute>,\n) => React.ReactNode;\n\nfunction RouteViewBase<TRoute extends AnyAbstractRouteEntity>(\n props: Readonly<RouteViewConfig<TRoute>>,\n): React.ReactNode {\n // @ts-expect-error redundand pass first argument\n const lazyViewComponentRef = useRef<React.ComponentType<any>>();\n\n let Component: React.ComponentType<any> | undefined;\n\n if (!('route' in props)) {\n return typeof props.children === 'function'\n ? props.children()\n : props.children;\n }\n\n if (!props.route.isOpened) {\n return props.fallback ?? null;\n }\n\n if (props.loadView) {\n if (!lazyViewComponentRef.current) {\n lazyViewComponentRef.current = loadable({\n load: () => props.loadView!(props.route),\n loading: props.loading,\n preload: props.preload,\n throwOnError: props.throwOnError,\n });\n }\n Component = lazyViewComponentRef.current;\n } else {\n Component = props.view;\n }\n\n const params: any = 'params' in props.route ? props.route.params : {};\n\n if (Component) {\n return <Component params={params}>{props.children}</Component>;\n }\n\n if (typeof props.children === 'function') {\n return props.children(params, props.route);\n }\n\n return props.children;\n}\n\nexport const RouteView = observer(RouteViewBase) as RouteViewBaseComponent;\n","import { observer } from 'mobx-react-lite';\nimport {\n type AnyRoute,\n type AnyRouteEntity,\n buildSearchString,\n isRouteEntity,\n type RouteNavigateParams,\n type RouteParams,\n routeConfig,\n} from 'mobx-route';\nimport { isValidElement, useEffect } from 'react';\nimport type { IsPartial, Maybe } from 'yummies/types';\n\ntype LayoutComponent =\n | React.ComponentType<{ children?: React.ReactNode }>\n | React.ComponentType<{ children: React.ReactNode }>;\n\ninterface BaseProps extends RouteNavigateParams {\n children: React.ReactNode;\n layout?: LayoutComponent;\n}\n\ntype PropsWithDefaultRoute<TRoute extends AnyRouteEntity> = BaseProps & {\n otherwise?: TRoute;\n} & (IsPartial<RouteParams<TRoute>> extends true\n ? {\n params?: Maybe<RouteParams<TRoute>>;\n }\n : {\n params: RouteParams<TRoute>;\n });\n\ntype PropsWithDefaultUrl = BaseProps & {\n otherwise?: string;\n};\n\nexport type RouteViewGroupProps<TRoute extends AnyRouteEntity> =\n | PropsWithDefaultRoute<TRoute>\n | PropsWithDefaultUrl;\n\ntype RouteViewGroupComponent = <TRoute extends AnyRouteEntity>(\n props: RouteViewGroupProps<TRoute>,\n) => React.ReactNode;\n\nexport const RouteViewGroup = observer(\n <TRoute extends AnyRouteEntity>({\n children,\n layout: Layout,\n otherwise: otherwiseNavigation,\n // @ts-expect-error\n params,\n ...navigateParams\n }: RouteViewGroupProps<TRoute>) => {\n let activeChildRouteNode: React.ReactNode = null;\n let lastInactiveChildNode: React.ReactNode = null;\n let hasRoutesInOpening = false;\n\n const childNodes: React.ReactNode[] = Array.isArray(children)\n ? children\n : [children];\n\n for (const childNode of childNodes) {\n const isRouteChild =\n isValidElement(childNode) &&\n // @ts-expect-error redundand checks better to wrap in this directive\n isRouteEntity(childNode.props?.route);\n\n if (isRouteChild) {\n const route = (childNode.props as any).route as AnyRoute;\n\n if (route.isOpened) {\n activeChildRouteNode = childNode;\n break;\n } else {\n if (route.isOpening) {\n hasRoutesInOpening = true;\n }\n lastInactiveChildNode = childNode;\n }\n } else {\n lastInactiveChildNode = childNode;\n }\n }\n\n const hasActiveChildNode = !!activeChildRouteNode;\n\n useEffect(() => {\n if (!hasActiveChildNode && !hasRoutesInOpening && otherwiseNavigation) {\n if (typeof otherwiseNavigation === 'string') {\n const history = routeConfig.get().history;\n const url = `${otherwiseNavigation}${buildSearchString(navigateParams.query || {})}`;\n\n if (navigateParams.replace) {\n history.replace(url, navigateParams.state);\n } else {\n history.push(url, navigateParams.state);\n }\n } else if (!otherwiseNavigation.isOpened) {\n otherwiseNavigation.open(params, navigateParams);\n }\n }\n }, [hasActiveChildNode, hasRoutesInOpening, otherwiseNavigation]);\n\n if (otherwiseNavigation && !activeChildRouteNode) {\n return null;\n }\n\n const resultNodeToRender =\n activeChildRouteNode ?? lastInactiveChildNode ?? null;\n\n if (Layout) {\n return <Layout>{resultNodeToRender}</Layout>;\n }\n\n return resultNodeToRender;\n },\n) as unknown as RouteViewGroupComponent;\n"],"names":["navigateParams","href","query"],"mappings":"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"}