@cratis/scene.react 4.10.0 → 4.12.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 (92) hide show
  1. package/dist/cjs/core/coreComponents.js +3 -1
  2. package/dist/cjs/core/coreComponents.js.map +1 -1
  3. package/dist/cjs/core/coreDescriptors.js +2 -0
  4. package/dist/cjs/core/coreDescriptors.js.map +1 -1
  5. package/dist/cjs/index.js +17 -0
  6. package/dist/cjs/index.js.map +1 -1
  7. package/dist/cjs/navigation/SceneNavigationContext.js +21 -0
  8. package/dist/cjs/navigation/SceneNavigationContext.js.map +1 -0
  9. package/dist/cjs/navigation/SceneNavigationHost.js +82 -0
  10. package/dist/cjs/navigation/SceneNavigationHost.js.map +1 -0
  11. package/dist/cjs/packages/EmbeddedPackageHost.js +15 -0
  12. package/dist/cjs/packages/EmbeddedPackageHost.js.map +1 -0
  13. package/dist/cjs/packages/PackageHostAssets.js +30 -0
  14. package/dist/cjs/packages/PackageHostAssets.js.map +1 -0
  15. package/dist/cjs/packages/PackageHostConfiguration.js +17 -9
  16. package/dist/cjs/packages/PackageHostConfiguration.js.map +1 -1
  17. package/dist/cjs/packages/PackageHostRenderMode.js +15 -0
  18. package/dist/cjs/packages/PackageHostRenderMode.js.map +1 -0
  19. package/dist/cjs/packages/PackageHostView.js +53 -0
  20. package/dist/cjs/packages/PackageHostView.js.map +1 -0
  21. package/dist/cjs/packages/StandalonePackageHost.js +15 -0
  22. package/dist/cjs/packages/StandalonePackageHost.js.map +1 -0
  23. package/dist/cjs/packages/WebViewPackageHost.js +15 -0
  24. package/dist/cjs/packages/WebViewPackageHost.js.map +1 -0
  25. package/dist/cjs/renderer/InteractiveComponent.js +4 -0
  26. package/dist/cjs/renderer/InteractiveComponent.js.map +1 -1
  27. package/dist/esm/core/coreComponents.js +3 -1
  28. package/dist/esm/core/coreComponents.js.map +1 -1
  29. package/dist/esm/core/coreDescriptors.js +2 -0
  30. package/dist/esm/core/coreDescriptors.js.map +1 -1
  31. package/dist/esm/index.js +8 -0
  32. package/dist/esm/index.js.map +1 -1
  33. package/dist/esm/navigation/SceneNavigationContext.d.ts +20 -0
  34. package/dist/esm/navigation/SceneNavigationContext.d.ts.map +1 -0
  35. package/dist/esm/navigation/SceneNavigationContext.js +18 -0
  36. package/dist/esm/navigation/SceneNavigationContext.js.map +1 -0
  37. package/dist/esm/navigation/SceneNavigationHost.d.ts +12 -0
  38. package/dist/esm/navigation/SceneNavigationHost.d.ts.map +1 -0
  39. package/dist/esm/navigation/SceneNavigationHost.js +80 -0
  40. package/dist/esm/navigation/SceneNavigationHost.js.map +1 -0
  41. package/dist/esm/navigation/index.d.ts +2 -0
  42. package/dist/esm/navigation/index.d.ts.map +1 -1
  43. package/dist/esm/navigation/index.js +2 -0
  44. package/dist/esm/navigation/index.js.map +1 -1
  45. package/dist/esm/packages/EmbeddedPackageHost.d.ts +3 -0
  46. package/dist/esm/packages/EmbeddedPackageHost.d.ts.map +1 -0
  47. package/dist/esm/packages/EmbeddedPackageHost.js +13 -0
  48. package/dist/esm/packages/EmbeddedPackageHost.js.map +1 -0
  49. package/dist/esm/packages/PackageHostAssets.d.ts +6 -0
  50. package/dist/esm/packages/PackageHostAssets.d.ts.map +1 -0
  51. package/dist/esm/packages/PackageHostAssets.js +28 -0
  52. package/dist/esm/packages/PackageHostAssets.js.map +1 -0
  53. package/dist/esm/packages/PackageHostConfiguration.d.ts +3 -0
  54. package/dist/esm/packages/PackageHostConfiguration.d.ts.map +1 -1
  55. package/dist/esm/packages/PackageHostConfiguration.js +17 -9
  56. package/dist/esm/packages/PackageHostConfiguration.js.map +1 -1
  57. package/dist/esm/packages/PackageHostRenderMode.d.ts +6 -0
  58. package/dist/esm/packages/PackageHostRenderMode.d.ts.map +1 -0
  59. package/dist/esm/packages/PackageHostRenderMode.js +13 -0
  60. package/dist/esm/packages/PackageHostRenderMode.js.map +1 -0
  61. package/dist/esm/packages/PackageHostView.d.ts +13 -0
  62. package/dist/esm/packages/PackageHostView.d.ts.map +1 -0
  63. package/dist/esm/packages/PackageHostView.js +51 -0
  64. package/dist/esm/packages/PackageHostView.js.map +1 -0
  65. package/dist/esm/packages/StandalonePackageHost.d.ts +3 -0
  66. package/dist/esm/packages/StandalonePackageHost.d.ts.map +1 -0
  67. package/dist/esm/packages/StandalonePackageHost.js +13 -0
  68. package/dist/esm/packages/StandalonePackageHost.js.map +1 -0
  69. package/dist/esm/packages/WebViewPackageHost.d.ts +3 -0
  70. package/dist/esm/packages/WebViewPackageHost.d.ts.map +1 -0
  71. package/dist/esm/packages/WebViewPackageHost.js +13 -0
  72. package/dist/esm/packages/WebViewPackageHost.js.map +1 -0
  73. package/dist/esm/packages/index.d.ts +6 -0
  74. package/dist/esm/packages/index.d.ts.map +1 -1
  75. package/dist/esm/packages/index.js +6 -0
  76. package/dist/esm/packages/index.js.map +1 -1
  77. package/dist/esm/renderer/InteractiveComponent.js +4 -0
  78. package/dist/esm/renderer/InteractiveComponent.js.map +1 -1
  79. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  80. package/navigation/SceneNavigationContext.tsx +34 -0
  81. package/navigation/SceneNavigationHost.tsx +67 -0
  82. package/navigation/index.ts +2 -0
  83. package/package.json +5 -5
  84. package/packages/EmbeddedPackageHost.tsx +10 -0
  85. package/packages/PackageHostAssets.tsx +18 -0
  86. package/packages/PackageHostConfiguration.ts +24 -9
  87. package/packages/PackageHostRenderMode.ts +11 -0
  88. package/packages/PackageHostView.tsx +43 -0
  89. package/packages/StandalonePackageHost.tsx +10 -0
  90. package/packages/WebViewPackageHost.tsx +10 -0
  91. package/packages/index.ts +6 -0
  92. package/renderer/InteractiveComponent.tsx +5 -0
