@cratis/scene.react 4.16.0 → 4.18.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 (86) hide show
  1. package/core/coreComponents.ts +2 -0
  2. package/core/coreDescriptors.ts +6 -0
  3. package/core/corePackage.ts +1 -0
  4. package/dist/cjs/core/coreComponents.js +3 -1
  5. package/dist/cjs/core/coreComponents.js.map +1 -1
  6. package/dist/cjs/core/coreDescriptors.js +16 -0
  7. package/dist/cjs/core/coreDescriptors.js.map +1 -1
  8. package/dist/cjs/core/corePackage.js +2 -1
  9. package/dist/cjs/core/corePackage.js.map +1 -1
  10. package/dist/cjs/index.js +11 -0
  11. package/dist/cjs/index.js.map +1 -1
  12. package/dist/cjs/navigation/CoreOutlet.js +32 -0
  13. package/dist/cjs/navigation/CoreOutlet.js.map +1 -0
  14. package/dist/cjs/navigation/SceneNavigationContext.js +2 -0
  15. package/dist/cjs/navigation/SceneNavigationContext.js.map +1 -1
  16. package/dist/cjs/navigation/SceneNavigationHost.js +46 -29
  17. package/dist/cjs/navigation/SceneNavigationHost.js.map +1 -1
  18. package/dist/cjs/navigation/SceneOutletContext.js +14 -0
  19. package/dist/cjs/navigation/SceneOutletContext.js.map +1 -0
  20. package/dist/cjs/navigation/collectOutletOwners.js +36 -0
  21. package/dist/cjs/navigation/collectOutletOwners.js.map +1 -0
  22. package/dist/cjs/navigation/placeInOutlet.js +65 -0
  23. package/dist/cjs/navigation/placeInOutlet.js.map +1 -0
  24. package/dist/cjs/navigation/useSceneHistoryNavigation.js +22 -6
  25. package/dist/cjs/navigation/useSceneHistoryNavigation.js.map +1 -1
  26. package/dist/cjs/packages/ScenePackageBundle.js +23 -13
  27. package/dist/cjs/packages/ScenePackageBundle.js.map +1 -1
  28. package/dist/esm/core/coreComponents.d.ts.map +1 -1
  29. package/dist/esm/core/coreComponents.js +3 -1
  30. package/dist/esm/core/coreComponents.js.map +1 -1
  31. package/dist/esm/core/coreDescriptors.d.ts.map +1 -1
  32. package/dist/esm/core/coreDescriptors.js +16 -0
  33. package/dist/esm/core/coreDescriptors.js.map +1 -1
  34. package/dist/esm/core/corePackage.d.ts.map +1 -1
  35. package/dist/esm/core/corePackage.js +2 -1
  36. package/dist/esm/core/corePackage.js.map +1 -1
  37. package/dist/esm/index.js +5 -1
  38. package/dist/esm/index.js.map +1 -1
  39. package/dist/esm/navigation/CoreOutlet.d.ts +3 -0
  40. package/dist/esm/navigation/CoreOutlet.d.ts.map +1 -0
  41. package/dist/esm/navigation/CoreOutlet.js +30 -0
  42. package/dist/esm/navigation/CoreOutlet.js.map +1 -0
  43. package/dist/esm/navigation/SceneNavigationContext.d.ts +3 -0
  44. package/dist/esm/navigation/SceneNavigationContext.d.ts.map +1 -1
  45. package/dist/esm/navigation/SceneNavigationContext.js +3 -2
  46. package/dist/esm/navigation/SceneNavigationContext.js.map +1 -1
  47. package/dist/esm/navigation/SceneNavigationHost.d.ts.map +1 -1
  48. package/dist/esm/navigation/SceneNavigationHost.js +47 -30
  49. package/dist/esm/navigation/SceneNavigationHost.js.map +1 -1
  50. package/dist/esm/navigation/SceneNavigationState.d.ts +2 -0
  51. package/dist/esm/navigation/SceneNavigationState.d.ts.map +1 -1
  52. package/dist/esm/navigation/SceneOutletContext.d.ts +9 -0
  53. package/dist/esm/navigation/SceneOutletContext.d.ts.map +1 -0
  54. package/dist/esm/navigation/SceneOutletContext.js +11 -0
  55. package/dist/esm/navigation/SceneOutletContext.js.map +1 -0
  56. package/dist/esm/navigation/collectOutletOwners.d.ts +4 -0
  57. package/dist/esm/navigation/collectOutletOwners.d.ts.map +1 -0
  58. package/dist/esm/navigation/collectOutletOwners.js +33 -0
  59. package/dist/esm/navigation/collectOutletOwners.js.map +1 -0
  60. package/dist/esm/navigation/index.d.ts +4 -0
  61. package/dist/esm/navigation/index.d.ts.map +1 -1
  62. package/dist/esm/navigation/index.js +4 -0
  63. package/dist/esm/navigation/index.js.map +1 -1
  64. package/dist/esm/navigation/placeInOutlet.d.ts +4 -0
  65. package/dist/esm/navigation/placeInOutlet.d.ts.map +1 -0
  66. package/dist/esm/navigation/placeInOutlet.js +63 -0
  67. package/dist/esm/navigation/placeInOutlet.js.map +1 -0
  68. package/dist/esm/navigation/useSceneHistoryNavigation.d.ts +2 -1
  69. package/dist/esm/navigation/useSceneHistoryNavigation.d.ts.map +1 -1
  70. package/dist/esm/navigation/useSceneHistoryNavigation.js +22 -6
  71. package/dist/esm/navigation/useSceneHistoryNavigation.js.map +1 -1
  72. package/dist/esm/packages/ScenePackageBundle.d.ts.map +1 -1
  73. package/dist/esm/packages/ScenePackageBundle.js +23 -13
  74. package/dist/esm/packages/ScenePackageBundle.js.map +1 -1
  75. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  76. package/navigation/CoreOutlet.tsx +26 -0
  77. package/navigation/SceneNavigationContext.tsx +10 -0
  78. package/navigation/SceneNavigationHost.tsx +12 -6
  79. package/navigation/SceneNavigationState.ts +13 -0
  80. package/navigation/SceneOutletContext.tsx +19 -0
  81. package/navigation/collectOutletOwners.ts +30 -0
  82. package/navigation/index.ts +4 -0
  83. package/navigation/placeInOutlet.ts +68 -0
  84. package/navigation/useSceneHistoryNavigation.ts +20 -5
  85. package/package.json +5 -5
  86. package/packages/ScenePackageBundle.ts +30 -17
