@cratis/scene.react 4.13.0 → 4.14.0

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.
Files changed (89) hide show
  1. package/dist/cjs/core/coreComponents.js +1 -1
  2. package/dist/cjs/index.js +12 -2
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/navigation/SceneNavigationContext.js.map +1 -1
  5. package/dist/cjs/navigation/SceneNavigationHost.js +19 -44
  6. package/dist/cjs/navigation/SceneNavigationHost.js.map +1 -1
  7. package/dist/cjs/navigation/createBrowserSceneHistory.js +39 -0
  8. package/dist/cjs/navigation/createBrowserSceneHistory.js.map +1 -0
  9. package/dist/cjs/navigation/createMemorySceneHistory.js +53 -0
  10. package/dist/cjs/navigation/createMemorySceneHistory.js.map +1 -0
  11. package/dist/cjs/navigation/matchSceneRoute.js +43 -0
  12. package/dist/cjs/navigation/matchSceneRoute.js.map +1 -0
  13. package/dist/cjs/navigation/resolveDestination.js +1 -0
  14. package/dist/cjs/navigation/resolveDestination.js.map +1 -1
  15. package/dist/cjs/navigation/sceneRoutesFrom.js +37 -0
  16. package/dist/cjs/navigation/sceneRoutesFrom.js.map +1 -0
  17. package/dist/cjs/navigation/useSceneHistoryNavigation.js +112 -0
  18. package/dist/cjs/navigation/useSceneHistoryNavigation.js.map +1 -0
  19. package/dist/esm/core/coreComponents.js +1 -1
  20. package/dist/esm/index.js +6 -1
  21. package/dist/esm/index.js.map +1 -1
  22. package/dist/esm/navigation/DestinationResolution.d.ts +1 -0
  23. package/dist/esm/navigation/DestinationResolution.d.ts.map +1 -1
  24. package/dist/esm/navigation/DialogResult.d.ts +5 -0
  25. package/dist/esm/navigation/DialogResult.d.ts.map +1 -0
  26. package/dist/esm/navigation/DialogResult.js +2 -0
  27. package/dist/esm/navigation/DialogResult.js.map +1 -0
  28. package/dist/esm/navigation/SceneHistory.d.ts +10 -0
  29. package/dist/esm/navigation/SceneHistory.d.ts.map +1 -0
  30. package/dist/esm/navigation/SceneHistory.js +2 -0
  31. package/dist/esm/navigation/SceneHistory.js.map +1 -0
  32. package/dist/esm/navigation/SceneNavigationContext.d.ts +6 -1
  33. package/dist/esm/navigation/SceneNavigationContext.d.ts.map +1 -1
  34. package/dist/esm/navigation/SceneNavigationContext.js.map +1 -1
  35. package/dist/esm/navigation/SceneNavigationHost.d.ts +7 -2
  36. package/dist/esm/navigation/SceneNavigationHost.d.ts.map +1 -1
  37. package/dist/esm/navigation/SceneNavigationHost.js +20 -45
  38. package/dist/esm/navigation/SceneNavigationHost.js.map +1 -1
  39. package/dist/esm/navigation/SceneNavigationState.d.ts +8 -0
  40. package/dist/esm/navigation/SceneNavigationState.d.ts.map +1 -0
  41. package/dist/esm/navigation/SceneNavigationState.js +2 -0
  42. package/dist/esm/navigation/SceneNavigationState.js.map +1 -0
  43. package/dist/esm/navigation/SceneRoute.d.ts +6 -0
  44. package/dist/esm/navigation/SceneRoute.d.ts.map +1 -0
  45. package/dist/esm/navigation/SceneRoute.js +2 -0
  46. package/dist/esm/navigation/SceneRoute.js.map +1 -0
  47. package/dist/esm/navigation/createBrowserSceneHistory.d.ts +4 -0
  48. package/dist/esm/navigation/createBrowserSceneHistory.d.ts.map +1 -0
  49. package/dist/esm/navigation/createBrowserSceneHistory.js +37 -0
  50. package/dist/esm/navigation/createBrowserSceneHistory.js.map +1 -0
  51. package/dist/esm/navigation/createMemorySceneHistory.d.ts +7 -0
  52. package/dist/esm/navigation/createMemorySceneHistory.d.ts.map +1 -0
  53. package/dist/esm/navigation/createMemorySceneHistory.js +51 -0
  54. package/dist/esm/navigation/createMemorySceneHistory.js.map +1 -0
  55. package/dist/esm/navigation/index.d.ts +10 -1
  56. package/dist/esm/navigation/index.d.ts.map +1 -1
  57. package/dist/esm/navigation/index.js +10 -1
  58. package/dist/esm/navigation/index.js.map +1 -1
  59. package/dist/esm/navigation/matchSceneRoute.d.ts +7 -0
  60. package/dist/esm/navigation/matchSceneRoute.d.ts.map +1 -0
  61. package/dist/esm/navigation/matchSceneRoute.js +41 -0
  62. package/dist/esm/navigation/matchSceneRoute.js.map +1 -0
  63. package/dist/esm/navigation/resolveDestination.d.ts.map +1 -1
  64. package/dist/esm/navigation/resolveDestination.js +1 -0
  65. package/dist/esm/navigation/resolveDestination.js.map +1 -1
  66. package/dist/esm/navigation/sceneRoutesFrom.d.ts +4 -0
  67. package/dist/esm/navigation/sceneRoutesFrom.d.ts.map +1 -0
  68. package/dist/esm/navigation/sceneRoutesFrom.js +35 -0
  69. package/dist/esm/navigation/sceneRoutesFrom.js.map +1 -0
  70. package/dist/esm/navigation/useSceneHistoryNavigation.d.ts +6 -0
  71. package/dist/esm/navigation/useSceneHistoryNavigation.d.ts.map +1 -0
  72. package/dist/esm/navigation/useSceneHistoryNavigation.js +110 -0
  73. package/dist/esm/navigation/useSceneHistoryNavigation.js.map +1 -0
  74. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  75. package/navigation/DestinationResolution.ts +3 -0
  76. package/navigation/DialogResult.ts +8 -0
  77. package/navigation/SceneHistory.ts +24 -0
  78. package/navigation/SceneNavigationContext.tsx +16 -1
  79. package/navigation/SceneNavigationHost.tsx +30 -40
  80. package/navigation/SceneNavigationState.ts +19 -0
  81. package/navigation/SceneRoute.ts +12 -0
  82. package/navigation/createBrowserSceneHistory.ts +40 -0
  83. package/navigation/createMemorySceneHistory.ts +44 -0
  84. package/navigation/index.ts +10 -1
  85. package/navigation/matchSceneRoute.ts +45 -0
  86. package/navigation/resolveDestination.ts +1 -0
  87. package/navigation/sceneRoutesFrom.ts +24 -0
  88. package/navigation/useSceneHistoryNavigation.ts +80 -0
  89. package/package.json +5 -5
@@ -2,8 +2,8 @@
2
2
 
3
3
  require('react/jsx-runtime');
4
4
  require('react');
5
- require('@cratis/scene.model');
6
5
  require('@cratis/scene.engine');
6
+ require('@cratis/scene.model');
7
7
  require('../interactions/useInteractions.js');
8
8
  require('../interactions/NotificationHost.js');
9
9
  var ScenePackageBundle = require('../packages/ScenePackageBundle.js');
package/dist/cjs/index.js CHANGED
@@ -19,10 +19,15 @@ var NavBar = require('./NavBar/NavBar.js');
19
19
  var extractNavigationItem = require('./NavBar/extractNavigationItem.js');
20
20
  var SceneNavigationContext = require('./navigation/SceneNavigationContext.js');
21
21
  var SceneNavigationHost = require('./navigation/SceneNavigationHost.js');
22
- var resolveDestination = require('./navigation/resolveDestination.js');
22
+ var createBrowserSceneHistory = require('./navigation/createBrowserSceneHistory.js');
23
+ var createMemorySceneHistory = require('./navigation/createMemorySceneHistory.js');
23
24
  var diagnoseRenderedNavigation = require('./navigation/diagnoseRenderedNavigation.js');
24
25
  var identityRoute = require('./navigation/identityRoute.js');
26
+ var matchSceneRoute = require('./navigation/matchSceneRoute.js');
25
27
  var navigationRouteKey = require('./navigation/navigationRouteKey.js');
28
+ var resolveDestination = require('./navigation/resolveDestination.js');
29
+ var sceneRoutesFrom = require('./navigation/sceneRoutesFrom.js');
30
+ var useSceneHistoryNavigation = require('./navigation/useSceneHistoryNavigation.js');
26
31
  var IconAdapterRegistry = require('./icons/IconAdapterRegistry.js');
27
32
  var IconAdapterContext = require('./icons/IconAdapterContext.js');
28
33
  var useIconGlyph = require('./icons/useIconGlyph.js');
@@ -90,10 +95,15 @@ exports.extractNavigationItem = extractNavigationItem.extractNavigationItem;
90
95
  exports.SceneNavigationProvider = SceneNavigationContext.SceneNavigationProvider;
91
96
  exports.useSceneNavigation = SceneNavigationContext.useSceneNavigation;
92
97
  exports.SceneNavigationHost = SceneNavigationHost.SceneNavigationHost;
93
- exports.resolveDestination = resolveDestination.resolveDestination;
98
+ exports.createBrowserSceneHistory = createBrowserSceneHistory.createBrowserSceneHistory;
99
+ exports.createMemorySceneHistory = createMemorySceneHistory.createMemorySceneHistory;
94
100
  exports.diagnoseRenderedNavigation = diagnoseRenderedNavigation.diagnoseRenderedNavigation;
95
101
  exports.identityRoute = identityRoute.identityRoute;
102
+ exports.matchSceneRoute = matchSceneRoute.matchSceneRoute;
96
103
  exports.navigationRouteKey = navigationRouteKey.navigationRouteKey;
104
+ exports.resolveDestination = resolveDestination.resolveDestination;
105
+ exports.sceneRoutesFrom = sceneRoutesFrom.sceneRoutesFrom;
106
+ exports.useSceneHistoryNavigation = useSceneHistoryNavigation.useSceneHistoryNavigation;
97
107
  exports.IconAdapterRegistry = IconAdapterRegistry.IconAdapterRegistry;
98
108
  exports.createIconAdapterRegistry = IconAdapterRegistry.createIconAdapterRegistry;