@@ -0,0 +1,34 @@
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
+
4
+ import { ReactNode, createContext, useContext } from 'react';
5
+ import { BindingScope } from '@cratis/scene.engine';
6
+ import { DestinationReference } from '@cratis/scene.model';
7
+ import { DestinationResolution } from './DestinationResolution';
8
+
9
+ export interface SceneNavigationContextValue {
10
+ currentScreen: string;
11
+ currentUrl?: string;
12
+ currentOutlet?: string;
13
+ currentDialog?: string;
14
+ bindingScope: BindingScope;
15
+ navigate(destination: DestinationReference): DestinationResolution;
16
+ closeDialog(): void;
17
+ }
18
+
19
+ const Context = createContext<SceneNavigationContextValue | undefined>(undefined);
20
+
21
+ export interface SceneNavigationProviderProps {
22
+ value: SceneNavigationContextValue;
23
+ children: ReactNode;
24
+ }
25
+
26
+ export function SceneNavigationProvider({ value, children }: SceneNavigationProviderProps) {
27
+ return <Context.Provider value={value}>{children}</Context.Provider>;
28
+ }
29
+
30
+ export function useSceneNavigation(): SceneNavigationContextValue {
31
+ const context = useContext(Context);
32
+ if (!context) throw new Error('No Scene navigation context is available.');
33
+ return context;
34
+ }
@@ -0,0 +1,67 @@
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
+
4
+ import { useCallback, useMemo, useState } from 'react';
5
+ import { BindingScope } from '@cratis/scene.engine';
6
+ import { DestinationKind, DestinationReference, SceneElement } from '@cratis/scene.model';
7
+ import { SceneElementView } from '../SceneElementView';
8
+ import { ComponentRegistry } from '../renderer';
9
+ import { SceneNavigationContextValue, SceneNavigationProvider } from './SceneNavigationContext';
10
+ import { resolveDestination } from './resolveDestination';
11
+
12
+ export interface SceneNavigationHostProps {
13
+ screens: Record<string, SceneElement>;
14
+ dialogs?: Record<string, SceneElement>;
15
+ initialScreen: string;
16
+ registry: ComponentRegistry;
17
+ bindingScope?: BindingScope;
18
+ }
19
+
20
+ /**
21
+ * Executes Scene destinations against an in-memory rendered hierarchy: screen routes replace outlets,
22
+ * dialog destinations render dialog content, and external destinations only report their resolved action.
23
+ */
24
+ export function SceneNavigationHost({ screens, dialogs = {}, initialScreen, registry, bindingScope = {} }: SceneNavigationHostProps) {
25
+ const [currentScreen, setCurrentScreen] = useState(initialScreen);
26
+ const [currentUrl, setCurrentUrl] = useState<string | undefined>(initialScreen);
27
+ const [currentOutlet, setCurrentOutlet] = useState<string | undefined>();
28
+ const [currentDialog, setCurrentDialog] = useState<string | undefined>();
29
+
30
+ const navigate = useCallback((destination: DestinationReference) => {
31
+ const resolution = resolveDestination(destination, bindingScope);
32
+ if (resolution.kind === DestinationKind.Dialog) {
33
+ setCurrentDialog(resolution.dialog);
34
+ return resolution;
35
+ }
36
+
37
+ if (resolution.kind !== DestinationKind.External) {
38
+ setCurrentScreen(destination.screen ?? destination.slice ?? currentScreen);
39
+ setCurrentOutlet(resolution.outlet);
40
+ }
41
+
42
+ setCurrentUrl(resolution.url);
43
+ return resolution;
44
+ }, [bindingScope, currentScreen]);
45
+
46
+ const value = useMemo<SceneNavigationContextValue>(() => ({
47
+ currentScreen,
48
+ currentUrl,
49
+ currentOutlet,
50
+ currentDialog,
51
+ bindingScope,
52
+ navigate,
53
+ closeDialog: () => setCurrentDialog(undefined),
54
+ }), [bindingScope, currentDialog, currentOutlet, currentScreen, currentUrl, navigate]);
55
+
56
+ const screen = screens[currentScreen];
57
+ const dialog = currentDialog ? dialogs[currentDialog] : undefined;
58
+
59
+ return <SceneNavigationProvider value={value}>
60
+ <div data-scene-outlet={currentOutlet ?? 'primary'} data-scene-url={currentUrl}>
61
+ {screen && <SceneElementView element={screen} registry={registry} dataContext={bindingScope.dataContext} queryResults={bindingScope.queryResults} componentOutputs={bindingScope.componentOutputs} />}
62
+ </div>
63
+ {dialog && <div role='dialog' data-scene-dialog={currentDialog}>
64
+ <SceneElementView element={dialog} registry={registry} dataContext={bindingScope.dataContext} queryResults={bindingScope.queryResults} componentOutputs={bindingScope.componentOutputs} />
65
+ </div>}
66
+ </SceneNavigationProvider>;
67
+ }
@@ -2,4 +2,6 @@
2
2
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
3
 