@@ -1,13 +1,17 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { useState, useMemo } from 'react';
3
3
  import { SceneElementView } from '../SceneElementView.js';
4
4
  import { createMemorySceneHistory } from './createMemorySceneHistory.js';
5
5
  import { SceneNavigationProvider } from './SceneNavigationContext.js';
6
6
  import { sceneRoutesFrom } from './sceneRoutesFrom.js';
7
+ import { collectOutletOwners } from './collectOutletOwners.js';
8
+ import { SceneOutletContext } from './SceneOutletContext.js';
7
9
  import { useSceneHistoryNavigation } from './useSceneHistoryNavigation.js';
8
10
 
9
11
  /**
10
- * Executes Scene destinations against an in-memory rendered hierarchy: screen routes replace outlets,
12
+ * Executes Scene destinations against an in-memory rendered hierarchy: a destination into a nested outlet -
13
+ * one a screen declares with `core:outlet` - places its screen there and keeps the screens above it, to any
14
+ * depth; any other screen route replaces the primary region,
11
15
  * dialog destinations render dialog content over the screen that opened them, and external destinations
12
16
  * only report their resolved action. Every navigation is a history entry.
13
17
  */ function SceneNavigationHost({ screens, dialogs = {}, initialScreen, registry, bindingScope = {}, history, destinations, routes }) {
@@ -17,37 +21,50 @@ import { useSceneHistoryNavigation } from './useSceneHistoryNavigation.js';
17
21
  routes,
18
22
  screens
19
23
  ]);
20
- const value = useSceneHistoryNavigation(history ?? memoryHistory, routeTable, initialScreen, bindingScope);
21
- const screen = screens[value.currentScreen];
24
+ const outletOwners = useMemo(()=>collectOutletOwners(screens), [
25
+ screens
26
+ ]);
27
+ const value = useSceneHistoryNavigation(history ?? memoryHistory, routeTable, initialScreen, bindingScope, outletOwners, destinations);
28
+ const outletContext = useMemo(()=>({
29
+ screens,
30
+ registry
31
+ }), [
32
+ registry,
33
+ screens
34
+ ]);
35
+ const screen = screens[value.primaryScreen];
22
36
  const dialog = value.currentDialog ? dialogs[value.currentDialog] : undefined;