99
109
  exports.IconAdapterProvider = IconAdapterContext.IconAdapterProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"SceneNavigationContext.js","sources":["../../../navigation/SceneNavigationContext.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { ReactNode, createContext, useContext } from 'react';\nimport { BindingScope } from '@cratis/scene.engine';\nimport { DestinationReference } from '@cratis/scene.model';\nimport { DestinationResolution } from './DestinationResolution';\n\nexport interface SceneNavigationContextValue {\n currentScreen: string;\n currentUrl?: string;\n currentOutlet?: string;\n currentDialog?: string;\n bindingScope: BindingScope;\n navigate(destination: DestinationReference): DestinationResolution;\n closeDialog(): void;\n}\n\nconst Context = createContext<SceneNavigationContextValue | undefined>(undefined);\n\nexport interface SceneNavigationProviderProps {\n value: SceneNavigationContextValue;\n children: ReactNode;\n}\n\nexport function SceneNavigationProvider({ value, children }: SceneNavigationProviderProps) {\n return <Context.Provider value={value}>{children}</Context.Provider>;\n}\n\nexport function useSceneNavigation(): SceneNavigationContextValue {\n const context = useContext(Context);\n if (!context) throw new Error('No Scene navigation context is available.');\n return context;\n}\n"],"names":["Context","createContext","undefined","SceneNavigationProvider","value","children","_jsx","Provider","useSceneNavigation","context","useContext","Error"],"mappings":";;;;;AAkBA,MAAMA,wBAAUC,mBAAAA,CAAuDC,SAAAA,CAAAA;AAOhE,SAASC,uBAAAA,CAAwB,EAAEC,KAAK,EAAEC,QAAQ,EAAgC,EAAA;IACrF,qBAAOC,cAAA,CAACN,QAAQO,QAAQ,EAAA;QAACH,KAAAA,EAAOA,KAAAA;AAAQC,QAAAA,QAAAA,EAAAA;;AAC5C;AAEO,SAASG,kBAAAA,GAAAA;AACZ,IAAA,MAAMC,UAAUC,gBAAAA,CAAWV,OAAAA,CAAAA;AAC3B,IAAA,IAAI,CAACS,OAAAA,EAAS,MAAM,IAAIE,KAAAA,CAAM,2CAAA,CAAA;IAC9B,OAAOF,OAAAA;AACX;;;"}
1
+ {"version":3,"file":"SceneNavigationContext.js","sources":["../../../navigation/SceneNavigationContext.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { ReactNode, createContext, useContext } from 'react';\nimport { BindingScope } from '@cratis/scene.engine';\nimport { DestinationReference } from '@cratis/scene.model';\nimport { DestinationResolution } from './DestinationResolution';\nimport { DialogResult } from './DialogResult';\n\nexport interface SceneNavigationContextValue {\n currentScreen: string;\n currentUrl?: string;\n currentOutlet?: string;\n currentDialog?: string;\n\n /** The route parameters of the current entry - from the destination that opened it, or the deep link. */\n currentParameters: Record<string, string>;\n\n /** What the last dialog returned when it closed. */\n dialogResult?: DialogResult;\n\n /** A deep link that matched no route; the host fell back to its initial screen. */\n unresolvedUrl?: string;\n bindingScope: BindingScope;\n navigate(destination: DestinationReference): DestinationResolution;\n\n /** Closes the open dialog, returning to the entry that opened it, and records its result. */\n closeDialog(result?: unknown): void;\n\n /** Goes back one history entry. */\n back(): void;\n}\n\nconst Context = createContext<SceneNavigationContextValue | undefined>(undefined);\n\nexport interface SceneNavigationProviderProps {\n value: SceneNavigationContextValue;\n children: ReactNode;\n}\n\nexport function SceneNavigationProvider({ value, children }: SceneNavigationProviderProps) {\n return <Context.Provider value={value}>{children}</Context.Provider>;\n}\n\nexport function useSceneNavigation(): SceneNavigationContextValue {\n const context = useContext(Context);\n if (!context) throw new Error('No Scene navigation context is available.');\n return context;\n}\n"],"names":["Context","createContext","undefined","SceneNavigationProvider","value","children","_jsx","Provider","useSceneNavigation","context","useContext","Error"],"mappings":";;;;;AAiCA,MAAMA,wBAAUC,mBAAAA,CAAuDC,SAAAA,CAAAA;AAOhE,SAASC,uBAAAA,CAAwB,EAAEC,KAAK,EAAEC,QAAQ,EAAgC,EAAA;IACrF,qBAAOC,cAAA,CAACN,QAAQO,QAAQ,EAAA;QAACH,KAAAA,EAAOA,KAAAA;AAAQC,QAAAA,QAAAA,EAAAA;;AAC5C;AAEO,SAASG,kBAAAA,GAAAA;AACZ,IAAA,MAAMC,UAAUC,gBAAAA,CAAWV,OAAAA,CAAAA;AAC3B,IAAA,IAAI,CAACS,OAAAA,EAAS,MAAM,IAAIE,KAAAA,CAAM,2CAAA,CAAA;IAC9B,OAAOF,OAAAA;AACX;;;"}
@@ -2,59 +2,32 @@
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var react = require('react');
5
- var scene_model = require('@cratis/scene.model');
6
5
  var SceneElementView = require('../SceneElementView.js');
6
+ var createMemorySceneHistory = require('./createMemorySceneHistory.js');
7
7
  var SceneNavigationContext = require('./SceneNavigationContext.js');
8
- var resolveDestination = require('./resolveDestination.js');
8
+ var sceneRoutesFrom = require('./sceneRoutesFrom.js');
9
+ var useSceneHistoryNavigation = require('./useSceneHistoryNavigation.js');
9
10
 