4
4
  export * from './DestinationResolution';
5
+ export * from './SceneNavigationContext';
6
+ export * from './SceneNavigationHost';
5
7
  export * from './resolveDestination';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/scene.react",
3
- "version": "4.10.0",
3
+ "version": "4.12.0",
4
4
  "description": "The React renderer implementing Scene.Engine's Renderer contract against real React/DOM.",
5
5
  "keywords": [
6
6
  "cratis",
@@ -51,12 +51,12 @@
51
51
  "build-storybook": "storybook build"
52
52
  },
53
53
  "dependencies": {
54
- "@cratis/scene.engine": "4.10.0",
55
- "@cratis/scene.model": "4.10.0"
54
+ "@cratis/scene.engine": "4.12.0",
55
+ "@cratis/scene.model": "4.12.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@cratis/scene.engine": "4.10.0",
59
- "@cratis/scene.model": "4.10.0",
58
+ "@cratis/scene.engine": "4.12.0",
59
+ "@cratis/scene.model": "4.12.0",
60
60
  "@storybook/addon-links": "^10.6.1",
61
61
  "@storybook/react": "^10.6.1",
62
62
  "@storybook/react-vite": "^10.6.1",
@@ -0,0 +1,10 @@
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
+
4
+ import { PackageHostView, PackageHostViewProps } from './PackageHostView';
5
+ import { PackageHostRenderMode } from './PackageHostRenderMode';
6
+
7
+ /** An embedded package host example surface for application or designer panes. */
8
+ export function EmbeddedPackageHost(props: Omit<PackageHostViewProps, 'mode'>) {
9
+ return <PackageHostView {...props} mode={PackageHostRenderMode.Embedded} />;
10
+ }
@@ -0,0 +1,18 @@
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
+
4
+ export interface PackageHostAssetsProps {
5
+ assets: string[];
6
+ fonts: string[];
7
+ }
8
+
9
+ /**
10
+ * Emits package-declared browser assets for a package host example.
11
+ */
12
+ export function PackageHostAssets({ assets, fonts }: PackageHostAssetsProps) {
13
+ const fontSet = new Set(fonts);
14
+ return <>
15
+ {assets.filter(asset => !fontSet.has(asset)).map(asset => <link key={asset} rel='stylesheet' href={asset} data-scene-package-asset={asset} />)}
16
+ {fonts.map(font => <link key={font} rel='preload' href={font} as='font' crossOrigin='' data-scene-package-font={font} />)}
17
+ </>;
18
+ }
@@ -9,6 +9,9 @@ import { ScenePackageBundle, mergePackageRegistries, validatePackageBundle } fro
9
9
  export interface PackageHostPolicy {
10
10
  allowExecutableImports: boolean;
11
11
  allowNetworkAssets: boolean;
12
+
13
+ /** Whether this host is resolving optional design-time bundles in addition to runtime contributions. */
14
+ loadDesignTime?: boolean;
12
15
  }
13
16
 
14
17
  export interface PackageHostConfiguration {
@@ -26,6 +29,8 @@ export interface ResolvedPackageHost {
26
29
  themes: Theme[];
27
30
  icons: IconAdapterRegistry;
28
31
  assets: string[];
32
+ fonts: string[];
33
+ blocked: boolean;
29
34
  diagnostics: string[];
30
35
  }
31
36
 
@@ -63,7 +68,7 @@ export function resolvePackageHost(configuration: PackageHostConfiguration): Res
63
68
  }
64
69
 
65
70
  diagnostics.push(...validatePackageBundle(bundle).map(problem => `${packageName}: ${problem}`));
66
- if (!configuration.policy.allowExecutableImports && bundle.designTime) {
71
+ if (configuration.policy.loadDesignTime === true && !configuration.policy.allowExecutableImports && bundle.designTime) {
67
72
  diagnostics.push(`Package '${packageName}' provides executable design-time contributions, but host policy forbids them`);
68
73
  }
69
74
 
@@ -84,15 +89,25 @@ export function resolvePackageHost(configuration: PackageHostConfiguration): Res
84
89
  bundles.push(bundle);
85
90
  }
86
91
 
92
+ const blocked = diagnostics.length > 0;
93
+ const approved = blocked ? [] : bundles;
94
+ const approvedAssets = blocked ? [] : assets;
95
+
87
96
  return {
88
- bundles,
89
- components: mergePackageRegistries(bundles),
90
- layouts: bundles.flatMap(bundle => bundle.layouts ?? []),
91
- screenTemplates: bundles.flatMap(bundle => bundle.screenTemplates ?? []),
92
- dialogTemplates: bundles.flatMap(bundle => bundle.dialogTemplates ?? []),
93
- themes: bundles.flatMap(bundle => bundle.themes ?? []),
94
- icons: createIconAdapterRegistry(bundles.map(bundle => bundle.iconLibrary?.adapter).filter(adapter => adapter !== undefined)),
95
- assets,
97
+ bundles: approved,
98
+ components: mergePackageRegistries(approved),
99
+ layouts: approved.flatMap(bundle => bundle.layouts ?? []),
100
+ screenTemplates: approved.flatMap(bundle => bundle.screenTemplates ?? []),
101
+ dialogTemplates: approved.flatMap(bundle => bundle.dialogTemplates ?? []),
102
+ themes: approved.flatMap(bundle => bundle.themes ?? []),
103
+ icons: createIconAdapterRegistry(approved.map(bundle => bundle.iconLibrary?.adapter).filter(adapter => adapter !== undefined)),
104
+ assets: approvedAssets,
105
+ fonts: approvedAssets.filter(isFontAsset),
106
+ blocked,
96
107
  diagnostics,
97
108
  };
98
109
  }