23
- return /*#__PURE__*/ jsxs(SceneNavigationProvider, {
37
+ return /*#__PURE__*/ jsx(SceneNavigationProvider, {
24
38
  value: value,
25
- children: [
26
- /*#__PURE__*/ jsx("div", {
27
- "data-scene-outlet": value.currentOutlet ?? 'primary',
28
- "data-scene-url": value.currentUrl,
29
- children: screen && /*#__PURE__*/ jsx(SceneElementView, {
30
- element: screen,
31
- registry: registry,
32
- dataContext: bindingScope.dataContext,
33
- queryResults: bindingScope.queryResults,
34
- componentOutputs: bindingScope.componentOutputs
35
- })
36
- }),
37
- dialog && /*#__PURE__*/ jsx("div", {
38
- role: "dialog",
39
- "aria-modal": "true",
40
- "aria-label": value.currentDialog,
41
- "data-scene-dialog": value.currentDialog,
42
- children: /*#__PURE__*/ jsx(SceneElementView, {
43
- element: dialog,
44
- registry: registry,
45
- dataContext: bindingScope.dataContext,
46
- queryResults: bindingScope.queryResults,
47
- componentOutputs: bindingScope.componentOutputs
39
+ children: /*#__PURE__*/ jsxs(SceneOutletContext.Provider, {
40
+ value: outletContext,
41
+ children: [
42
+ /*#__PURE__*/ jsx("div", {
43
+ "data-scene-outlet": value.primaryScreen === value.currentScreen ? value.currentOutlet ?? 'primary' : 'primary',
44
+ "data-scene-url": value.currentUrl,
45
+ children: screen && /*#__PURE__*/ jsx(SceneElementView, {
46
+ element: screen,
47
+ registry: registry,
48
+ dataContext: bindingScope.dataContext,
49
+ queryResults: bindingScope.queryResults,
50
+ componentOutputs: bindingScope.componentOutputs
51
+ })
52
+ }),
53
+ dialog && /*#__PURE__*/ jsx("div", {
54
+ role: "dialog",
55
+ "aria-modal": "true",
56
+ "aria-label": value.currentDialog,
57
+ "data-scene-dialog": value.currentDialog,
58
+ children: /*#__PURE__*/ jsx(SceneElementView, {
59
+ element: dialog,
60
+ registry: registry,
61
+ dataContext: bindingScope.dataContext,
62
+ queryResults: bindingScope.queryResults,
63
+ componentOutputs: bindingScope.componentOutputs
64
+ })
48
65
  })
49
- })
50
- ]
66
+ ]
67
+ })
51
68
  });
52
69
  }
53
70
 
@@ -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 { 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,QAAAA,CAAS,IAAMC,wBAAAA,EAAAA,CAAAA;IACvC,MAAMC,UAAAA,GAAaC,QAAQ,IAAML,MAAAA,IAAUM,gBAAgBC,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,yBAAAA,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,IAAA,CAACC,uBAAAA,EAAAA;QAAwBR,KAAAA,EAAOA,KAAAA;;0BACnCS,GAAA,CAACC,KAAAA,EAAAA;gBAAIC,mBAAAA,EAAmBX,KAAAA,CAAMY,aAAa,IAAI,SAAA;AAAWC,gBAAAA,gBAAAA,EAAgBb,MAAMc,UAAU;AACrFZ,gBAAAA,QAAAA,EAAAA,MAAAA,kBAAUO,GAAA,CAACM,gBAAAA,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,GAAA,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,GAAA,CAACM,gBAAAA,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;;"}
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 { collectOutletOwners } from './collectOutletOwners';\nimport { SceneOutletContext } from './SceneOutletContext';\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: a destination into a nested outlet -\n * one a screen declares with `core:outlet` - places its screen there and keeps the screens above it, to any\n * depth; any other screen route replaces the primary region,\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 outletOwners = useMemo(() => collectOutletOwners(screens), [screens]);\n const value = useSceneHistoryNavigation(history ?? memoryHistory, routeTable, initialScreen, bindingScope, outletOwners, destinations);\n const outletContext = useMemo(() => ({ screens, registry }), [registry, screens]);\n\n const screen = screens[value.primaryScreen];\n const dialog = value.currentDialog ? dialogs[value.currentDialog] : undefined;\n\n return <SceneNavigationProvider value={value}><SceneOutletContext.Provider value={outletContext}>\n <div data-scene-outlet={value.primaryScreen === value.currentScreen ? value.currentOutlet ?? 'primary' : '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 </SceneOutletContext.Provider></SceneNavigationProvider>;\n}\n"],"names":["SceneNavigationHost","screens","dialogs","initialScreen","registry","bindingScope","history","destinations","routes","memoryHistory","useState","createMemorySceneHistory","routeTable","useMemo","sceneRoutesFrom","Object","keys","outletOwners","collectOutletOwners","value","useSceneHistoryNavigation","outletContext","screen","primaryScreen","dialog","currentDialog","undefined","_jsx","SceneNavigationProvider","_jsxs","SceneOutletContext","Provider","div","data-scene-outlet","currentScreen","currentOutlet","data-scene-url","currentUrl","SceneElementView","element","dataContext","queryResults","componentOutputs","role","aria-modal","aria-label","data-scene-dialog"],"mappings":";;;;;;;;;;AAqCA;;;;;;IAOO,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,QAAAA,CAAS,IAAMC,wBAAAA,EAAAA,CAAAA;IACvC,MAAMC,UAAAA,GAAaC,QAAQ,IAAML,MAAAA,IAAUM,gBAAgBC,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,YAAAA,GAAeJ,OAAAA,CAAQ,IAAMK,mBAAAA,CAAoBjB,OAAAA,CAAAA,EAAU;AAACA,QAAAA;AAAQ,KAAA,CAAA;AAC1E,IAAA,MAAMkB,QAAQC,yBAAAA,CAA0Bd,OAAAA,IAAWG,eAAeG,UAAAA,EAAYT,aAAAA,EAAeE,cAAcY,YAAAA,EAAcV,YAAAA,CAAAA;IACzH,MAAMc,aAAAA,GAAgBR,OAAAA,CAAQ,KAAO;AAAEZ,YAAAA,OAAAA;AAASG,YAAAA;AAAS,SAAA,CAAA,EAAI;AAACA,QAAAA,QAAAA;AAAUH,QAAAA;AAAQ,KAAA,CAAA;AAEhF,IAAA,MAAMqB,MAAAA,GAASrB,OAAO,CAACkB,KAAAA,CAAMI,aAAa,CAAC;IAC3C,MAAMC,MAAAA,GAASL,MAAMM,aAAa,GAAGvB,OAAO,CAACiB,KAAAA,CAAMM,aAAa,CAAC,GAAGC,SAAAA;AAEpE,IAAA,qBAAOC,GAAA,CAACC,uBAAAA,EAAAA;QAAwBT,KAAAA,EAAOA,KAAAA;gCAAOU,IAAA,CAACC,mBAAmBC,QAAQ,EAAA;YAACZ,KAAAA,EAAOE,aAAAA;;8BAC9EM,GAAA,CAACK,KAAAA,EAAAA;oBAAIC,mBAAAA,EAAmBd,KAAAA,CAAMI,aAAa,KAAKJ,KAAAA,CAAMe,aAAa,GAAGf,KAAAA,CAAMgB,aAAa,IAAI,SAAA,GAAY,SAAA;AAAWC,oBAAAA,gBAAAA,EAAgBjB,MAAMkB,UAAU;AAC/If,oBAAAA,QAAAA,EAAAA,MAAAA,kBAAUK,GAAA,CAACW,gBAAAA,EAAAA;wBAAiBC,OAAAA,EAASjB,MAAAA;wBAAQlB,QAAAA,EAAUA,QAAAA;AAAUoC,wBAAAA,WAAAA,EAAanC,aAAamC,WAAW;AAAEC,wBAAAA,YAAAA,EAAcpC,aAAaoC,YAAY;AAAEC,wBAAAA,gBAAAA,EAAkBrC,aAAaqC;;;AAEpLlB,gBAAAA,MAAAA,kBAAUG,GAAA,CAACK,KAAAA,EAAAA;oBAAIW,IAAAA,EAAK,QAAA;oBAASC,YAAAA,EAAW,MAAA;AAAOC,oBAAAA,YAAAA,EAAY1B,MAAMM,aAAa;AAAEqB,oBAAAA,mBAAAA,EAAmB3B,MAAMM,aAAa;AACnH,oBAAA,QAAA,gBAAAE,GAAA,CAACW,gBAAAA,EAAAA;wBAAiBC,OAAAA,EAASf,MAAAA;wBAAQpB,QAAAA,EAAUA,QAAAA;AAAUoC,wBAAAA,WAAAA,EAAanC,aAAamC,WAAW;AAAEC,wBAAAA,YAAAA,EAAcpC,aAAaoC,YAAY;AAAEC,wBAAAA,gBAAAA,EAAkBrC,aAAaqC;;;;;;AAGlL;;"}
@@ -1,5 +1,7 @@
1
1
  export interface SceneNavigationState {
2
2
  screen: string;
3
+ primary?: string;
4
+ outlets?: Record<string, string>;
3
5
  url?: string;
4
6
  outlet?: string;
5
7
  dialog?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"SceneNavigationState.d.ts","sourceRoot":"","sources":["../../../navigation/SceneNavigationState.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,MAAM,CAAC;IAGf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC"}
1
+ {"version":3,"file":"SceneNavigationState.d.ts","sourceRoot":"","sources":["../../../navigation/SceneNavigationState.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,oBAAoB;IAEjC,MAAM,EAAE,MAAM,CAAC;IAMf,OAAO,CAAC,EAAE,MAAM,CAAC;IAMjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAGhB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC"}
@@ -0,0 +1,9 @@
1
+ import { SceneElement } from '@cratis/scene.model';
2
+ import { ComponentRegistry } from '../renderer';
3
+ export interface SceneOutletContextValue {
4
+ screens: Record<string, SceneElement>;
5
+ registry: ComponentRegistry;
6
+ }
7
+ export declare const SceneOutletContext: import("react").Context<SceneOutletContextValue | undefined>;
8
+ export declare function useSceneOutlets(): SceneOutletContextValue | undefined;
9
+ //# sourceMappingURL=SceneOutletContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SceneOutletContext.d.ts","sourceRoot":"","sources":["../../../navigation/SceneOutletContext.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhD,MAAM,WAAW,uBAAuB;IACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACtC,QAAQ,EAAE,iBAAiB,CAAC;CAC/B;AAED,eAAO,MAAM,kBAAkB,8DAAgE,CAAC;AAGhG,wBAAgB,eAAe,IAAI,uBAAuB,GAAG,SAAS,CAErE"}
@@ -0,0 +1,11 @@
1
+ import { createContext, useContext } from 'react';
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 SceneOutletContext = /*#__PURE__*/ createContext(undefined);
6
+ /** The screens and registry of the enclosing navigation host, or undefined outside one. */ function useSceneOutlets() {
7
+ return useContext(SceneOutletContext);
8
+ }
9
+
10
+ export { SceneOutletContext, useSceneOutlets };
11
+ //# sourceMappingURL=SceneOutletContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SceneOutletContext.js","sources":["../../../navigation/SceneOutletContext.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 { createContext, useContext } from 'react';\nimport { SceneElement } from '@cratis/scene.model';\nimport { ComponentRegistry } from '../renderer';\n\n/** What a nested outlet needs to render the screen placed in it. */\nexport interface SceneOutletContextValue {\n screens: Record<string, SceneElement>;\n registry: ComponentRegistry;\n}\n\nexport const SceneOutletContext = createContext<SceneOutletContextValue | undefined>(undefined);\n\n/** The screens and registry of the enclosing navigation host, or undefined outside one. */\nexport function useSceneOutlets(): SceneOutletContextValue | undefined {\n return useContext(SceneOutletContext);\n}\n"],"names":["SceneOutletContext","createContext","undefined","useSceneOutlets","useContext"],"mappings":";;AAAA;AACA;AAYO,MAAMA,kBAAAA,iBAAqBC,aAAAA,CAAmDC,SAAAA;AAErF,4FACO,SAASC,eAAAA,GAAAA;AACZ,IAAA,OAAOC,UAAAA,CAAWJ,kBAAAA,CAAAA;AACtB;;"}
@@ -0,0 +1,4 @@
1
+ import { SceneElement } from '@cratis/scene.model';
2
+ export declare const outletComponentName = "core:outlet";
3
+ export declare function collectOutletOwners(screens: Record<string, SceneElement>): Record<string, string>;
4
+ //# sourceMappingURL=collectOutletOwners.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collectOutletOwners.d.ts","sourceRoot":"","sources":["../../../navigation/collectOutletOwners.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGnD,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AAOjD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASjG"}
@@ -0,0 +1,33 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+ /** The registry key of the component that declares a nested outlet. */ const outletComponentName = 'core:outlet';
4
+ /**
5
+ * Which screen declares each nested outlet: every `core:outlet` element, by its `name`, mapped to the screen
6
+ * whose tree contains it. A screen that declares an outlet another screen also declares keeps the first one,
7
+ * in screen order - the same rule navigation diagnostics report as a duplicate outlet.
8
+ */ function collectOutletOwners(screens) {
9
+ const owners = {};
10
+ for (const [screen, element] of Object.entries(screens)){
11
+ for (const outlet of outletNames(element)){
12
+ if (!(outlet in owners)) owners[outlet] = screen;
13
+ }
14
+ }
15
+ return owners;
16
+ }
17
+ function outletNames(element) {
18
+ const node = element;
19
+ const own = node.componentName === outletComponentName && typeof node.properties?.name === 'string' ? [
20
+ node.properties.name
21
+ ] : [];
22
+ const children = [
23
+ ...Object.values(node.slots ?? {}).flat(),
24
+ ...Array.isArray(node.children) ? node.children : []
25
+ ];
26
+ return [
27
+ ...own,
28
+ ...children.flatMap(outletNames)
29
+ ];
30
+ }
31
+
32
+ export { collectOutletOwners, outletComponentName };
33
+ //# sourceMappingURL=collectOutletOwners.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collectOutletOwners.js","sources":["../../../navigation/collectOutletOwners.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 { SceneElement } from '@cratis/scene.model';\n\n/** The registry key of the component that declares a nested outlet. */\nexport const outletComponentName = 'core:outlet';\n\n/**\n * Which screen declares each nested outlet: every `core:outlet` element, by its `name`, mapped to the screen\n * whose tree contains it. A screen that declares an outlet another screen also declares keeps the first one,\n * in screen order - the same rule navigation diagnostics report as a duplicate outlet.\n */\nexport function collectOutletOwners(screens: Record<string, SceneElement>): Record<string, string> {\n const owners: Record<string, string> = {};\n for (const [screen, element] of Object.entries(screens)) {\n for (const outlet of outletNames(element)) {\n if (!(outlet in owners)) owners[outlet] = screen;\n }\n }\n\n return owners;\n}\n\nfunction outletNames(element: SceneElement): string[] {\n const node = element as { componentName?: string; properties?: Record<string, unknown>; slots?: Record<string, SceneElement[]>; children?: SceneElement[] };\n const own = node.componentName === outletComponentName && typeof node.properties?.name === 'string' ? [node.properties.name] : [];\n const children = [...Object.values(node.slots ?? {}).flat(), ...(Array.isArray(node.children) ? node.children : [])];\n return [...own, ...children.flatMap(outletNames)];\n}\n"],"names":["outletComponentName","collectOutletOwners","screens","owners","screen","element","Object","entries","outlet","outletNames","node","own","componentName","properties","name","children","values","slots","flat","Array","isArray","flatMap"],"mappings":"AAAA;AACA;AAIA,wEACO,MAAMA,mBAAAA,GAAsB;AAEnC;;;;IAKO,SAASC,mBAAAA,CAAoBC,OAAqC,EAAA;AACrE,IAAA,MAAMC,SAAiC,EAAC;IACxC,KAAK,MAAM,CAACC,MAAAA,EAAQC,OAAAA,CAAQ,IAAIC,MAAAA,CAAOC,OAAO,CAACL,OAAAA,CAAAA,CAAU;QACrD,KAAK,MAAMM,MAAAA,IAAUC,WAAAA,CAAYJ,OAAAA,CAAAA,CAAU;YACvC,IAAI,EAAEG,MAAAA,IAAUL,MAAK,GAAIA,MAAM,CAACK,OAAO,GAAGJ,MAAAA;AAC9C,QAAA;AACJ,IAAA;IAEA,OAAOD,MAAAA;AACX;AAEA,SAASM,YAAYJ,OAAqB,EAAA;AACtC,IAAA,MAAMK,IAAAA,GAAOL,OAAAA;IACb,MAAMM,GAAAA,GAAMD,IAAAA,CAAKE,aAAa,KAAKZ,mBAAAA,IAAuB,OAAOU,IAAAA,CAAKG,UAAU,EAAEC,IAAAA,KAAS,QAAA,GAAW;QAACJ,IAAAA,CAAKG,UAAU,CAACC;AAAK,KAAA,GAAG,EAAE;AACjI,IAAA,MAAMC,QAAAA,GAAW;AAAIT,QAAAA,GAAAA,MAAAA,CAAOU,MAAM,CAACN,IAAAA,CAAKO,KAAK,IAAI,IAAIC,IAAI,EAAA;WAAQC,KAAAA,CAAMC,OAAO,CAACV,IAAAA,CAAKK,QAAQ,IAAIL,IAAAA,CAAKK,QAAQ,GAAG;AAAI,KAAA;IACpH,OAAO;AAAIJ,QAAAA,GAAAA,GAAAA;AAAQI,QAAAA,GAAAA,QAAAA,CAASM,OAAO,CAACZ,WAAAA;AAAa,KAAA;AACrD;;"}
@@ -15,4 +15,8 @@ export * from './navigationRouteKey';
15
15
  export * from './resolveDestination';
16
16
  export * from './sceneRoutesFrom';
17
17
  export * from './useSceneHistoryNavigation';
18
+ export * from './CoreOutlet';
19
+ export * from './SceneOutletContext';
20
+ export * from './collectOutletOwners';
21
+ export * from './placeInOutlet';
18
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../navigation/index.ts"],"names":[],"mappings":"AAGA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../navigation/index.ts"],"names":[],"mappings":"AAGA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC"}
@@ -15,4 +15,8 @@ export * from './navigationRouteKey';
15
15
  export * from './resolveDestination';
16
16
  export * from './sceneRoutesFrom';
17
17
  export * from './useSceneHistoryNavigation';
18
+ export * from './CoreOutlet';
19
+ export * from './SceneOutletContext';
20
+ export * from './collectOutletOwners';
21
+ export * from './placeInOutlet';
18
22
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../navigation/index.ts"],"names":[],"mappings":"AAGA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../navigation/index.ts"],"names":[],"mappings":"AAGA,cAAc,yBAAyB,CAAC;AACxC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,cAAc,CAAC;AAC7B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { DestinationReference } from '@cratis/scene.model';
2
+ import { SceneNavigationState } from './SceneNavigationState';
3
+ export declare function placeInOutlet(current: SceneNavigationState, screen: string, outlet: string | undefined, owners: Record<string, string>, destinations?: DestinationReference[]): Pick<SceneNavigationState, 'primary' | 'outlets'>;
4
+ //# sourceMappingURL=placeInOutlet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placeInOutlet.d.ts","sourceRoot":"","sources":["../../../navigation/placeInOutlet.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAa9D,wBAAgB,aAAa,CACzB,OAAO,EAAE,oBAAoB,EAC7B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,YAAY,GAAE,oBAAoB,EAAO,GAC1C,IAAI,CAAC,oBAAoB,EAAE,SAAS,GAAG,SAAS,CAAC,CAmBnD"}
@@ -0,0 +1,63 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+ /** The most levels of nesting a placement follows before it stops, so a cyclic model cannot loop forever. */ const maximumDepth = 32;
4
+ /**
5
+ * Places a screen, recursively composing nested outlets.
6
+ *
7
+ * When `outlet` is a nested outlet - one a screen declares with `core:outlet` - the screen goes into it, and
8
+ * the screen that declares it is placed first: where it already is, or, when it is not on the page, where a
9
+ * known destination puts it, up to the primary region. Outlets declared by screens that are no longer on the
10
+ * page are dropped. Any other outlet replaces the primary region and clears every nested placement.
11
+ */ function placeInOutlet(current, screen, outlet, owners, destinations = []) {
12
+ const primary = current.primary ?? current.screen;
13
+ const placements = {
14
+ ...current.outlets ?? {}
15
+ };
16
+ let root = primary;
17
+ let target = {
18
+ screen,
19
+ outlet
20
+ };
21
+ for(let depth = 0; target && depth < maximumDepth; depth++){
22
+ const owner = target.outlet === undefined ? undefined : owners[target.outlet];
23
+ if (owner === undefined) {
24
+ root = target.screen;
25
+ break;
26
+ }
27
+ placements[target.outlet] = target.screen;
28
+ if (isOnPage(owner, root, placements, owners)) break;
29
+ target = {
30
+ screen: owner,
31
+ outlet: destinations.find((destination)=>destination.screen === owner && destination.outlet)?.outlet
32
+ };
33
+ }
34
+ return {
35
+ primary: root,
36
+ outlets: reachable(root, placements, owners)
37
+ };
38
+ }
39
+ /** Whether a screen is the root or shown in an outlet reachable from it. */ function isOnPage(screen, root, placements, owners) {
40
+ return screen === root || Object.values(reachable(root, placements, owners)).includes(screen);
41
+ }
42
+ /** Only the placements reachable from the root through the outlets each placed screen declares. */ function reachable(root, placements, owners) {
43
+ const kept = {};
44
+ const pending = [
45
+ root
46
+ ];
47
+ const seen = new Set();
48
+ while(pending.length > 0){
49
+ const screen = pending.shift();
50
+ if (seen.has(screen)) continue;
51
+ seen.add(screen);
52
+ for (const [outlet, placed] of Object.entries(placements)){
53
+ if (owners[outlet] === screen) {
54
+ kept[outlet] = placed;
55
+ pending.push(placed);
56
+ }
57
+ }
58
+ }
59
+ return kept;
60
+ }
61
+
62
+ export { placeInOutlet };
63
+ //# sourceMappingURL=placeInOutlet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placeInOutlet.js","sources":["../../../navigation/placeInOutlet.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 { DestinationReference } from '@cratis/scene.model';\nimport { SceneNavigationState } from './SceneNavigationState';\n\n/** The most levels of nesting a placement follows before it stops, so a cyclic model cannot loop forever. */\nconst maximumDepth = 32;\n\n/**\n * Places a screen, recursively composing nested outlets.\n *\n * When `outlet` is a nested outlet - one a screen declares with `core:outlet` - the screen goes into it, and\n * the screen that declares it is placed first: where it already is, or, when it is not on the page, where a\n * known destination puts it, up to the primary region. Outlets declared by screens that are no longer on the\n * page are dropped. Any other outlet replaces the primary region and clears every nested placement.\n */\nexport function placeInOutlet(\n current: SceneNavigationState,\n screen: string,\n outlet: string | undefined,\n owners: Record<string, string>,\n destinations: DestinationReference[] = [],\n): Pick<SceneNavigationState, 'primary' | 'outlets'> {\n const primary = current.primary ?? current.screen;\n const placements: Record<string, string> = { ...(current.outlets ?? {}) };\n let root = primary;\n\n let target: { screen: string; outlet: string | undefined } | undefined = { screen, outlet };\n for (let depth = 0; target && depth < maximumDepth; depth++) {\n const owner = target.outlet === undefined ? undefined : owners[target.outlet];\n if (owner === undefined) {\n root = target.screen;\n break;\n }\n\n placements[target.outlet!] = target.screen;\n if (isOnPage(owner, root, placements, owners)) break;\n target = { screen: owner, outlet: destinations.find(destination => destination.screen === owner && destination.outlet)?.outlet };\n }\n\n return { primary: root, outlets: reachable(root, placements, owners) };\n}\n\n/** Whether a screen is the root or shown in an outlet reachable from it. */\nfunction isOnPage(screen: string, root: string, placements: Record<string, string>, owners: Record<string, string>): boolean {\n return screen === root || Object.values(reachable(root, placements, owners)).includes(screen);\n}\n\n/** Only the placements reachable from the root through the outlets each placed screen declares. */\nfunction reachable(root: string, placements: Record<string, string>, owners: Record<string, string>): Record<string, string> {\n const kept: Record<string, string> = {};\n const pending = [root];\n const seen = new Set<string>();\n while (pending.length > 0) {\n const screen = pending.shift()!;\n if (seen.has(screen)) continue;\n seen.add(screen);\n for (const [outlet, placed] of Object.entries(placements)) {\n if (owners[outlet] === screen) {\n kept[outlet] = placed;\n pending.push(placed);\n }\n }\n }\n\n return kept;\n}\n"],"names":["maximumDepth","placeInOutlet","current","screen","outlet","owners","destinations","primary","placements","outlets","root","target","depth","owner","undefined","isOnPage","find","destination","reachable","Object","values","includes","kept","pending","seen","Set","length","shift","has","add","placed","entries","push"],"mappings":"AAAA;AACA;AAKA,8GACA,MAAMA,YAAAA,GAAe,EAAA;AAErB;;;;;;;AAOC,IACM,SAASC,aAAAA,CACZC,OAA6B,EAC7BC,MAAc,EACdC,MAA0B,EAC1BC,MAA8B,EAC9BC,YAAAA,GAAuC,EAAE,EAAA;AAEzC,IAAA,MAAMC,OAAAA,GAAUL,OAAAA,CAAQK,OAAO,IAAIL,QAAQC,MAAM;AACjD,IAAA,MAAMK,UAAAA,GAAqC;AAAE,QAAA,GAAIN,OAAAA,CAAQO,OAAO,IAAI;AAAI,KAAA;AACxE,IAAA,IAAIC,IAAAA,GAAOH,OAAAA;AAEX,IAAA,IAAII,MAAAA,GAAqE;AAAER,QAAAA,MAAAA;AAAQC,QAAAA;AAAO,KAAA;AAC1F,IAAA,IAAK,IAAIQ,KAAAA,GAAQ,CAAA,EAAGD,MAAAA,IAAUC,KAAAA,GAAQZ,cAAcY,KAAAA,EAAAA,CAAS;QACzD,MAAMC,KAAAA,GAAQF,MAAAA,CAAOP,MAAM,KAAKU,SAAAA,GAAYA,YAAYT,MAAM,CAACM,MAAAA,CAAOP,MAAM,CAAC;AAC7E,QAAA,IAAIS,UAAUC,SAAAA,EAAW;AACrBJ,YAAAA,IAAAA,GAAOC,OAAOR,MAAM;AACpB,YAAA;AACJ,QAAA;AAEAK,QAAAA,UAAU,CAACG,MAAAA,CAAOP,MAAM,CAAE,GAAGO,OAAOR,MAAM;AAC1C,QAAA,IAAIY,QAAAA,CAASF,KAAAA,EAAOH,IAAAA,EAAMF,UAAAA,EAAYH,MAAAA,CAAAA,EAAS;QAC/CM,MAAAA,GAAS;YAAER,MAAAA,EAAQU,KAAAA;YAAOT,MAAAA,EAAQE,YAAAA,CAAaU,IAAI,CAACC,CAAAA,WAAAA,GAAeA,WAAAA,CAAYd,MAAM,KAAKU,KAAAA,IAASI,WAAAA,CAAYb,MAAM,CAAA,EAAGA;AAAO,SAAA;AACnI,IAAA;IAEA,OAAO;QAAEG,OAAAA,EAASG,IAAAA;QAAMD,OAAAA,EAASS,SAAAA,CAAUR,MAAMF,UAAAA,EAAYH,MAAAA;AAAQ,KAAA;AACzE;AAEA,6EACA,SAASU,QAAAA,CAASZ,MAAc,EAAEO,IAAY,EAAEF,UAAkC,EAAEH,MAA8B,EAAA;IAC9G,OAAOF,MAAAA,KAAWO,IAAAA,IAAQS,MAAAA,CAAOC,MAAM,CAACF,UAAUR,IAAAA,EAAMF,UAAAA,EAAYH,MAAAA,CAAAA,CAAAA,CAASgB,QAAQ,CAAClB,MAAAA,CAAAA;AAC1F;AAEA,oGACA,SAASe,SAAAA,CAAUR,IAAY,EAAEF,UAAkC,EAAEH,MAA8B,EAAA;AAC/F,IAAA,MAAMiB,OAA+B,EAAC;AACtC,IAAA,MAAMC,OAAAA,GAAU;AAACb,QAAAA;AAAK,KAAA;AACtB,IAAA,MAAMc,OAAO,IAAIC,GAAAA,EAAAA;IACjB,MAAOF,OAAAA,CAAQG,MAAM,GAAG,CAAA,CAAG;QACvB,MAAMvB,MAAAA,GAASoB,QAAQI,KAAK,EAAA;QAC5B,IAAIH,IAAAA,CAAKI,GAAG,CAACzB,MAAAA,CAAAA,EAAS;AACtBqB,QAAAA,IAAAA,CAAKK,GAAG,CAAC1B,MAAAA,CAAAA;QACT,KAAK,MAAM,CAACC,MAAAA,EAAQ0B,MAAAA,CAAO,IAAIX,MAAAA,CAAOY,OAAO,CAACvB,UAAAA,CAAAA,CAAa;AACvD,YAAA,IAAIH,MAAM,CAACD,MAAAA,CAAO,KAAKD,MAAAA,EAAQ;gBAC3BmB,IAAI,CAAClB,OAAO,GAAG0B,MAAAA;AACfP,gBAAAA,OAAAA,CAAQS,IAAI,CAACF,MAAAA,CAAAA;AACjB,YAAA;AACJ,QAAA;AACJ,IAAA;IAEA,OAAOR,IAAAA;AACX;;"}
@@ -1,6 +1,7 @@
1
1
  import { BindingScope } from '@cratis/scene.engine';
2
+ import { DestinationReference } from '@cratis/scene.model';
2
3
  import { SceneHistory } from './SceneHistory';
3
4
  import { SceneNavigationContextValue } from './SceneNavigationContext';
4
5
  import { SceneRoute } from './SceneRoute';
5
- export declare function useSceneHistoryNavigation(history: SceneHistory, routes: SceneRoute[], initialScreen: string, bindingScope: BindingScope): SceneNavigationContextValue;
6
+ export declare function useSceneHistoryNavigation(history: SceneHistory, routes: SceneRoute[], initialScreen: string, bindingScope: BindingScope, outletOwners?: Record<string, string>, destinations?: DestinationReference[]): SceneNavigationContextValue;
6
7
  //# sourceMappingURL=useSceneHistoryNavigation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSceneHistoryNavigation.d.ts","sourceRoot":"","sources":["../../../navigation/useSceneHistoryNavigation.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAKpD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAEvE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAO1C,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,GAAG,2BAA2B,CA4DrK"}
1
+ {"version":3,"file":"useSceneHistoryNavigation.d.ts","sourceRoot":"","sources":["../../../navigation/useSceneHistoryNavigation.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAmB,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAK5E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAEvE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAO1C,wBAAgB,yBAAyB,CACrC,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,UAAU,EAAE,EACpB,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,YAAY,EAC1B,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EACzC,YAAY,GAAE,oBAAoB,EAAO,GAC1C,2BAA2B,CAmE7B"}
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useMemo, useState, useRef, useEffect } from 'react';
2
2
  import { DestinationKind } from '@cratis/scene.model';
3
3
  import { matchSceneRoute } from './matchSceneRoute.js';
4
+ import { placeInOutlet } from './placeInOutlet.js';
4
5
  import { resolveDestination } from './resolveDestination.js';
5
6
 
6
7
  // Copyright (c) Cratis. All rights reserved.
@@ -9,16 +10,24 @@ import { resolveDestination } from './resolveDestination.js';
9
10
  * Keeps a navigation host's state in a {@link SceneHistory}: every navigation and every dialog is an entry,
10
11
  * so back and forward step through them, a refresh restores the stored entry, and a deep link is matched
11
12
  * against the route table. Closing a dialog goes back to the entry that opened it.
12
- */ function useSceneHistoryNavigation(history, routes, initialScreen, bindingScope) {
13
+ */ function useSceneHistoryNavigation(history, routes, initialScreen, bindingScope, outletOwners = {}, destinations = []) {
13
14
  const fromLocation = useCallback((url)=>{
14
15
  const match = url ? matchSceneRoute(url, routes) : undefined;
15
- return match ? {
16
+ if (!match) return undefined;
17
+ const start = {
18
+ screen: initialScreen};
19
+ const placement = placeInOutlet(start, match.route.screen, match.route.outlet, outletOwners, destinations);
20
+ return {
16
21
  screen: match.route.screen,
17
22
  url,
18
23
  outlet: match.route.outlet,
19
- parameters: match.parameters
20
- } : undefined;
24
+ parameters: match.parameters,
25
+ ...placement
26
+ };
21
27
  }, [
28
+ destinations,
29
+ initialScreen,
30
+ outletOwners,
22
31
  routes
23
32
  ]);
24
33
  const fallback = useMemo(()=>({
@@ -59,17 +68,22 @@ import { resolveDestination } from './resolveDestination.js';
59
68
  dialog: resolution.dialog
60
69
  });
61
70
  } else if (resolution.kind !== DestinationKind.External) {
71
+ const screen = destination.screen ?? destination.slice ?? state.screen;
72
+ const placement = placeInOutlet(state, screen, resolution.outlet, outletOwners, destinations);
62
73
  go({
63
- screen: destination.screen ?? destination.slice ?? state.screen,
74
+ screen,
64
75
  url: resolution.url,
65
76
  outlet: resolution.outlet,
66
- parameters: resolution.parameters
77
+ parameters: resolution.parameters,
78
+ ...placement
67
79
  });
68
80
  }
69
81
  return resolution;
70
82
  }, [
71
83
  bindingScope,
84
+ destinations,
72
85
  go,
86
+ outletOwners,
73
87
  state
74
88
  ]);
75
89
  const closeDialog = useCallback((result)=>{
@@ -85,6 +99,8 @@ import { resolveDestination } from './resolveDestination.js';
85
99
  ]);
86
100
  return useMemo(()=>({
87
101
  currentScreen: state.screen,
102
+ primaryScreen: state.primary ?? state.screen,
103
+ outlets: state.outlets ?? {},
88
104
  currentUrl: state.url,
89
105
  currentOutlet: state.outlet,
90
106
  currentDialog: state.dialog,
@@ -1 +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":"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"}
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 { placeInOutlet } from './placeInOutlet';\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(\n history: SceneHistory,\n routes: SceneRoute[],\n initialScreen: string,\n bindingScope: BindingScope,\n outletOwners: Record<string, string> = {},\n destinations: DestinationReference[] = [],\n): SceneNavigationContextValue {\n const fromLocation = useCallback((url: string | undefined): SceneNavigationState | undefined => {\n const match = url ? matchSceneRoute(url, routes) : undefined;\n if (!match) return undefined;\n const start: SceneNavigationState = { screen: initialScreen, parameters: {} };\n const placement = placeInOutlet(start, match.route.screen, match.route.outlet, outletOwners, destinations);\n return { screen: match.route.screen, url, outlet: match.route.outlet, parameters: match.parameters, ...placement };\n }, [destinations, initialScreen, outletOwners, 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 const screen = destination.screen ?? destination.slice ?? state.screen;\n const placement = placeInOutlet(state, screen, resolution.outlet, outletOwners, destinations);\n go({ screen, url: resolution.url, outlet: resolution.outlet, parameters: resolution.parameters, ...placement });\n }\n\n return resolution;\n }, [bindingScope, destinations, go, outletOwners, 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 primaryScreen: state.primary ?? state.screen,\n outlets: state.outlets ?? {},\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","outletOwners","destinations","fromLocation","useCallback","url","match","matchSceneRoute","undefined","start","screen","placement","placeInOutlet","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","primaryScreen","primary","outlets","currentUrl","currentOutlet","currentDialog","currentParameters"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ScenePackageBundle.d.ts","sourceRoot":"","sources":["../../../packages/ScenePackageBundle.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,EAAkC,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC/J,OAAO,EAAE,mBAAmB,EAA6B,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,2BAA2B,EAAuC,MAAM,+BAA+B,CAAC;AACjH,OAAO,EAAE,gBAAgB,EAA4B,MAAM,oBAAoB,CAAC;AAchF,MAAM,WAAW,kBAAkB;IAK/B,QAAQ,EAAE,YAAY,CAAC;IAKvB,UAAU,EAAE,iBAAiB,CAAC;IAK9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAMnB,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IAKnC,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IAMnC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAQnB,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAKpC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IAMjB,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAMhC,mBAAmB,CAAC,EAAE,2BAA2B,CAAC;IAGlD,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAYD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAEvF;AAWD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAyD1E;AAmFD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,iBAAiB,CAEvF;AAOD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,mBAAmB,CAEpF"}
1
+ {"version":3,"file":"ScenePackageBundle.d.ts","sourceRoot":"","sources":["../../../packages/ScenePackageBundle.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,EAAsD,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACnL,OAAO,EAAE,mBAAmB,EAA6B,MAAM,8BAA8B,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,2BAA2B,EAAuC,MAAM,+BAA+B,CAAC;AACjH,OAAO,EAAE,gBAAgB,EAA4B,MAAM,oBAAoB,CAAC;AAchF,MAAM,WAAW,kBAAkB;IAK/B,QAAQ,EAAE,YAAY,CAAC;IAKvB,UAAU,EAAE,iBAAiB,CAAC;IAK9B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAMnB,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IAKnC,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IAMnC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAQnB,WAAW,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAKpC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IAMjB,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAMhC,mBAAmB,CAAC,EAAE,2BAA2B,CAAC;IAGlD,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAYD,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAEvF;AAWD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAyD1E;AAgGD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,iBAAiB,CAEvF;AAOD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,mBAAmB,CAEpF"}
@@ -103,6 +103,28 @@ function validateIconLibrary(bundle) {
103
103
  }
104
104
  return problems;
105
105
  }
106
+ function validateProperties(component, properties, parent, problems) {
107
+ const paths = new Set();
108
+ for (const property of properties){
109
+ const path = `${parent}${property.path}`;
110
+ if (paths.has(property.path)) {
111
+ problems.push(`'${component}' describes the property '${path}' more than once`);
112
+ }
113
+ paths.add(property.path);
114
+ if (property.valueType === PropertyValueType.Collection && property.item === undefined) {
115
+ problems.push(`'${component}' describes the collection '${path}' without describing its items`);
116
+ }
117
+ if (property.valueType === PropertyValueType.Collection && property.item !== undefined) {
118
+ validateProperties(component, property.item.properties, `${path}[].`, problems);
119
+ }
120
+ if (property.valueType === PropertyValueType.Object && property.properties !== undefined) {
121
+ validateProperties(component, property.properties, `${path}.`, problems);
122
+ }
123
+ if (property.valueType === PropertyValueType.Enum && (property.choices ?? []).length === 0) {
124
+ problems.push(`'${component}' describes the enum '${path}' without choices`);
125
+ }
126
+ }
127
+ }
106
128
  function validateDescriptors(bundle) {
107
129
  const problems = [];
108
130
  const declared = new Set(bundle.manifest.components.map((name)=>componentRegistryKey(bundle.manifest.name, name)));
@@ -115,19 +137,7 @@ function validateDescriptors(bundle) {
115
137
  problems.push(`describes '${descriptor.component}' more than once`);
116
138
  }
117
139
  described.add(descriptor.component);
118
- const paths = new Set();
119
- for (const property of descriptor.properties){
120
- if (paths.has(property.path)) {
121
- problems.push(`'${descriptor.component}' describes the property '${property.path}' more than once`);
122
- }
123
- paths.add(property.path);
124
- if (property.valueType === PropertyValueType.Collection && property.item === undefined) {
125
- problems.push(`'${descriptor.component}' describes the collection '${property.path}' without describing its items`);
126
- }
127
- if (property.valueType === PropertyValueType.Enum && (property.choices ?? []).length === 0) {
128
- problems.push(`'${descriptor.component}' describes the enum '${property.path}' without choices`);
129
- }
130
- }
140
+ validateProperties(descriptor.component, descriptor.properties, '', problems);
131
141
  }
132
142
  return problems;
133
143
  }