10
11
  /**
11
12
  * Executes Scene destinations against an in-memory rendered hierarchy: screen routes replace outlets,
12
- * dialog destinations render dialog content, and external destinations only report their resolved action.
13
- */ function SceneNavigationHost({ screens, dialogs = {}, initialScreen, registry, bindingScope = {} }) {
14
- const [currentScreen, setCurrentScreen] = react.useState(initialScreen);
15
- const [currentUrl, setCurrentUrl] = react.useState(initialScreen);
16
- const [currentOutlet, setCurrentOutlet] = react.useState();
17
- const [currentDialog, setCurrentDialog] = react.useState();
18
- const navigate = react.useCallback((destination)=>{
19
- const resolution = resolveDestination.resolveDestination(destination, bindingScope);
20
- if (resolution.kind === scene_model.DestinationKind.Dialog) {
21
- setCurrentDialog(resolution.dialog);
22
- return resolution;
23
- }
24
- if (resolution.kind !== scene_model.DestinationKind.External) {
25
- setCurrentScreen(destination.screen ?? destination.slice ?? currentScreen);
26
- setCurrentOutlet(resolution.outlet);
27
- }
28
- setCurrentUrl(resolution.url);
29
- return resolution;
30
- }, [
31
- bindingScope,
32
- currentScreen
13
+ * dialog destinations render dialog content over the screen that opened them, and external destinations
14
+ * only report their resolved action. Every navigation is a history entry.
15
+ */ function SceneNavigationHost({ screens, dialogs = {}, initialScreen, registry, bindingScope = {}, history, destinations, routes }) {
16
+ const [memoryHistory] = react.useState(()=>createMemorySceneHistory.createMemorySceneHistory());
17
+ const routeTable = react.useMemo(()=>routes ?? sceneRoutesFrom.sceneRoutesFrom(Object.keys(screens), destinations), [
18
+ destinations,
19
+ routes,
20
+ screens
33
21
  ]);
34
- const value = react.useMemo(()=>({
35
- currentScreen,
36
- currentUrl,
37
- currentOutlet,
38
- currentDialog,
39
- bindingScope,
40
- navigate,
41
- closeDialog: ()=>setCurrentDialog(undefined)
42
- }), [
43
- bindingScope,
44
- currentDialog,
45
- currentOutlet,
46
- currentScreen,
47
- currentUrl,
48
- navigate
49
- ]);
50
- const screen = screens[currentScreen];
51
- const dialog = currentDialog ? dialogs[currentDialog] : undefined;
22
+ const value = useSceneHistoryNavigation.useSceneHistoryNavigation(history ?? memoryHistory, routeTable, initialScreen, bindingScope);
23
+ const screen = screens[value.currentScreen];
24
+ const dialog = value.currentDialog ? dialogs[value.currentDialog] : undefined;
52
25
  return /*#__PURE__*/ jsxRuntime.jsxs(SceneNavigationContext.SceneNavigationProvider, {
53
26
  value: value,
54
27
  children: [
55
28
  /*#__PURE__*/ jsxRuntime.jsx("div", {
56
- "data-scene-outlet": currentOutlet ?? 'primary',
57
- "data-scene-url": currentUrl,
29
+ "data-scene-outlet": value.currentOutlet ?? 'primary',
30
+ "data-scene-url": value.currentUrl,
58
31
  children: screen && /*#__PURE__*/ jsxRuntime.jsx(SceneElementView.SceneElementView, {
59
32
  element: screen,
60
33
  registry: registry,
@@ -65,7 +38,9 @@ var resolveDestination = require('./resolveDestination.js');
65
38
  }),
66
39
  dialog && /*#__PURE__*/ jsxRuntime.jsx("div", {
67
40
  role: "dialog",
68
- "data-scene-dialog": currentDialog,
41
+ "aria-modal": "true",
42
+ "aria-label": value.currentDialog,
43
+ "data-scene-dialog": value.currentDialog,
69
44
  children: /*#__PURE__*/ jsxRuntime.jsx(SceneElementView.SceneElementView, {
70
45
  element: dialog,
71
46
  registry: registry,
@@ -1 +1 @@
1
- {"version":3,"file":"SceneNavigationHost.js","sources":["../../../navigation/SceneNavigationHost.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useCallback, useMemo, useState } from 'react';\nimport { BindingScope } from '@cratis/scene.engine';\nimport { DestinationKind, DestinationReference, SceneElement } from '@cratis/scene.model';\nimport { SceneElementView } from '../SceneElementView';\nimport { ComponentRegistry } from '../renderer';\nimport { SceneNavigationContextValue, SceneNavigationProvider } from './SceneNavigationContext';\nimport { resolveDestination } from './resolveDestination';\n\nexport interface SceneNavigationHostProps {\n screens: Record<string, SceneElement>;\n dialogs?: Record<string, SceneElement>;\n initialScreen: string;\n registry: ComponentRegistry;\n bindingScope?: BindingScope;\n}\n\n/**\n * Executes Scene destinations against an in-memory rendered hierarchy: screen routes replace outlets,\n * dialog destinations render dialog content, and external destinations only report their resolved action.\n */\nexport function SceneNavigationHost({ screens, dialogs = {}, initialScreen, registry, bindingScope = {} }: SceneNavigationHostProps) {\n const [currentScreen, setCurrentScreen] = useState(initialScreen);\n const [currentUrl, setCurrentUrl] = useState<string | undefined>(initialScreen);\n const [currentOutlet, setCurrentOutlet] = useState<string | undefined>();\n const [currentDialog, setCurrentDialog] = useState<string | undefined>();\n\n const navigate = useCallback((destination: DestinationReference) => {\n const resolution = resolveDestination(destination, bindingScope);\n if (resolution.kind === DestinationKind.Dialog) {\n setCurrentDialog(resolution.dialog);\n return resolution;\n }\n\n if (resolution.kind !== DestinationKind.External) {\n setCurrentScreen(destination.screen ?? destination.slice ?? currentScreen);\n setCurrentOutlet(resolution.outlet);\n }\n\n setCurrentUrl(resolution.url);\n return resolution;\n }, [bindingScope, currentScreen]);\n\n const value = useMemo<SceneNavigationContextValue>(() => ({\n currentScreen,\n currentUrl,\n currentOutlet,\n currentDialog,\n bindingScope,\n navigate,\n closeDialog: () => setCurrentDialog(undefined),\n }), [bindingScope, currentDialog, currentOutlet, currentScreen, currentUrl, navigate]);\n\n const screen = screens[currentScreen];\n const dialog = currentDialog ? dialogs[currentDialog] : undefined;\n\n return <SceneNavigationProvider value={value}>\n <div data-scene-outlet={currentOutlet ?? 'primary'} data-scene-url={currentUrl}>\n {screen && <SceneElementView element={screen} registry={registry} dataContext={bindingScope.dataContext} queryResults={bindingScope.queryResults} componentOutputs={bindingScope.componentOutputs} />}\n </div>\n {dialog && <div role='dialog' data-scene-dialog={currentDialog}>\n <SceneElementView element={dialog} registry={registry} dataContext={bindingScope.dataContext} queryResults={bindingScope.queryResults} componentOutputs={bindingScope.componentOutputs} />\n </div>}\n </SceneNavigationProvider>;\n}\n"],"names":["SceneNavigationHost","screens","dialogs","initialScreen","registry","bindingScope","currentScreen","setCurrentScreen","useState","currentUrl","setCurrentUrl","currentOutlet","setCurrentOutlet","currentDialog","setCurrentDialog","navigate","useCallback","destination","resolution","resolveDestination","kind","DestinationKind","Dialog","dialog","External","screen","slice","outlet","url","value","useMemo","closeDialog","undefined","_jsxs","SceneNavigationProvider","_jsx","div","data-scene-outlet","data-scene-url","SceneElementView","element","dataContext","queryResults","componentOutputs","role","data-scene-dialog"],"mappings":"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"}
1
+ {"version":3,"file":"SceneNavigationHost.js","sources":["../../../navigation/SceneNavigationHost.tsx"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useMemo, useState } from 'react';\nimport { BindingScope } from '@cratis/scene.engine';\nimport { DestinationReference, SceneElement } from '@cratis/scene.model';\nimport { SceneElementView } from '../SceneElementView';\nimport { ComponentRegistry } from '../renderer';\nimport { createMemorySceneHistory } from './createMemorySceneHistory';\nimport { SceneHistory } from './SceneHistory';\nimport { SceneNavigationProvider } from './SceneNavigationContext';\nimport { SceneRoute } from './SceneRoute';\nimport { sceneRoutesFrom } from './sceneRoutesFrom';\nimport { useSceneHistoryNavigation } from './useSceneHistoryNavigation';\n\nexport interface SceneNavigationHostProps {\n screens: Record<string, SceneElement>;\n dialogs?: Record<string, SceneElement>;\n initialScreen: string;\n registry: ComponentRegistry;\n bindingScope?: BindingScope;\n\n /**\n * Where entries are recorded. `createBrowserSceneHistory()` drives the address bar, so deep links,\n * refresh and back/forward work; the default is an in-memory history for embedded hosts.\n */\n history?: SceneHistory;\n\n /** The destinations the application navigates to, so their route overrides can be deep linked. */\n destinations?: DestinationReference[];\n\n /** An explicit route table, used instead of the one derived from `screens` and `destinations`. */\n routes?: SceneRoute[];\n}\n\n/**\n * Executes Scene destinations against an in-memory rendered hierarchy: screen routes replace outlets,\n * dialog destinations render dialog content over the screen that opened them, and external destinations\n * only report their resolved action. Every navigation is a history entry.\n */\nexport function SceneNavigationHost({ screens, dialogs = {}, initialScreen, registry, bindingScope = {}, history, destinations, routes }: SceneNavigationHostProps) {\n const [memoryHistory] = useState(() => createMemorySceneHistory());\n const routeTable = useMemo(() => routes ?? sceneRoutesFrom(Object.keys(screens), destinations), [destinations, routes, screens]);\n const value = useSceneHistoryNavigation(history ?? memoryHistory, routeTable, initialScreen, bindingScope);\n\n const screen = screens[value.currentScreen];\n const dialog = value.currentDialog ? dialogs[value.currentDialog] : undefined;\n\n return <SceneNavigationProvider value={value}>\n <div data-scene-outlet={value.currentOutlet ?? 'primary'} data-scene-url={value.currentUrl}>\n {screen && <SceneElementView element={screen} registry={registry} dataContext={bindingScope.dataContext} queryResults={bindingScope.queryResults} componentOutputs={bindingScope.componentOutputs} />}\n </div>\n {dialog && <div role='dialog' aria-modal='true' aria-label={value.currentDialog} data-scene-dialog={value.currentDialog}>\n <SceneElementView element={dialog} registry={registry} dataContext={bindingScope.dataContext} queryResults={bindingScope.queryResults} componentOutputs={bindingScope.componentOutputs} />\n </div>}\n </SceneNavigationProvider>;\n}\n"],"names":["SceneNavigationHost","screens","dialogs","initialScreen","registry","bindingScope","history","destinations","routes","memoryHistory","useState","createMemorySceneHistory","routeTable","useMemo","sceneRoutesFrom","Object","keys","value","useSceneHistoryNavigation","screen","currentScreen","dialog","currentDialog","undefined","_jsxs","SceneNavigationProvider","_jsx","div","data-scene-outlet","currentOutlet","data-scene-url","currentUrl","SceneElementView","element","dataContext","queryResults","componentOutputs","role","aria-modal","aria-label","data-scene-dialog"],"mappings":";;;;;;;;;;AAmCA;;;;IAKO,SAASA,mBAAAA,CAAoB,EAAEC,OAAO,EAAEC,OAAAA,GAAU,EAAE,EAAEC,aAAa,EAAEC,QAAQ,EAAEC,YAAAA,GAAe,EAAE,EAAEC,OAAO,EAAEC,YAAY,EAAEC,MAAM,EAA4B,EAAA;AAC9J,IAAA,MAAM,CAACC,aAAAA,CAAc,GAAGC,cAAAA,CAAS,IAAMC,iDAAAA,EAAAA,CAAAA;IACvC,MAAMC,UAAAA,GAAaC,cAAQ,IAAML,MAAAA,IAAUM,gCAAgBC,MAAAA,CAAOC,IAAI,CAACf,OAAAA,CAAAA,EAAUM,YAAAA,CAAAA,EAAe;AAACA,QAAAA,YAAAA;AAAcC,QAAAA,MAAAA;AAAQP,QAAAA;AAAQ,KAAA,CAAA;AAC/H,IAAA,MAAMgB,KAAAA,GAAQC,mDAAAA,CAA0BZ,OAAAA,IAAWG,aAAAA,EAAeG,YAAYT,aAAAA,EAAeE,YAAAA,CAAAA;AAE7F,IAAA,MAAMc,MAAAA,GAASlB,OAAO,CAACgB,KAAAA,CAAMG,aAAa,CAAC;IAC3C,MAAMC,MAAAA,GAASJ,MAAMK,aAAa,GAAGpB,OAAO,CAACe,KAAAA,CAAMK,aAAa,CAAC,GAAGC,SAAAA;AAEpE,IAAA,qBAAOC,eAAA,CAACC,8CAAAA,EAAAA;QAAwBR,KAAAA,EAAOA,KAAAA;;0BACnCS,cAAA,CAACC,KAAAA,EAAAA;gBAAIC,mBAAAA,EAAmBX,KAAAA,CAAMY,aAAa,IAAI,SAAA;AAAWC,gBAAAA,gBAAAA,EAAgBb,MAAMc,UAAU;AACrFZ,gBAAAA,QAAAA,EAAAA,MAAAA,kBAAUO,cAAA,CAACM,iCAAAA,EAAAA;oBAAiBC,OAAAA,EAASd,MAAAA;oBAAQf,QAAAA,EAAUA,QAAAA;AAAU8B,oBAAAA,WAAAA,EAAa7B,aAAa6B,WAAW;AAAEC,oBAAAA,YAAAA,EAAc9B,aAAa8B,YAAY;AAAEC,oBAAAA,gBAAAA,EAAkB/B,aAAa+B;;;AAEpLf,YAAAA,MAAAA,kBAAUK,cAAA,CAACC,KAAAA,EAAAA;gBAAIU,IAAAA,EAAK,QAAA;gBAASC,YAAAA,EAAW,MAAA;AAAOC,gBAAAA,YAAAA,EAAYtB,MAAMK,aAAa;AAAEkB,gBAAAA,mBAAAA,EAAmBvB,MAAMK,aAAa;AACnH,gBAAA,QAAA,gBAAAI,cAAA,CAACM,iCAAAA,EAAAA;oBAAiBC,OAAAA,EAASZ,MAAAA;oBAAQjB,QAAAA,EAAUA,QAAAA;AAAU8B,oBAAAA,WAAAA,EAAa7B,aAAa6B,WAAW;AAAEC,oBAAAA,YAAAA,EAAc9B,aAAa8B,YAAY;AAAEC,oBAAAA,gBAAAA,EAAkB/B,aAAa+B;;;;;AAGlL;;"}
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ // Copyright (c) Cratis. All rights reserved.
4
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5
+ const stateKey = 'sceneNavigation';
6
+ /**
7
+ * A history backed by the browser's `history` API under a base path such as `/app/`. Entries carry their
8
+ * navigation state, so back and forward restore the screen, outlet, parameters and dialog without
9
+ * re-resolving; an entry without state - a typed or bookmarked URL - is reported by URL alone.
10
+ */ function createBrowserSceneHistory(browser = window, basePath = '/') {
11
+ const base = basePath.endsWith('/') ? basePath : `${basePath}/`;
12
+ const href = (url)=>`${base}${url ?? ''}`;
13
+ const location = ()=>{
14
+ const current = `${browser.location.pathname}${browser.location.search}`;
15
+ if (!current.startsWith(base)) return undefined;
16
+ const relative = current.substring(base.length);
17
+ return relative.length ? relative : undefined;
18
+ };
19
+ const stored = (value)=>value?.[stateKey];
20
+ return {
21
+ location,
22
+ state: ()=>stored(browser.history.state),
23
+ push: (state)=>browser.history.pushState({
24
+ [stateKey]: state
25
+ }, '', href(state.url)),
26
+ replace: (state)=>browser.history.replaceState({
27
+ [stateKey]: state
28
+ }, '', href(state.url)),
29
+ back: ()=>browser.history.back(),
30
+ listen: (listener)=>{
31
+ const onPopState = (event)=>listener(location(), stored(event.state));
32
+ browser.addEventListener('popstate', onPopState);
33
+ return ()=>browser.removeEventListener('popstate', onPopState);
34
+ }
35
+ };
36
+ }
37
+
38
+ exports.createBrowserSceneHistory = createBrowserSceneHistory;
39
+ //# sourceMappingURL=createBrowserSceneHistory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createBrowserSceneHistory.js","sources":["../../../navigation/createBrowserSceneHistory.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { SceneHistory } from './SceneHistory';\nimport { SceneNavigationState } from './SceneNavigationState';\n\n/** The part of `window` the browser history needs, so a host can pass its own frame's window. */\nexport type SceneBrowserWindow = Pick<Window, 'history' | 'location' | 'addEventListener' | 'removeEventListener'>;\n\nconst stateKey = 'sceneNavigation';\n\n/**\n * A history backed by the browser's `history` API under a base path such as `/app/`. Entries carry their\n * navigation state, so back and forward restore the screen, outlet, parameters and dialog without\n * re-resolving; an entry without state - a typed or bookmarked URL - is reported by URL alone.\n */\nexport function createBrowserSceneHistory(browser: SceneBrowserWindow = window, basePath = '/'): SceneHistory {\n const base = basePath.endsWith('/') ? basePath : `${basePath}/`;\n const href = (url: string | undefined) => `${base}${url ?? ''}`;\n const location = () => {\n const current = `${browser.location.pathname}${browser.location.search}`;\n if (!current.startsWith(base)) return undefined;\n const relative = current.substring(base.length);\n return relative.length ? relative : undefined;\n };\n const stored = (value: unknown) => (value as Record<string, SceneNavigationState> | null)?.[stateKey];\n\n return {\n location,\n state: () => stored(browser.history.state),\n push: state => browser.history.pushState({ [stateKey]: state }, '', href(state.url)),\n replace: state => browser.history.replaceState({ [stateKey]: state }, '', href(state.url)),\n back: () => browser.history.back(),\n listen: listener => {\n const onPopState = (event: PopStateEvent) => listener(location(), stored(event.state));\n browser.addEventListener('popstate', onPopState);\n return () => browser.removeEventListener('popstate', onPopState);\n },\n };\n}\n"],"names":["stateKey","createBrowserSceneHistory","browser","window","basePath","base","endsWith","href","url","location","current","pathname","search","startsWith","undefined","relative","substring","length","stored","value","state","history","push","pushState","replace","replaceState","back","listen","listener","onPopState","event","addEventListener","removeEventListener"],"mappings":";;AAAA;AACA;AAQA,MAAMA,QAAAA,GAAW,iBAAA;AAEjB;;;;AAIC,IACM,SAASC,yBAAAA,CAA0BC,UAA8BC,MAAM,EAAEC,WAAW,GAAG,EAAA;IAC1F,MAAMC,IAAAA,GAAOD,SAASE,QAAQ,CAAC,OAAOF,QAAAA,GAAW,CAAA,EAAGA,QAAAA,CAAS,CAAC,CAAC;AAC/D,IAAA,MAAMG,OAAO,CAACC,GAAAA,GAA4B,CAAA,EAAGH,IAAAA,CAAAA,EAAOG,OAAO,EAAA,CAAA,CAAI;AAC/D,IAAA,MAAMC,QAAAA,GAAW,IAAA;QACb,MAAMC,OAAAA,GAAU,CAAA,EAAGR,OAAAA,CAAQO,QAAQ,CAACE,QAAQ,CAAA,EAAGT,OAAAA,CAAQO,QAAQ,CAACG,MAAM,CAAA,CAAE;AACxE,QAAA,IAAI,CAACF,OAAAA,CAAQG,UAAU,CAACR,OAAO,OAAOS,SAAAA;AACtC,QAAA,MAAMC,QAAAA,GAAWL,OAAAA,CAAQM,SAAS,CAACX,KAAKY,MAAM,CAAA;QAC9C,OAAOF,QAAAA,CAASE,MAAM,GAAGF,QAAAA,GAAWD,SAAAA;AACxC,IAAA,CAAA;AACA,IAAA,MAAMI,MAAAA,GAAS,CAACC,KAAAA,GAAoBA,KAAAA,GAAwDnB,QAAAA,CAAS;IAErG,OAAO;AACHS,QAAAA,QAAAA;AACAW,QAAAA,KAAAA,EAAO,IAAMF,MAAAA,CAAOhB,OAAAA,CAAQmB,OAAO,CAACD,KAAK,CAAA;AACzCE,QAAAA,IAAAA,EAAMF,CAAAA,KAAAA,GAASlB,OAAAA,CAAQmB,OAAO,CAACE,SAAS,CAAC;AAAE,gBAAA,CAACvB,WAAWoB;aAAM,EAAG,EAAA,EAAIb,IAAAA,CAAKa,KAAAA,CAAMZ,GAAG,CAAA,CAAA;AAClFgB,QAAAA,OAAAA,EAASJ,CAAAA,KAAAA,GAASlB,OAAAA,CAAQmB,OAAO,CAACI,YAAY,CAAC;AAAE,gBAAA,CAACzB,WAAWoB;aAAM,EAAG,EAAA,EAAIb,IAAAA,CAAKa,KAAAA,CAAMZ,GAAG,CAAA,CAAA;AACxFkB,QAAAA,IAAAA,EAAM,IAAMxB,OAAAA,CAAQmB,OAAO,CAACK,IAAI,EAAA;AAChCC,QAAAA,MAAAA,EAAQC,CAAAA,QAAAA,GAAAA;AACJ,YAAA,MAAMC,aAAa,CAACC,KAAAA,GAAyBF,SAASnB,QAAAA,EAAAA,EAAYS,MAAAA,CAAOY,MAAMV,KAAK,CAAA,CAAA;YACpFlB,OAAAA,CAAQ6B,gBAAgB,CAAC,UAAA,EAAYF,UAAAA,CAAAA;AACrC,YAAA,OAAO,IAAM3B,OAAAA,CAAQ8B,mBAAmB,CAAC,UAAA,EAAYH,UAAAA,CAAAA;AACzD,QAAA;AACJ,KAAA;AACJ;;"}
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ // Copyright (c) Cratis. All rights reserved.
4
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5
+ /**
6
+ * A history kept in memory with the same push, replace and back semantics as the browser's: pushing
7
+ * discards any forward entries, and going back notifies listeners. `forward` is exposed for hosts and specs
8
+ * that drive the stack themselves.
9
+ */ function createMemorySceneHistory(initialUrl) {
10
+ const entries = [
11
+ {
12
+ url: initialUrl
13
+ }
14
+ ];
15
+ let index = 0;
16
+ const listeners = new Set();
17
+ const notify = ()=>listeners.forEach((listener)=>listener(entries[index].url, entries[index].state));
18
+ return {
19
+ location: ()=>entries[index].url,
20
+ state: ()=>entries[index].state,
21
+ push: (state)=>{
22
+ entries.splice(index + 1, entries.length, {
23
+ url: state.url,
24
+ state
25
+ });
26
+ index = entries.length - 1;
27
+ },
28
+ replace: (state)=>{
29
+ entries[index] = {
30
+ url: state.url,
31
+ state
32
+ };
33
+ },
34
+ back: ()=>{
35
+ if (index === 0) return;
36
+ index--;
37
+ notify();
38
+ },
39
+ forward: ()=>{
40
+ if (index === entries.length - 1) return;
41
+ index++;
42
+ notify();
43
+ },
44
+ entries: ()=>entries.map((entry)=>entry.state).filter((state)=>state !== undefined),
45
+ listen: (listener)=>{
46
+ listeners.add(listener);
47
+ return ()=>listeners.delete(listener);
48
+ }
49
+ };
50
+ }
51
+
52
+ exports.createMemorySceneHistory = createMemorySceneHistory;
53
+ //# sourceMappingURL=createMemorySceneHistory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createMemorySceneHistory.js","sources":["../../../navigation/createMemorySceneHistory.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { SceneHistory } from './SceneHistory';\nimport { SceneNavigationState } from './SceneNavigationState';\n\n/**\n * A history kept in memory with the same push, replace and back semantics as the browser's: pushing\n * discards any forward entries, and going back notifies listeners. `forward` is exposed for hosts and specs\n * that drive the stack themselves.\n */\nexport function createMemorySceneHistory(initialUrl?: string): SceneHistory & { forward(): void; entries(): SceneNavigationState[] } {\n const entries: { url?: string; state?: SceneNavigationState }[] = [{ url: initialUrl }];\n let index = 0;\n const listeners = new Set<(url: string | undefined, state: SceneNavigationState | undefined) => void>();\n const notify = () => listeners.forEach(listener => listener(entries[index].url, entries[index].state));\n\n return {\n location: () => entries[index].url,\n state: () => entries[index].state,\n push: state => {\n entries.splice(index + 1, entries.length, { url: state.url, state });\n index = entries.length - 1;\n },\n replace: state => {\n entries[index] = { url: state.url, state };\n },\n back: () => {\n if (index === 0) return;\n index--;\n notify();\n },\n forward: () => {\n if (index === entries.length - 1) return;\n index++;\n notify();\n },\n entries: () => entries.map(entry => entry.state).filter((state): state is SceneNavigationState => state !== undefined),\n listen: listener => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n"],"names":["createMemorySceneHistory","initialUrl","entries","url","index","listeners","Set","notify","forEach","listener","state","location","push","splice","length","replace","back","forward","map","entry","filter","undefined","listen","add","delete"],"mappings":";;AAAA;AACA;AAKA;;;;IAKO,SAASA,wBAAAA,CAAyBC,UAAmB,EAAA;AACxD,IAAA,MAAMC,OAAAA,GAA4D;AAAC,QAAA;YAAEC,GAAAA,EAAKF;AAAW;AAAE,KAAA;AACvF,IAAA,IAAIG,KAAAA,GAAQ,CAAA;AACZ,IAAA,MAAMC,YAAY,IAAIC,GAAAA,EAAAA;AACtB,IAAA,MAAMC,SAAS,IAAMF,SAAAA,CAAUG,OAAO,CAACC,CAAAA,WAAYA,QAAAA,CAASP,OAAO,CAACE,KAAAA,CAAM,CAACD,GAAG,EAAED,OAAO,CAACE,KAAAA,CAAM,CAACM,KAAK,CAAA,CAAA;IAEpG,OAAO;AACHC,QAAAA,QAAAA,EAAU,IAAMT,OAAO,CAACE,KAAAA,CAAM,CAACD,GAAG;AAClCO,QAAAA,KAAAA,EAAO,IAAMR,OAAO,CAACE,KAAAA,CAAM,CAACM,KAAK;AACjCE,QAAAA,IAAAA,EAAMF,CAAAA,KAAAA,GAAAA;AACFR,YAAAA,OAAAA,CAAQW,MAAM,CAACT,KAAAA,GAAQ,CAAA,EAAGF,OAAAA,CAAQY,MAAM,EAAE;AAAEX,gBAAAA,GAAAA,EAAKO,MAAMP,GAAG;AAAEO,gBAAAA;AAAM,aAAA,CAAA;YAClEN,KAAAA,GAAQF,OAAAA,CAAQY,MAAM,GAAG,CAAA;AAC7B,QAAA,CAAA;AACAC,QAAAA,OAAAA,EAASL,CAAAA,KAAAA,GAAAA;YACLR,OAAO,CAACE,MAAM,GAAG;AAAED,gBAAAA,GAAAA,EAAKO,MAAMP,GAAG;AAAEO,gBAAAA;AAAM,aAAA;AAC7C,QAAA,CAAA;QACAM,IAAAA,EAAM,IAAA;AACF,YAAA,IAAIZ,UAAU,CAAA,EAAG;AACjBA,YAAAA,KAAAA,EAAAA;AACAG,YAAAA,MAAAA,EAAAA;AACJ,QAAA,CAAA;QACAU,OAAAA,EAAS,IAAA;AACL,YAAA,IAAIb,KAAAA,KAAUF,OAAAA,CAAQY,MAAM,GAAG,CAAA,EAAG;AAClCV,YAAAA,KAAAA,EAAAA;AACAG,YAAAA,MAAAA,EAAAA;AACJ,QAAA,CAAA;AACAL,QAAAA,OAAAA,EAAS,IAAMA,OAAAA,CAAQgB,GAAG,CAACC,CAAAA,KAAAA,GAASA,KAAAA,CAAMT,KAAK,CAAA,CAAEU,MAAM,CAAC,CAACV,QAAyCA,KAAAA,KAAUW,SAAAA,CAAAA;AAC5GC,QAAAA,MAAAA,EAAQb,CAAAA,QAAAA,GAAAA;AACJJ,YAAAA,SAAAA,CAAUkB,GAAG,CAACd,QAAAA,CAAAA;YACd,OAAO,IAAMJ,SAAAA,CAAUmB,MAAM,CAACf,QAAAA,CAAAA;AAClC,QAAA;AACJ,KAAA;AACJ;;"}
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ // Copyright (c) Cratis. All rights reserved.
4
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5
+ /**
6
+ * Matches a URL - as a deep link, a refresh or a typed address delivers it - against route templates.
7
+ * `{name}` segments capture path parameters; the query string adds the rest. Literal segments win over
8
+ * parameters, so `orders/new` beats `orders/{orderId}`, and routes are otherwise tried in the given order.
9
+ */ function matchSceneRoute(url, routes) {
10
+ const [path, query = ''] = url.split('?', 2);
11
+ const segments = path.split('/').filter((segment)=>segment.length > 0).map(decodeURIComponent);
12
+ const queryParameters = Object.fromEntries(new URLSearchParams(query));
13
+ const candidates = routes.map((route, order)=>({
14
+ route,
15
+ order,
16
+ template: route.route.split('/').filter((segment)=>segment.length > 0)
17
+ })).filter((candidate)=>candidate.template.length === segments.length).sort((left, right)=>literalCount(right.template) - literalCount(left.template) || left.order - right.order);
18
+ for (const { route, template } of candidates){
19
+ const parameters = {};
20
+ const matches = template.every((part, index)=>{
21
+ const name = /^\{([^}]+)\}$/.exec(part)?.[1];
22
+ if (name) {
23
+ parameters[name] = segments[index];
24
+ return true;
25
+ }
26
+ return part === segments[index];
27
+ });
28
+ if (matches) return {
29
+ route,
30
+ parameters: {
31
+ ...queryParameters,
32
+ ...parameters
33
+ }
34
+ };
35
+ }
36
+ return undefined;
37
+ }
38
+ function literalCount(template) {
39
+ return template.filter((part)=>!/^\{[^}]+\}$/.test(part)).length;
40
+ }
41
+
42
+ exports.matchSceneRoute = matchSceneRoute;
43
+ //# sourceMappingURL=matchSceneRoute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchSceneRoute.js","sources":["../../../navigation/matchSceneRoute.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { SceneRoute } from './SceneRoute';\n\n/** A URL matched to the route that opens it. */\nexport interface SceneRouteMatch {\n route: SceneRoute;\n parameters: Record<string, string>;\n}\n\n/**\n * Matches a URL - as a deep link, a refresh or a typed address delivers it - against route templates.\n * `{name}` segments capture path parameters; the query string adds the rest. Literal segments win over\n * parameters, so `orders/new` beats `orders/{orderId}`, and routes are otherwise tried in the given order.\n */\nexport function matchSceneRoute(url: string, routes: SceneRoute[]): SceneRouteMatch | undefined {\n const [path, query = ''] = url.split('?', 2);\n const segments = path.split('/').filter(segment => segment.length > 0).map(decodeURIComponent);\n const queryParameters = Object.fromEntries(new URLSearchParams(query));\n\n const candidates = routes\n .map((route, order) => ({ route, order, template: route.route.split('/').filter(segment => segment.length > 0) }))\n .filter(candidate => candidate.template.length === segments.length)\n .sort((left, right) => literalCount(right.template) - literalCount(left.template) || left.order - right.order);\n\n for (const { route, template } of candidates) {\n const parameters: Record<string, string> = {};\n const matches = template.every((part, index) => {\n const name = /^\\{([^}]+)\\}$/.exec(part)?.[1];\n if (name) {\n parameters[name] = segments[index];\n return true;\n }\n return part === segments[index];\n });\n if (matches) return { route, parameters: { ...queryParameters, ...parameters } };\n }\n\n return undefined;\n}\n\nfunction literalCount(template: string[]): number {\n return template.filter(part => !/^\\{[^}]+\\}$/.test(part)).length;\n}\n"],"names":["matchSceneRoute","url","routes","path","query","split","segments","filter","segment","length","map","decodeURIComponent","queryParameters","Object","fromEntries","URLSearchParams","candidates","route","order","template","candidate","sort","left","right","literalCount","parameters","matches","every","part","index","name","exec","undefined","test"],"mappings":";;AAAA;AACA;AAUA;;;;AAIC,IACM,SAASA,eAAAA,CAAgBC,GAAW,EAAEC,MAAoB,EAAA;IAC7D,MAAM,CAACC,MAAMC,KAAAA,GAAQ,EAAE,CAAC,GAAGH,GAAAA,CAAII,KAAK,CAAC,GAAA,EAAK,CAAA,CAAA;AAC1C,IAAA,MAAMC,QAAAA,GAAWH,IAAAA,CAAKE,KAAK,CAAC,KAAKE,MAAM,CAACC,CAAAA,OAAAA,GAAWA,OAAAA,CAAQC,MAAM,GAAG,CAAA,CAAA,CAAGC,GAAG,CAACC,kBAAAA,CAAAA;AAC3E,IAAA,MAAMC,eAAAA,GAAkBC,MAAAA,CAAOC,WAAW,CAAC,IAAIC,eAAAA,CAAgBX,KAAAA,CAAAA,CAAAA;AAE/D,IAAA,MAAMY,aAAad,MAAAA,CACdQ,GAAG,CAAC,CAACO,KAAAA,EAAOC,SAAW;AAAED,YAAAA,KAAAA;AAAOC,YAAAA,KAAAA;AAAOC,YAAAA,QAAAA,EAAUF,KAAAA,CAAMA,KAAK,CAACZ,KAAK,CAAC,GAAA,CAAA,CAAKE,MAAM,CAACC,CAAAA,OAAAA,GAAWA,OAAAA,CAAQC,MAAM,GAAG,CAAA;AAAG,SAAA,CAAA,CAAA,CAC9GF,MAAM,CAACa,CAAAA,SAAAA,GAAaA,SAAAA,CAAUD,QAAQ,CAACV,MAAM,KAAKH,QAAAA,CAASG,MAAM,EACjEY,IAAI,CAAC,CAACC,IAAAA,EAAMC,KAAAA,GAAUC,YAAAA,CAAaD,KAAAA,CAAMJ,QAAQ,CAAA,GAAIK,YAAAA,CAAaF,IAAAA,CAAKH,QAAQ,CAAA,IAAKG,IAAAA,CAAKJ,KAAK,GAAGK,MAAML,KAAK,CAAA;AAEjH,IAAA,KAAK,MAAM,EAAED,KAAK,EAAEE,QAAQ,EAAE,IAAIH,UAAAA,CAAY;AAC1C,QAAA,MAAMS,aAAqC,EAAC;AAC5C,QAAA,MAAMC,OAAAA,GAAUP,QAAAA,CAASQ,KAAK,CAAC,CAACC,IAAAA,EAAMC,KAAAA,GAAAA;AAClC,YAAA,MAAMC,OAAO,eAAA,CAAgBC,IAAI,CAACH,IAAAA,CAAAA,GAAQ,CAAA,CAAE;AAC5C,YAAA,IAAIE,IAAAA,EAAM;AACNL,gBAAAA,UAAU,CAACK,IAAAA,CAAK,GAAGxB,QAAQ,CAACuB,KAAAA,CAAM;gBAClC,OAAO,IAAA;AACX,YAAA;YACA,OAAOD,IAAAA,KAAStB,QAAQ,CAACuB,KAAAA,CAAM;AACnC,QAAA,CAAA,CAAA;AACA,QAAA,IAAIH,SAAS,OAAO;AAAET,YAAAA,KAAAA;YAAOQ,UAAAA,EAAY;AAAE,gBAAA,GAAGb,eAAe;AAAE,gBAAA,GAAGa;AAAW;AAAE,SAAA;AACnF,IAAA;IAEA,OAAOO,SAAAA;AACX;AAEA,SAASR,aAAaL,QAAkB,EAAA;IACpC,OAAOA,QAAAA,CAASZ,MAAM,CAACqB,CAAAA,IAAAA,GAAQ,CAAC,aAAA,CAAcK,IAAI,CAACL,IAAAA,CAAAA,CAAAA,CAAOnB,MAAM;AACpE;;"}
@@ -23,6 +23,7 @@ var identityRoute = require('./identityRoute.js');
23
23
  url,
24
24
  outlet: destination.outlet,
25
25
  dialog: destination.dialog,
26
+ parameters: Object.fromEntries(parameters),
26
27
  action: kind === scene_model.DestinationKind.Dialog ? 'openDialog' : kind === scene_model.DestinationKind.External ? 'openExternal' : 'navigate',
27
28
  diagnostics
28
29
  };
@@ -1 +1 @@
1
- {"version":3,"file":"resolveDestination.js","sources":["../../../navigation/resolveDestination.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { BindingScope, resolveBindingExpression } from '@cratis/scene.engine';\nimport { DestinationKind, DestinationReference } from '@cratis/scene.model';\nimport { DestinationResolution } from './DestinationResolution';\nimport { identityRoute } from './identityRoute';\n\n/**\n * Resolves a destination into a renderer-neutral navigation action, deep link and target outlet/dialog.\n */\nexport function resolveDestination(destination: DestinationReference, scope: BindingScope = {}): DestinationResolution {\n const diagnostics: string[] = [];\n const kind = destination.kind ?? (destination.dialog ? DestinationKind.Dialog : DestinationKind.Outlet);\n const parameters = Object.entries(destination.routeParameterBindings ?? {})\n .map(([name, binding]) => [name, String(resolveBindingExpression(binding, scope) ?? '')] as const);\n const base = destination.route ?? identityRoute(destination);\n if (!base && kind !== DestinationKind.Dialog) diagnostics.push('Destination has no routeable screen, stable identity or route.');\n\n const url = base ? appendParameters(base, parameters) : undefined;\n return {\n kind,\n url,\n outlet: destination.outlet,\n dialog: destination.dialog,\n action: kind === DestinationKind.Dialog ? 'openDialog' : kind === DestinationKind.External ? 'openExternal' : 'navigate',\n diagnostics,\n };\n}\n\nfunction appendParameters(route: string, parameters: readonly (readonly [string, string])[]): string {\n if (!parameters.length) return route;\n let replaced = route;\n const unused: string[] = [];\n for (const [name, value] of parameters) {\n const marker = `{${name}}`;\n if (replaced.includes(marker)) {\n replaced = replaced.split(marker).join(encodeURIComponent(value));\n } else {\n unused.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`);\n }\n }\n\n return unused.length ? `${replaced}?${unused.join('&')}` : replaced;\n}\n"],"names":["resolveDestination","destination","scope","diagnostics","kind","dialog","DestinationKind","Dialog","Outlet","parameters","Object","entries","routeParameterBindings","map","name","binding","String","resolveBindingExpression","base","route","identityRoute","push","url","appendParameters","undefined","outlet","action","External","length","replaced","unused","value","marker","includes","split","join","encodeURIComponent"],"mappings":";;;;;;AAAA;AACA;AAOA;;AAEC,IACM,SAASA,kBAAAA,CAAmBC,WAAiC,EAAEC,KAAAA,GAAsB,EAAE,EAAA;AAC1F,IAAA,MAAMC,cAAwB,EAAE;AAChC,IAAA,MAAMC,IAAAA,GAAOH,WAAAA,CAAYG,IAAI,KAAKH,WAAAA,CAAYI,MAAM,GAAGC,2BAAAA,CAAgBC,MAAM,GAAGD,2BAAAA,CAAgBE,MAAM,CAAD;AACrG,IAAA,MAAMC,UAAAA,GAAaC,MAAAA,CAAOC,OAAO,CAACV,YAAYW,sBAAsB,IAAI,EAAC,CAAA,CACpEC,GAAG,CAAC,CAAC,CAACC,IAAAA,EAAMC,QAAQ,GAAK;AAACD,YAAAA,IAAAA;YAAME,MAAAA,CAAOC,qCAAAA,CAAyBF,SAASb,KAAAA,CAAAA,IAAU,EAAA;AAAI,SAAA,CAAA;AAC5F,IAAA,MAAMgB,IAAAA,GAAOjB,WAAAA,CAAYkB,KAAK,IAAIC,2BAAAA,CAAcnB,WAAAA,CAAAA;IAChD,IAAI,CAACiB,QAAQd,IAAAA,KAASE,2BAAAA,CAAgBC,MAAM,EAAEJ,WAAAA,CAAYkB,IAAI,CAAC,gEAAA,CAAA;AAE/D,IAAA,MAAMC,GAAAA,GAAMJ,IAAAA,GAAOK,gBAAAA,CAAiBL,IAAAA,EAAMT,UAAAA,CAAAA,GAAce,SAAAA;IACxD,OAAO;AACHpB,QAAAA,IAAAA;AACAkB,QAAAA,GAAAA;AACAG,QAAAA,MAAAA,EAAQxB,YAAYwB,MAAM;AAC1BpB,QAAAA,MAAAA,EAAQJ,YAAYI,MAAM;QAC1BqB,MAAAA,EAAQtB,IAAAA,KAASE,4BAAgBC,MAAM,GAAG,eAAeH,IAAAA,KAASE,2BAAAA,CAAgBqB,QAAQ,GAAG,cAAA,GAAiB,UAAA;AAC9GxB,QAAAA;AACJ,KAAA;AACJ;AAEA,SAASoB,gBAAAA,CAAiBJ,KAAa,EAAEV,UAAkD,EAAA;AACvF,IAAA,IAAI,CAACA,UAAAA,CAAWmB,MAAM,EAAE,OAAOT,KAAAA;AAC/B,IAAA,IAAIU,QAAAA,GAAWV,KAAAA;AACf,IAAA,MAAMW,SAAmB,EAAE;AAC3B,IAAA,KAAK,MAAM,CAAChB,IAAAA,EAAMiB,KAAAA,CAAM,IAAItB,UAAAA,CAAY;AACpC,QAAA,MAAMuB,SAAS,CAAC,CAAC,EAAElB,IAAAA,CAAK,CAAC,CAAC;QAC1B,IAAIe,QAAAA,CAASI,QAAQ,CAACD,MAAAA,CAAAA,EAAS;AAC3BH,YAAAA,QAAAA,GAAWA,SAASK,KAAK,CAACF,MAAAA,CAAAA,CAAQG,IAAI,CAACC,kBAAAA,CAAmBL,KAAAA,CAAAA,CAAAA;QAC9D,CAAA,MAAO;YACHD,MAAAA,CAAOT,IAAI,CAAC,CAAA,EAAGe,kBAAAA,CAAmBtB,MAAM,CAAC,EAAEsB,mBAAmBL,KAAAA,CAAAA,CAAAA,CAAQ,CAAA;AAC1E,QAAA;AACJ,IAAA;IAEA,OAAOD,MAAAA,CAAOF,MAAM,GAAG,CAAA,EAAGC,QAAAA,CAAS,CAAC,EAAEC,MAAAA,CAAOK,IAAI,CAAC,GAAA,CAAA,CAAA,CAAM,GAAGN,QAAAA;AAC/D;;"}
1
+ {"version":3,"file":"resolveDestination.js","sources":["../../../navigation/resolveDestination.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { BindingScope, resolveBindingExpression } from '@cratis/scene.engine';\nimport { DestinationKind, DestinationReference } from '@cratis/scene.model';\nimport { DestinationResolution } from './DestinationResolution';\nimport { identityRoute } from './identityRoute';\n\n/**\n * Resolves a destination into a renderer-neutral navigation action, deep link and target outlet/dialog.\n */\nexport function resolveDestination(destination: DestinationReference, scope: BindingScope = {}): DestinationResolution {\n const diagnostics: string[] = [];\n const kind = destination.kind ?? (destination.dialog ? DestinationKind.Dialog : DestinationKind.Outlet);\n const parameters = Object.entries(destination.routeParameterBindings ?? {})\n .map(([name, binding]) => [name, String(resolveBindingExpression(binding, scope) ?? '')] as const);\n const base = destination.route ?? identityRoute(destination);\n if (!base && kind !== DestinationKind.Dialog) diagnostics.push('Destination has no routeable screen, stable identity or route.');\n\n const url = base ? appendParameters(base, parameters) : undefined;\n return {\n kind,\n url,\n outlet: destination.outlet,\n dialog: destination.dialog,\n parameters: Object.fromEntries(parameters),\n action: kind === DestinationKind.Dialog ? 'openDialog' : kind === DestinationKind.External ? 'openExternal' : 'navigate',\n diagnostics,\n };\n}\n\nfunction appendParameters(route: string, parameters: readonly (readonly [string, string])[]): string {\n if (!parameters.length) return route;\n let replaced = route;\n const unused: string[] = [];\n for (const [name, value] of parameters) {\n const marker = `{${name}}`;\n if (replaced.includes(marker)) {\n replaced = replaced.split(marker).join(encodeURIComponent(value));\n } else {\n unused.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`);\n }\n }\n\n return unused.length ? `${replaced}?${unused.join('&')}` : replaced;\n}\n"],"names":["resolveDestination","destination","scope","diagnostics","kind","dialog","DestinationKind","Dialog","Outlet","parameters","Object","entries","routeParameterBindings","map","name","binding","String","resolveBindingExpression","base","route","identityRoute","push","url","appendParameters","undefined","outlet","fromEntries","action","External","length","replaced","unused","value","marker","includes","split","join","encodeURIComponent"],"mappings":";;;;;;AAAA;AACA;AAOA;;AAEC,IACM,SAASA,kBAAAA,CAAmBC,WAAiC,EAAEC,KAAAA,GAAsB,EAAE,EAAA;AAC1F,IAAA,MAAMC,cAAwB,EAAE;AAChC,IAAA,MAAMC,IAAAA,GAAOH,WAAAA,CAAYG,IAAI,KAAKH,WAAAA,CAAYI,MAAM,GAAGC,2BAAAA,CAAgBC,MAAM,GAAGD,2BAAAA,CAAgBE,MAAM,CAAD;AACrG,IAAA,MAAMC,UAAAA,GAAaC,MAAAA,CAAOC,OAAO,CAACV,YAAYW,sBAAsB,IAAI,EAAC,CAAA,CACpEC,GAAG,CAAC,CAAC,CAACC,IAAAA,EAAMC,QAAQ,GAAK;AAACD,YAAAA,IAAAA;YAAME,MAAAA,CAAOC,qCAAAA,CAAyBF,SAASb,KAAAA,CAAAA,IAAU,EAAA;AAAI,SAAA,CAAA;AAC5F,IAAA,MAAMgB,IAAAA,GAAOjB,WAAAA,CAAYkB,KAAK,IAAIC,2BAAAA,CAAcnB,WAAAA,CAAAA;IAChD,IAAI,CAACiB,QAAQd,IAAAA,KAASE,2BAAAA,CAAgBC,MAAM,EAAEJ,WAAAA,CAAYkB,IAAI,CAAC,gEAAA,CAAA;AAE/D,IAAA,MAAMC,GAAAA,GAAMJ,IAAAA,GAAOK,gBAAAA,CAAiBL,IAAAA,EAAMT,UAAAA,CAAAA,GAAce,SAAAA;IACxD,OAAO;AACHpB,QAAAA,IAAAA;AACAkB,QAAAA,GAAAA;AACAG,QAAAA,MAAAA,EAAQxB,YAAYwB,MAAM;AAC1BpB,QAAAA,MAAAA,EAAQJ,YAAYI,MAAM;QAC1BI,UAAAA,EAAYC,MAAAA,CAAOgB,WAAW,CAACjB,UAAAA,CAAAA;QAC/BkB,MAAAA,EAAQvB,IAAAA,KAASE,4BAAgBC,MAAM,GAAG,eAAeH,IAAAA,KAASE,2BAAAA,CAAgBsB,QAAQ,GAAG,cAAA,GAAiB,UAAA;AAC9GzB,QAAAA;AACJ,KAAA;AACJ;AAEA,SAASoB,gBAAAA,CAAiBJ,KAAa,EAAEV,UAAkD,EAAA;AACvF,IAAA,IAAI,CAACA,UAAAA,CAAWoB,MAAM,EAAE,OAAOV,KAAAA;AAC/B,IAAA,IAAIW,QAAAA,GAAWX,KAAAA;AACf,IAAA,MAAMY,SAAmB,EAAE;AAC3B,IAAA,KAAK,MAAM,CAACjB,IAAAA,EAAMkB,KAAAA,CAAM,IAAIvB,UAAAA,CAAY;AACpC,QAAA,MAAMwB,SAAS,CAAC,CAAC,EAAEnB,IAAAA,CAAK,CAAC,CAAC;QAC1B,IAAIgB,QAAAA,CAASI,QAAQ,CAACD,MAAAA,CAAAA,EAAS;AAC3BH,YAAAA,QAAAA,GAAWA,SAASK,KAAK,CAACF,MAAAA,CAAAA,CAAQG,IAAI,CAACC,kBAAAA,CAAmBL,KAAAA,CAAAA,CAAAA;QAC9D,CAAA,MAAO;YACHD,MAAAA,CAAOV,IAAI,CAAC,CAAA,EAAGgB,kBAAAA,CAAmBvB,MAAM,CAAC,EAAEuB,mBAAmBL,KAAAA,CAAAA,CAAAA,CAAQ,CAAA;AAC1E,QAAA;AACJ,IAAA;IAEA,OAAOD,MAAAA,CAAOF,MAAM,GAAG,CAAA,EAAGC,QAAAA,CAAS,CAAC,EAAEC,MAAAA,CAAOK,IAAI,CAAC,GAAA,CAAA,CAAA,CAAM,GAAGN,QAAAA;AAC/D;;"}
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ var scene_model = require('@cratis/scene.model');
4
+ var identityRoute = require('./identityRoute.js');
5
+
6
+ // Copyright (c) Cratis. All rights reserved.
7
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
8
+ /**
9
+ * The routes a host can deep link to: every screen at its own name, and every outlet destination at its
10
+ * authored route override or identity route. A destination listed earlier wins a route it shares with a
11
+ * later one, and authored overrides come before the screen-name defaults.
12
+ */ function sceneRoutesFrom(screens, destinations = []) {
13
+ const routes = [];
14
+ for (const destination of destinations){
15
+ const kind = destination.kind ?? (destination.dialog ? scene_model.DestinationKind.Dialog : scene_model.DestinationKind.Outlet);
16
+ const screen = destination.screen ?? destination.slice;
17
+ const route = destination.route ?? identityRoute.identityRoute(destination);
18
+ if (kind !== scene_model.DestinationKind.Outlet || !screen || !route) continue;
19
+ routes.push({
20
+ route,
21
+ screen,
22
+ ...destination.outlet ? {
23
+ outlet: destination.outlet
24
+ } : {}
25
+ });
26
+ }
27
+ return [
28
+ ...routes,
29
+ ...screens.map((screen)=>({
30
+ route: screen,
31
+ screen
32
+ }))
33
+ ];
34
+ }
35
+
36
+ exports.sceneRoutesFrom = sceneRoutesFrom;
37
+ //# sourceMappingURL=sceneRoutesFrom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sceneRoutesFrom.js","sources":["../../../navigation/sceneRoutesFrom.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { DestinationKind, DestinationReference } from '@cratis/scene.model';\nimport { identityRoute } from './identityRoute';\nimport { SceneRoute } from './SceneRoute';\n\n/**\n * The routes a host can deep link to: every screen at its own name, and every outlet destination at its\n * authored route override or identity route. A destination listed earlier wins a route it shares with a\n * later one, and authored overrides come before the screen-name defaults.\n */\nexport function sceneRoutesFrom(screens: string[], destinations: DestinationReference[] = []): SceneRoute[] {\n const routes: SceneRoute[] = [];\n for (const destination of destinations) {\n const kind = destination.kind ?? (destination.dialog ? DestinationKind.Dialog : DestinationKind.Outlet);\n const screen = destination.screen ?? destination.slice;\n const route = destination.route ?? identityRoute(destination);\n if (kind !== DestinationKind.Outlet || !screen || !route) continue;\n routes.push({ route, screen, ...(destination.outlet ? { outlet: destination.outlet } : {}) });\n }\n\n return [...routes, ...screens.map(screen => ({ route: screen, screen }))];\n}\n"],"names":["sceneRoutesFrom","screens","destinations","routes","destination","kind","dialog","DestinationKind","Dialog","Outlet","screen","slice","route","identityRoute","push","outlet","map"],"mappings":";;;;;AAAA;AACA;AAMA;;;;AAIC,IACM,SAASA,eAAAA,CAAgBC,OAAiB,EAAEC,eAAuC,EAAE,EAAA;AACxF,IAAA,MAAMC,SAAuB,EAAE;IAC/B,KAAK,MAAMC,eAAeF,YAAAA,CAAc;AACpC,QAAA,MAAMG,IAAAA,GAAOD,WAAAA,CAAYC,IAAI,KAAKD,WAAAA,CAAYE,MAAM,GAAGC,2BAAAA,CAAgBC,MAAM,GAAGD,2BAAAA,CAAgBE,MAAM,CAAD;AACrG,QAAA,MAAMC,MAAAA,GAASN,WAAAA,CAAYM,MAAM,IAAIN,YAAYO,KAAK;AACtD,QAAA,MAAMC,KAAAA,GAAQR,WAAAA,CAAYQ,KAAK,IAAIC,2BAAAA,CAAcT,WAAAA,CAAAA;AACjD,QAAA,IAAIC,SAASE,2BAAAA,CAAgBE,MAAM,IAAI,CAACC,MAAAA,IAAU,CAACE,KAAAA,EAAO;AAC1DT,QAAAA,MAAAA,CAAOW,IAAI,CAAC;AAAEF,YAAAA,KAAAA;AAAOF,YAAAA,MAAAA;YAAQ,GAAIN,WAAAA,CAAYW,MAAM,GAAG;AAAEA,gBAAAA,MAAAA,EAAQX,YAAYW;AAAO,aAAA,GAAI;AAAI,SAAA,CAAA;AAC/F,IAAA;IAEA,OAAO;AAAIZ,QAAAA,GAAAA,MAAAA;AAAWF,QAAAA,GAAAA,OAAAA,CAAQe,GAAG,CAACN,CAAAA,MAAAA,IAAW;gBAAEE,KAAAA,EAAOF,MAAAA;AAAQA,gBAAAA;aAAO,CAAA;AAAI,KAAA;AAC7E;;"}
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var scene_model = require('@cratis/scene.model');
5
+ var matchSceneRoute = require('./matchSceneRoute.js');
6
+ var resolveDestination = require('./resolveDestination.js');
7
+
8
+ // Copyright (c) Cratis. All rights reserved.
9
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
10
+ /**
11
+ * Keeps a navigation host's state in a {@link SceneHistory}: every navigation and every dialog is an entry,
12
+ * so back and forward step through them, a refresh restores the stored entry, and a deep link is matched
13
+ * against the route table. Closing a dialog goes back to the entry that opened it.
14
+ */ function useSceneHistoryNavigation(history, routes, initialScreen, bindingScope) {
15
+ const fromLocation = react.useCallback((url)=>{
16
+ const match = url ? matchSceneRoute.matchSceneRoute(url, routes) : undefined;
17
+ return match ? {
18
+ screen: match.route.screen,
19
+ url,
20
+ outlet: match.route.outlet,
21
+ parameters: match.parameters
22
+ } : undefined;
23
+ }, [
24
+ routes
25
+ ]);
26
+ const fallback = react.useMemo(()=>({
27
+ screen: initialScreen,
28
+ url: initialScreen,
29
+ parameters: {}
30
+ }), [
31
+ initialScreen
32
+ ]);
33
+ const [unresolvedUrl] = react.useState(()=>{
34
+ const url = history.location();
35
+ return !history.state() && url && !fromLocation(url) ? url : undefined;
36
+ });
37
+ const [state, setState] = react.useState(()=>history.state() ?? fromLocation(history.location()) ?? fallback);
38
+ const [dialogResult, setDialogResult] = react.useState();
39
+ // Back and forward restore the stored entry, or match a URL the host never wrote against the current routes.
40
+ const restore = react.useRef((url, stored)=>stored ?? fromLocation(url) ?? fallback);
41
+ restore.current = (url, stored)=>stored ?? fromLocation(url) ?? fallback;
42
+ const initialState = react.useRef(state);
43
+ // The current entry is written once, when a history is attached; later entries are written by navigate.
44
+ react.useEffect(()=>{
45
+ history.replace(initialState.current);
46
+ return history.listen((url, stored)=>setState(restore.current(url, stored)));
47
+ }, [
48
+ history
49
+ ]);
50
+ const go = react.useCallback((next)=>{
51
+ history.push(next);
52
+ setState(next);
53
+ }, [
54
+ history
55
+ ]);
56
+ const navigate = react.useCallback((destination)=>{
57
+ const resolution = resolveDestination.resolveDestination(destination, bindingScope);
58
+ if (resolution.kind === scene_model.DestinationKind.Dialog) {
59
+ if (resolution.dialog) go({
60
+ ...state,
61
+ dialog: resolution.dialog
62
+ });
63
+ } else if (resolution.kind !== scene_model.DestinationKind.External) {
64
+ go({
65
+ screen: destination.screen ?? destination.slice ?? state.screen,
66
+ url: resolution.url,
67
+ outlet: resolution.outlet,
68
+ parameters: resolution.parameters
69
+ });
70
+ }
71
+ return resolution;
72
+ }, [
73
+ bindingScope,
74
+ go,
75
+ state
76
+ ]);
77
+ const closeDialog = react.useCallback((result)=>{
78
+ if (!state.dialog) return;
79
+ setDialogResult({
80
+ dialog: state.dialog,
81
+ result
82
+ });
83
+ history.back();
84
+ }, [
85
+ history,
86
+ state.dialog
87
+ ]);
88
+ return react.useMemo(()=>({
89
+ currentScreen: state.screen,
90
+ currentUrl: state.url,
91
+ currentOutlet: state.outlet,
92
+ currentDialog: state.dialog,
93
+ currentParameters: state.parameters,
94
+ dialogResult,
95
+ unresolvedUrl,
96
+ bindingScope,
97
+ navigate,
98
+ closeDialog,
99
+ back: ()=>history.back()
100
+ }), [
101
+ bindingScope,
102
+ closeDialog,
103
+ dialogResult,
104
+ history,
105
+ navigate,
106
+ state,
107
+ unresolvedUrl
108
+ ]);
109
+ }
110
+
111
+ exports.useSceneHistoryNavigation = useSceneHistoryNavigation;
112
+ //# sourceMappingURL=useSceneHistoryNavigation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSceneHistoryNavigation.js","sources":["../../../navigation/useSceneHistoryNavigation.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { BindingScope } from '@cratis/scene.engine';\nimport { DestinationKind, DestinationReference } from '@cratis/scene.model';\nimport { DialogResult } from './DialogResult';\nimport { matchSceneRoute } from './matchSceneRoute';\nimport { resolveDestination } from './resolveDestination';\nimport { SceneHistory } from './SceneHistory';\nimport { SceneNavigationContextValue } from './SceneNavigationContext';\nimport { SceneNavigationState } from './SceneNavigationState';\nimport { SceneRoute } from './SceneRoute';\n\n/**\n * Keeps a navigation host's state in a {@link SceneHistory}: every navigation and every dialog is an entry,\n * so back and forward step through them, a refresh restores the stored entry, and a deep link is matched\n * against the route table. Closing a dialog goes back to the entry that opened it.\n */\nexport function useSceneHistoryNavigation(history: SceneHistory, routes: SceneRoute[], initialScreen: string, bindingScope: BindingScope): SceneNavigationContextValue {\n const fromLocation = useCallback((url: string | undefined): SceneNavigationState | undefined => {\n const match = url ? matchSceneRoute(url, routes) : undefined;\n return match ? { screen: match.route.screen, url, outlet: match.route.outlet, parameters: match.parameters } : undefined;\n }, [routes]);\n const fallback = useMemo<SceneNavigationState>(() => ({ screen: initialScreen, url: initialScreen, parameters: {} }), [initialScreen]);\n\n const [unresolvedUrl] = useState(() => {\n const url = history.location();\n return !history.state() && url && !fromLocation(url) ? url : undefined;\n });\n const [state, setState] = useState<SceneNavigationState>(() => history.state() ?? fromLocation(history.location()) ?? fallback);\n const [dialogResult, setDialogResult] = useState<DialogResult>();\n\n // Back and forward restore the stored entry, or match a URL the host never wrote against the current routes.\n const restore = useRef((url: string | undefined, stored: SceneNavigationState | undefined) => stored ?? fromLocation(url) ?? fallback);\n restore.current = (url, stored) => stored ?? fromLocation(url) ?? fallback;\n const initialState = useRef(state);\n\n // The current entry is written once, when a history is attached; later entries are written by navigate.\n useEffect(() => {\n history.replace(initialState.current);\n return history.listen((url, stored) => setState(restore.current(url, stored)));\n }, [history]);\n\n const go = useCallback((next: SceneNavigationState) => {\n history.push(next);\n setState(next);\n }, [history]);\n\n const navigate = useCallback((destination: DestinationReference) => {\n const resolution = resolveDestination(destination, bindingScope);\n if (resolution.kind === DestinationKind.Dialog) {\n if (resolution.dialog) go({ ...state, dialog: resolution.dialog });\n } else if (resolution.kind !== DestinationKind.External) {\n go({ screen: destination.screen ?? destination.slice ?? state.screen, url: resolution.url, outlet: resolution.outlet, parameters: resolution.parameters });\n }\n\n return resolution;\n }, [bindingScope, go, state]);\n\n const closeDialog = useCallback((result?: unknown) => {\n if (!state.dialog) return;\n setDialogResult({ dialog: state.dialog, result });\n history.back();\n }, [history, state.dialog]);\n\n return useMemo(() => ({\n currentScreen: state.screen,\n currentUrl: state.url,\n currentOutlet: state.outlet,\n currentDialog: state.dialog,\n currentParameters: state.parameters,\n dialogResult,\n unresolvedUrl,\n bindingScope,\n navigate,\n closeDialog,\n back: () => history.back(),\n }), [bindingScope, closeDialog, dialogResult, history, navigate, state, unresolvedUrl]);\n}\n"],"names":["useSceneHistoryNavigation","history","routes","initialScreen","bindingScope","fromLocation","useCallback","url","match","matchSceneRoute","undefined","screen","route","outlet","parameters","fallback","useMemo","unresolvedUrl","useState","location","state","setState","dialogResult","setDialogResult","restore","useRef","stored","current","initialState","useEffect","replace","listen","go","next","push","navigate","destination","resolution","resolveDestination","kind","DestinationKind","Dialog","dialog","External","slice","closeDialog","result","back","currentScreen","currentUrl","currentOutlet","currentDialog","currentParameters"],"mappings":";;;;;;;AAAA;AACA;AAaA;;;;IAKO,SAASA,yBAAAA,CAA0BC,OAAqB,EAAEC,MAAoB,EAAEC,aAAqB,EAAEC,YAA0B,EAAA;IACpI,MAAMC,YAAAA,GAAeC,kBAAY,CAACC,GAAAA,GAAAA;AAC9B,QAAA,MAAMC,KAAAA,GAAQD,GAAAA,GAAME,+BAAAA,CAAgBF,GAAAA,EAAKL,MAAAA,CAAAA,GAAUQ,SAAAA;AACnD,QAAA,OAAOF,KAAAA,GAAQ;YAAEG,MAAAA,EAAQH,KAAAA,CAAMI,KAAK,CAACD,MAAM;AAAEJ,YAAAA,GAAAA;YAAKM,MAAAA,EAAQL,KAAAA,CAAMI,KAAK,CAACC,MAAM;AAAEC,YAAAA,UAAAA,EAAYN,MAAMM;SAAW,GAAIJ,SAAAA;IACnH,CAAA,EAAG;AAACR,QAAAA;AAAO,KAAA,CAAA;IACX,MAAMa,QAAAA,GAAWC,aAAAA,CAA8B,KAAO;YAAEL,MAAAA,EAAQR,aAAAA;YAAeI,GAAAA,EAAKJ,aAAAA;AAAeW,YAAAA,UAAAA,EAAY;AAAG,SAAA,CAAA,EAAI;AAACX,QAAAA;AAAc,KAAA,CAAA;IAErI,MAAM,CAACc,aAAAA,CAAc,GAAGC,cAAAA,CAAS,IAAA;QAC7B,MAAMX,GAAAA,GAAMN,QAAQkB,QAAQ,EAAA;QAC5B,OAAO,CAAClB,QAAQmB,KAAK,EAAA,IAAMb,OAAO,CAACF,YAAAA,CAAaE,OAAOA,GAAAA,GAAMG,SAAAA;AACjE,IAAA,CAAA,CAAA;AACA,IAAA,MAAM,CAACU,KAAAA,EAAOC,QAAAA,CAAS,GAAGH,cAAAA,CAA+B,IAAMjB,OAAAA,CAAQmB,KAAK,EAAA,IAAMf,YAAAA,CAAaJ,OAAAA,CAAQkB,QAAQ,EAAA,CAAA,IAAOJ,QAAAA,CAAAA;IACtH,MAAM,CAACO,YAAAA,EAAcC,eAAAA,CAAgB,GAAGL,cAAAA,EAAAA;;AAGxC,IAAA,MAAMM,UAAUC,YAAAA,CAAO,CAAClB,KAAyBmB,MAAAA,GAA6CA,MAAAA,IAAUrB,aAAaE,GAAAA,CAAAA,IAAQQ,QAAAA,CAAAA;AAC7HS,IAAAA,OAAAA,CAAQG,OAAO,GAAG,CAACpB,KAAKmB,MAAAA,GAAWA,MAAAA,IAAUrB,aAAaE,GAAAA,CAAAA,IAAQQ,QAAAA;AAClE,IAAA,MAAMa,eAAeH,YAAAA,CAAOL,KAAAA,CAAAA;;IAG5BS,eAAAA,CAAU,IAAA;QACN5B,OAAAA,CAAQ6B,OAAO,CAACF,YAAAA,CAAaD,OAAO,CAAA;QACpC,OAAO1B,OAAAA,CAAQ8B,MAAM,CAAC,CAACxB,GAAAA,EAAKmB,SAAWL,QAAAA,CAASG,OAAAA,CAAQG,OAAO,CAACpB,GAAAA,EAAKmB,MAAAA,CAAAA,CAAAA,CAAAA;IACzE,CAAA,EAAG;AAACzB,QAAAA;AAAQ,KAAA,CAAA;IAEZ,MAAM+B,EAAAA,GAAK1B,kBAAY,CAAC2B,IAAAA,GAAAA;AACpBhC,QAAAA,OAAAA,CAAQiC,IAAI,CAACD,IAAAA,CAAAA;QACbZ,QAAAA,CAASY,IAAAA,CAAAA;IACb,CAAA,EAAG;AAAChC,QAAAA;AAAQ,KAAA,CAAA;IAEZ,MAAMkC,QAAAA,GAAW7B,kBAAY,CAAC8B,WAAAA,GAAAA;QAC1B,MAAMC,UAAAA,GAAaC,sCAAmBF,WAAAA,EAAahC,YAAAA,CAAAA;AACnD,QAAA,IAAIiC,UAAAA,CAAWE,IAAI,KAAKC,2BAAAA,CAAgBC,MAAM,EAAE;YAC5C,IAAIJ,UAAAA,CAAWK,MAAM,EAAEV,EAAAA,CAAG;AAAE,gBAAA,GAAGZ,KAAK;AAAEsB,gBAAAA,MAAAA,EAAQL,WAAWK;AAAO,aAAA,CAAA;AACpE,QAAA,CAAA,MAAO,IAAIL,UAAAA,CAAWE,IAAI,KAAKC,2BAAAA,CAAgBG,QAAQ,EAAE;YACrDX,EAAAA,CAAG;AAAErB,gBAAAA,MAAAA,EAAQyB,YAAYzB,MAAM,IAAIyB,YAAYQ,KAAK,IAAIxB,MAAMT,MAAM;AAAEJ,gBAAAA,GAAAA,EAAK8B,WAAW9B,GAAG;AAAEM,gBAAAA,MAAAA,EAAQwB,WAAWxB,MAAM;AAAEC,gBAAAA,UAAAA,EAAYuB,WAAWvB;AAAW,aAAA,CAAA;AAC5J,QAAA;QAEA,OAAOuB,UAAAA;IACX,CAAA,EAAG;AAACjC,QAAAA,YAAAA;AAAc4B,QAAAA,EAAAA;AAAIZ,QAAAA;AAAM,KAAA,CAAA;IAE5B,MAAMyB,WAAAA,GAAcvC,kBAAY,CAACwC,MAAAA,GAAAA;QAC7B,IAAI,CAAC1B,KAAAA,CAAMsB,MAAM,EAAE;QACnBnB,eAAAA,CAAgB;AAAEmB,YAAAA,MAAAA,EAAQtB,MAAMsB,MAAM;AAAEI,YAAAA;AAAO,SAAA,CAAA;AAC/C7C,QAAAA,OAAAA,CAAQ8C,IAAI,EAAA;IAChB,CAAA,EAAG;AAAC9C,QAAAA,OAAAA;AAASmB,QAAAA,KAAAA,CAAMsB;AAAO,KAAA,CAAA;IAE1B,OAAO1B,aAAAA,CAAQ,KAAO;AAClBgC,YAAAA,aAAAA,EAAe5B,MAAMT,MAAM;AAC3BsC,YAAAA,UAAAA,EAAY7B,MAAMb,GAAG;AACrB2C,YAAAA,aAAAA,EAAe9B,MAAMP,MAAM;AAC3BsC,YAAAA,aAAAA,EAAe/B,MAAMsB,MAAM;AAC3BU,YAAAA,iBAAAA,EAAmBhC,MAAMN,UAAU;AACnCQ,YAAAA,YAAAA;AACAL,YAAAA,aAAAA;AACAb,YAAAA,YAAAA;AACA+B,YAAAA,QAAAA;AACAU,YAAAA,WAAAA;YACAE,IAAAA,EAAM,IAAM9C,QAAQ8C,IAAI;AAC5B,SAAA,CAAA,EAAI;AAAC3C,QAAAA,YAAAA;AAAcyC,QAAAA,WAAAA;AAAavB,QAAAA,YAAAA;AAAcrB,QAAAA,OAAAA;AAASkC,QAAAA,QAAAA;AAAUf,QAAAA,KAAAA;AAAOH,QAAAA;AAAc,KAAA,CAAA;AAC1F;;"}
@@ -1,7 +1,7 @@
1
1
  import 'react/jsx-runtime';
2
2
  import 'react';
3
- import '@cratis/scene.model';
4
3
  import '@cratis/scene.engine';
4
+ import '@cratis/scene.model';
5
5
  import '../interactions/useInteractions.js';
6
6
  import '../interactions/NotificationHost.js';
7
7
  import { componentRegistryKey } from '../packages/ScenePackageBundle.js';
package/dist/esm/index.js CHANGED
@@ -17,10 +17,15 @@ export { NavBar } from './NavBar/NavBar.js';
17
17
  export { extractNavigationItem } from './NavBar/extractNavigationItem.js';
18
18
  export { SceneNavigationProvider, useSceneNavigation } from './navigation/SceneNavigationContext.js';
19
19
  export { SceneNavigationHost } from './navigation/SceneNavigationHost.js';
20
- export { resolveDestination } from './navigation/resolveDestination.js';
20
+ export { createBrowserSceneHistory } from './navigation/createBrowserSceneHistory.js';
21
+ export { createMemorySceneHistory } from './navigation/createMemorySceneHistory.js';
21
22
  export { diagnoseRenderedNavigation } from './navigation/diagnoseRenderedNavigation.js';
22
23
  export { identityRoute } from './navigation/identityRoute.js';
24
+ export { matchSceneRoute } from './navigation/matchSceneRoute.js';
23
25
  export { navigationRouteKey } from './navigation/navigationRouteKey.js';
26
+ export { resolveDestination } from './navigation/resolveDestination.js';
27
+ export { sceneRoutesFrom } from './navigation/sceneRoutesFrom.js';
28
+ export { useSceneHistoryNavigation } from './navigation/useSceneHistoryNavigation.js';
24
29
  export { IconAdapterRegistry, createIconAdapterRegistry } from './icons/IconAdapterRegistry.js';
25
30
  export { IconAdapterProvider, useIconAdapters } from './icons/IconAdapterContext.js';
26
31
  export { useIconGlyph } from './icons/useIconGlyph.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}