110
+
111
+ function isFontAsset(asset: string): boolean {
112
+ return /\.(woff2?|otf|ttf)(\?|$)/.test(asset) || asset.includes('/fonts/');
113
+ }
@@ -0,0 +1,11 @@
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
+
4
+ /**
5
+ * The host surface rendering a resolved package profile.
6
+ */
7
+ export enum PackageHostRenderMode {
8
+ Standalone = 'standalone',
9
+ Embedded = 'embedded',
10
+ WebView = 'webView',
11
+ }
@@ -0,0 +1,43 @@
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
+
4
+ import { SceneElement } from '@cratis/scene.model';
5
+ import { SceneElementView } from '../SceneElementView';
6
+ import { PackageHostAssets } from './PackageHostAssets';
7
+ import { PackageHostConfiguration, resolvePackageHost } from './PackageHostConfiguration';
8
+ import { PackageHostRenderMode } from './PackageHostRenderMode';
9
+
10
+ export interface PackageHostViewProps {
11
+ configuration: PackageHostConfiguration;
12
+ element: SceneElement;
13
+ mode?: PackageHostRenderMode;
14
+ dataContext?: unknown;
15
+ queryResults?: Record<string, unknown>;
16
+ componentOutputs?: Record<string, Record<string, unknown>>;
17
+ }
18
+
19
+ /**
20
+ * Renders a Scene element through an approved package host. Diagnostics are blocking: no rejected bundle,
21
+ * component, asset or design-time contribution is rendered after policy/dependency validation fails.
22
+ */
23
+ export function PackageHostView({
24
+ configuration,
25
+ element,
26
+ mode = PackageHostRenderMode.Embedded,
27
+ dataContext,
28
+ queryResults,
29
+ componentOutputs,
30
+ }: PackageHostViewProps) {
31
+ const host = resolvePackageHost(configuration);
32
+ if (host.blocked) {
33
+ return <section role='alert' data-scene-host-mode={mode} data-scene-host-blocked='true'>
34
+ <h2>Scene package host blocked rendering</h2>
35
+ <ul>{host.diagnostics.map(diagnostic => <li key={diagnostic}>{diagnostic}</li>)}</ul>
36
+ </section>;
37
+ }
38
+
39
+ return <section data-scene-host-mode={mode} data-scene-host-blocked='false'>
40
+ <PackageHostAssets assets={host.assets} fonts={host.fonts} />
41
+ <SceneElementView element={element} registry={host.components} dataContext={dataContext} queryResults={queryResults} componentOutputs={componentOutputs} />
42
+ </section>;
43
+ }
@@ -0,0 +1,10 @@
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
+
4
+ import { PackageHostView, PackageHostViewProps } from './PackageHostView';
5
+ import { PackageHostRenderMode } from './PackageHostRenderMode';
6
+
7
+ /** A standalone browser package host example surface. */
8
+ export function StandalonePackageHost(props: Omit<PackageHostViewProps, 'mode'>) {
9
+ return <PackageHostView {...props} mode={PackageHostRenderMode.Standalone} />;
10
+ }
@@ -0,0 +1,10 @@
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
+
4
+ import { PackageHostView, PackageHostViewProps } from './PackageHostView';
5
+ import { PackageHostRenderMode } from './PackageHostRenderMode';
6
+
7
+ /** A constrained webview package host example surface. */
8
+ export function WebViewPackageHost(props: Omit<PackageHostViewProps, 'mode'>) {
9
+ return <PackageHostView {...props} mode={PackageHostRenderMode.WebView} />;
10
+ }
package/packages/index.ts CHANGED
@@ -9,6 +9,12 @@ export * from './DesignTimePropertyDisplayProps';
9
9
  export * from './DesignTimePropertyEditorProps';
10
10
  export * from './DesignTimeProvider';
11
11
  export * from '../navigation';
12
+ export * from './EmbeddedPackageHost';
13
+ export * from './PackageHostAssets';
12
14
  export * from './PackageHostConfiguration';
15
+ export * from './PackageHostRenderMode';
16
+ export * from './PackageHostView';
13
17
  export * from './ScenePackageBundle';
18
+ export * from './StandalonePackageHost';
19
+ export * from './WebViewPackageHost';
14
20
  export * from './ComponentsUiLibraryContract';
@@ -39,6 +39,7 @@ function resolveProperties(properties: Record<string, unknown>, resolveBinding:
39
39
 
40
40
  function resolveValue(value: unknown, resolveBinding: (binding: BindingExpression) => unknown): unknown {
41
41
  if (isBindingExpression(value)) return resolveBinding(value);
42
+ if (isDestinationReference(value)) return value;
42
43
  if (Array.isArray(value)) return value.map(entry => resolveValue(entry, resolveBinding));
43
44
  if (value !== null && typeof value === 'object') {
44
45
  return Object.fromEntries(Object.entries(value as Record<string, unknown>).map(([name, entry]) => [name, resolveValue(entry, resolveBinding)]));
@@ -50,3 +51,7 @@ function resolveValue(value: unknown, resolveBinding: (binding: BindingExpressio
50
51
  function isBindingExpression(value: unknown): value is BindingExpression {
51
52
  return value !== null && typeof value === 'object' && !Array.isArray(value) && typeof (value as Record<string, unknown>).path === 'string';
52
53
  }
54
+
55
+ function isDestinationReference(value: unknown): boolean {
56
+ return value !== null && typeof value === 'object' && !Array.isArray(value) && 'routeParameterBindings' in value;
57
+ }